我正在努力针对大量 POCO
提高 linq 过滤器的性能,但本地测试表明存在 CPU 瓶颈。
我最初试图通过检索大型结果集并将其加载到单独处理服务器上的内存中,然后在 .Net 中过滤此结果集来减少 SQL 服务器上的负载。
这是演示代码:
public class CustomClass
{
public int Id { get; set; }
public int OtherId { get; set;}
public DateTime Date { get; set; }
}
public void DoStuff()
{
// approx 800,000 items
List<CustomClass> allItems = _repo.GetCustomClassItemsFromDatabase();
foreach (OtherCustomClass foo in _bar)
{
// original linq-to-entities query,
// get most recent Ids that apply to OtherId
List<CustomClass> filteredItems = (
from item in allItems
where item.OtherId == foo.OtherId && item.Date <= foo.Date
group item by item.Id into groupItems
select groupItems.OrderByDescending(i => i.Date).First()).ToList();
DoOtherStuff(filteredItems);
}
}
这使我的 4 个内核在 1 分钟 30 秒内达到 100% CPU,这对于生产系统来说是不可行的。我在 VS2012 中运行性能分析器,30% 的时间是
get
对 item.OtherId
的调用。我开始将 linq 重写为纯代码,以查看是否可以获得任何速度提升,但到目前为止我还没有任何运气。这是纯代码重写:
private List<CustomClass> FilterCustomClassByIdAndDate(
List<CustomClass> items, int id, DateTime date)
{
var mostRecentCustomClass = new Dictionary<int, CustomClass>();
foreach (CustomClass item in items)
{
if (item.Id != id || item.Date > date) { continue; }
CustomClass mostRecent;
if (mostRecentCustomClass.TryGetValue(item.Id, out mostRecent) &&
mostRecent.Date >= item.Date)
{ continue; }
mostRecentCustomClass[item.Id] = item;
}
var filteredItems = new List<CustomClass>();
foreach (KeyValuePair<int, CustomClass> pair in mostRecentCustomClass)
{
filteredItems.Add(pair.Value);
}
return filteredItems;
}
这仍然在
item.OrderId
调用中达到 100% CPU 和 30%。过去有没有人遇到过类似的问题,或者对如何改进这个问题有一些想法?编辑:代码显示了巨大的改进
感谢@FastAl,这段代码在一秒钟内运行了
_bar
-> DoOtherStuff(filteredItems)
循环:public void DoStuff()
{
// approx 800,000 items
List<CustomClass> allItems = _repo.GetCustomClassItemsFromDatabase();
var indexedItems = new Dictionary<int, List<CustomClass>>();
foreach (CustomClass item in allItems)
{
List<CustomClass> allByOtherId;
if (!indexedItems.TryGetValue(item.OtherId, out allByOtherId))
{
allByOtherId = new List<CustomClass>();
indexedItems[item.OtherId] = allByOtherId;
}
allByOtherId.Add(item);
}
foreach (OtherCustomClass foo in _bar)
{
List<CustomClass> filteredItems;
if (!indexedItems.ContainsKey(foo.OtherId))
{
filteredItems = new List<CustomClass>();
}
else
{
List<CustomClass> filteredItems = (
from item in indexedItems[foo.OtherId]
where item.Date <= foo.Date
group item by item.Id into groupItems
select groupItems.OrderByDescending(i => i.Date).First())
.ToList();
}
DoOtherStuff(filteredItems);
}
}
最佳答案
使用列表字典。
加载项目后,循环遍历它们一次以构建 list 字典。注意插入的循环并更改 where 子句。
请原谅我的错误,我只有 4 分钟;-) 学会爱字典。它非常快 - 使用最快的搜索/插入方法之一。来自 M$ 的非常棒的小工具。
我诚实的建议 - 在数据库上做。问问自己 - 你在那里尝试过吗?我已经这样做了一段时间,如果不先进行实际测试,我永远无法判断两个未知数中的哪一个会更快(除非它真的很明显,但如果是这样,你就不会在这里发布)。仔细检查数据库在 OtherID 上有索引,否则 它面临同样的问题 您的 linq 语句是(线性搜索)。
public class CustomClass
{
public int Id { get; set; }
public int OtherId { get; set;}
public DateTime Date { get; set; }
}
public void DoStuff()
{
// approx 800,000 items
List<CustomClass> allItems = _repo.GetCustomClassItemsFromDatabase();
var index1 = new Dictionary <int, CustomClass>;
foreach (OtherCustomClass foo1 in allItems)
{
List<CustomClass> allOtherIDs ;
allOtherIDs=null;
if (!index1.TryGetValue(foo1.OtherID,allOtherIDs))
{
allOtherIDs=new List<CustomClass>;
index1.add(foo1.OtherID,allOtherIDs);
}
allOtherIDs(foo1.OtherID)=foo1;
}
foreach (OtherCustomClass foo in _bar)
{
// original linq-to-entities query,
// get most recent Ids that apply to OtherId
List<CustomClass> filteredItems = (
from item in allOtherIDs(foo.OtherID)
where item.Date <= foo.Date
group item by item.Id into groupItems
select groupItems.OrderByDescending(i => i.Date).First()).ToList();
DoOtherStuff(filteredItems);
}
}
关于c# - 有效过滤大量 POCO 实体,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16425883/