我正在努力提高针对大量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核心达到100%CPU达1m30s,对于生产系统来说是不可行的。我在VS2012中运行了性能分析器,30%的时间是对get
的{{1}}调用。
我开始将linq重写为普通代码以查看是否可以提高速度,但到目前为止我还没有运气。这是简单的代码重写:
item.OtherId
这仍然会在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;
}
来电时达到100%的CPU和30%。过去有没有人有类似的问题,或者对如何改进这个问题有一些想法?
编辑:显示大幅改进的代码
感谢@FastAl,此代码贯穿item.OrderId
- &gt; _bar
在第二个循环中循环:
DoOtherStuff(filteredItems)
答案 0 :(得分:3)
加载项目后,循环播放一次以构建列表字典。注意插入的循环并更改其中的子句。
请原谅我的错误,我只有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);
}
}