s の大規模なコレクションに対する linq フィルターのパフォーマンスの改善に取り組んでいますPOCO
が、ローカル テストでは 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;
}
これはまだ 100% の CPU を使用しており、item.OrderId
通話中は 30% に達しています。誰かが過去に同様の問題を抱えていましたか、それともおそらくこれを改善する方法について何らかの考えを持っていますか?
編集:大幅な改善を示すコード
@FastAl のおかげで、このコードは_bar
->DoOtherStuff(filteredItems)
ループを 1 秒以内に実行しました。
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);
}
}