web-dev-qa-db-ja.com

"ネストされたforeach"と "lambda / linqクエリ"のパフォーマンス(LINQ-to-Objects)

パフォーマンスの観点から、「ネストされたforeach」または「lambda/linqクエリ」をどのように使用すればよいですか?

39
JSC

できる限り明確なコードを記述し、ベンチマークとプロファイルを行って、パフォーマンスの問題を発見します。 doにパフォーマンスの問題がある場合は、さまざまなコードを試して、それがより速いかどうか(できるだけ現実的なデータで常に測定する)を調べ、次に、パフォーマンスの改善は、読みやすさのヒットに値します。

直接のforeachアプローチwillは、多くの場合LINQよりも高速です。たとえば、次のことを考慮してください。

var query = from element in list
            where element.X > 2
            where element.Y < 2
            select element.X + element.Y;

foreach (var value in query)
{
    Console.WriteLine(value);
}

現在、2つのwhere句とselect句があるため、すべての最終的なアイテムは3つのイテレータを通過する必要があります。 (明らかに、この場合、2つのwhere句を組み合わせることができますが、私は一般的なことを言っています。)

それを直接コードと比較してください:

foreach (var element in list)
{
    if (element.X > 2 && element.Y < 2)
    {
        Console.WriteLine(element.X + element.Y);
    }
}

実行するフープが少ないため、実行速度が速くなります。たぶん、コンソールの出力はイテレータのコストを下回る可能性があり、私は確かにLINQクエリを好むでしょう。

編集:「ネストされたforeach」ループについて回答するには...通常、これらはSelectManyまたは2番目のfrom句で表されます。

var query = from item in firstSequence
            from nestedItem in item.NestedItems
            select item.BaseCount + nestedItem.NestedCount;

ここでは、ネストされたforeachループにより、最初のシーケンスでアイテムごとに追加のイテレーターをすでに使用しているため、追加のイテレーターを1つだけ追加しています。 「インライン」ではなくデリゲートでプロジェクションを実行するオーバーヘッド(前に触れなかったもの)を含め、多少のオーバーヘッドはまだありますが、それでもなお、nested-foreachのパフォーマンスとそれほど変わりません。

もちろん、LINQでは足元で自分を撃つことができないと言っているのではありません。最初に頭を動かさなければ、非常に非効率的なクエリを書くことができますが、それはLINQに固有のものではありません...

60
Jon Skeet

もしあなたがそうするなら

foreach(Customer c in Customer)
{
  foreach(Order o in Orders)
  {
    //do something with c and o
  }
}

Customer.Count * Order.Countの反復を実行します


もしあなたがそうするなら

var query =
  from c in Customer
  join o in Orders on c.CustomerID equals o.CustomerID
  select new {c, o}

foreach(var x in query)
{
  //do something with x.c and x.o
}

Enumerable.JoinはHashJoinとして実装されているため、Customer.Count + Order.Countの反復を実行します。

23
Amy B

それについてはもっと複雑です。結局のところ、LINQ-to-Objectsの多くは(舞台裏で)foreachループですが、小さな抽象化/イテレータブロックなどのオーバーヘッドが追加されています。ただし、2つで非常に異なることをしない限り、バージョン(foreach vs LINQ)、両方ともO(N)である必要があります。

本当の質問は、あなたの特定のアルゴリズムを書くより良い方法はありますか?それはforeachが非効率であることを意味しますか?そして、LINQはあなたのためにそれをすることができますか?

たとえば、LINQを使用すると、データのハッシュ化、グループ化、並べ替えが簡単になります。

12
Marc Gravell

それは以前に言われましたが、それは繰り返す価値があります。

開発者は、パフォーマンステストを実行するまで、パフォーマンスのボトルネックがどこにあるかを知ることはありません。

テクニックAとテクニックBを比較する場合も同じです。劇的な違いがない限り、テストする必要があります。 O(n) vs O(n ^ x))のシナリオがある場合は明白かもしれませんが、LINQはコンパイラの魔術なので、プロファイリングに値します。

さらに、プロジェクトが本番環境にあり、コードのプロファイルを作成し、そのループによって実行速度が低下していることがわかっている場合を除き、読みやすさとメンテナンスの優先度はどちらでもかまいません。時期尚早の最適化は悪魔です。

2
Drithyin

大きな利点は、Linq-To-Objectsクエリを使用すると、クエリをPLinqに簡単に引き渡して、現在のシステムの正しい数のスレッドでシステムが自動的に操作を実行できるようになることです。

大きなデータセットでこの手法を使用している場合、それは非常に小さなトラブルで簡単に大きな勝利になるでしょう。

2
Denis Troller