0

実行に 20 秒かかる次のループを最適化する必要がありました。

    foreach (IGrouping<DateTime, DateTime> item in groups)
    {
        var countMatchId = initialGroups
                        .Where(grp => CalculateArg(grp.a.Arg) == item.Key && grp.b.Arg == someId)
                        .Sum(y => y.c.Value);

        var countAll = initialGroups
                        .Where(grp => CalculateArg(grp.a.Arg) == item.Key)
                        .Sum(y => y.c.Value);
    }

...ここで、CalculateArg は比較的高価な関数です。CalculateArg が原因である必要があるため、1 つのクエリでのみ使用する必要があると考えたので、次のように思いつきました。

    foreach (IGrouping<DateTime, DateTime> item in groups)
    {
        var result = initialGroups
                        .Where(grp => CalculateArg(grp.a.Arg) == item.Key);

        var countMatchId = result
                        .Where(x => x.c.Arg == someId).Sum(y => y.c.Value);

        var countAll = result
                        .Sum(y => y.c.Value);

この結果の問題は、約 200 ミリ秒しか節約できないため、何も最適化されていないことです。countMatchId には、すべての要素を反復する.Where ( )と、それらすべてを反復する .Sum() がまだあります。そして、 countAll の別の.Sum()がすべての要素を繰り返します。

これをさらに最適化するにはどうすればよいですか? 私が見逃している明らかな何かがあると確信しています。

4

5 に答える 5

0

ここで、この質問で考慮しなければならないことがいくつかあります。まず第一に、あなたのデータはどこから来ていますか?dbcontext によって作成されたエンティティからのものですか? はいの場合は、オブジェクトのナビゲーション プロパティを使用する代わりに、Context を使用してデータにアクセスして操作することを検討する必要があります。それはどういう意味ですか?以下の2つのクラスを考えてみましょう。

public class User{

   public int ID { get;set; } 
   public virtual ICollection<Animal> Animals {get;set;} 

}


public class Animal{
    public int ID { get; set; }
    public string Name {get;set;}
    [ForeignKey("Owner")]
    public int? Owner_ID {get;set;}
    public virtual User Owner {get;set;}
}

以下のコードでユーザーの動物にアクセスする代わりに、

User user = Context.User.Single(t=> t.ID == 1);
List<Animal> animals = user.Animals.ToList();

直接 dbcontext でアクセスする方がはるかに効率的です。(リストに 100k のエンティティがあり、ToList メソッドを使用してそれをメモリに取り込もうとしている場合は、パフォーマンスに関する考慮事項を考慮する必要があります。

List<Animal> animals = Context.Animals.Where(t => t.Owner_ID == 1).ToList();

また、ORM フレームワークを使用していない場合は、すべての計算オブジェクトをメモリに取得して、すべてキャッシュしてみてください。既にメモリ内にあるオブジェクトへのアクセスは、Queryable リスト内のオブジェクトよりもはるかに簡単であるため、これによりパフォーマンスが大幅に向上します。あなたの場合、グループオブジェクトはクエリ可能なオブジェクトである可能性があるため、パフォーマンスがあまり良くありません。

于 2013-08-17T09:35:24.170 に答える
0

itemsがたくさんある場合はgroups、アルゴリズムを変更することでメリットが得られる場合があります。

反復する代わりに、一度計算してみてください & GroupJoin 結果をまとめて、ala

var calculated = initialGroups
  .Select(group => new { Group = group, Arg = CalculateArg(group.a.Arg) })
  .ToList();

var sumCollection = groups
  .GroupJoin(calculated,
             item => item.Key,
             group => group.Arg,
      (group, calculatedCollection) =>
         new {
            Group = group,
            SumAll = calculatedCollection.Sum(y => y.Group.c.Value),
            SumMatchId = calculatedCollection
                         .Where(y => y.Group.b.Arg == someId)
                         .Sum(y => y.Group.c.Value)
         });

foreach (var item in sumCollection)
{
    item.SumAll     // you get the idea
    item.SumMatchId // 
}
于 2013-08-17T10:25:32.710 に答える
0

私はそれを修正する方法を見つけました:質問への有益なコメントに従って、ストップウォッチを使用して foreach のほぼすべての行をプロファイリングし、実際、CalculateArg()関数が犯人であることがわかりました-それを呼び出すと、反復ごとに 500ms が追加されました; 40 アイテムのコレクションでは、これは合計 20000 ミリ秒 = 20 秒を意味します。

つまり、グループ(SelectMany で作成された匿名オブジェクト) には、各要素の CalculateArg() の結果も含まれるようになりました。これにより、コードは次のようになります。

foreach (IGrouping<DateTime, DateTime> item in groups)
{
    var result = initialGroups
                    .Where(grp => grp.calculatedArg == item.Key);
}
于 2013-08-17T16:04:55.887 に答える