我正在努力提高一个针对大量POCO的Linq过滤器的性能,但本地测试表明存在CPU瓶颈。
我最初尝试这样做是为了减轻SQL服务器的负担,通过检索大量结果集并将其加载到单独的处理服务器的内存中,然后在.Net中过滤此结果集。
以下是演示代码:
这会使我的四个核心处理器的CPU持续100%使用率1分30秒,对于生产系统来说不可行。我在VS2012中运行了性能分析器,发现30%的时间是用于
我开始重写Linq代码为普通代码,以查看是否可以获得任何速度提升,但到目前为止我没有取得任何成功。以下是我重写的普通代码:
这仍然会导致 CPU 利用率达到 100%,并且在
我最初尝试这样做是为了减轻SQL服务器的负担,通过检索大量结果集并将其加载到单独的处理服务器的内存中,然后在.Net中过滤此结果集。
以下是演示代码:
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);
}
}
这会使我的四个核心处理器的CPU持续100%使用率1分30秒,对于生产系统来说不可行。我在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;
}
这仍然会导致 CPU 利用率达到 100%,并且在
item.OrderId
调用上占用了 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);
}
}
foo
列表中所有这800,000个项目中的至少1个记录,并且数据库必须累计提供至少800,000条记录以进行各个调用。 - Laurence