パフォーマンスの観点から、「ネストされたforeach」または「lambda/linqクエリ」をどのように使用すればよいですか?
できる限り明確なコードを記述し、ベンチマークとプロファイルを行って、パフォーマンスの問題を発見します。 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に固有のものではありません...
もしあなたがそうするなら
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の反復を実行します。
それについてはもっと複雑です。結局のところ、LINQ-to-Objectsの多くは(舞台裏で)foreach
ループですが、小さな抽象化/イテレータブロックなどのオーバーヘッドが追加されています。ただし、2つで非常に異なることをしない限り、バージョン(foreach vs LINQ)、両方ともO(N)である必要があります。
本当の質問は、あなたの特定のアルゴリズムを書くより良い方法はありますか?それはforeach
が非効率であることを意味しますか?そして、LINQはあなたのためにそれをすることができますか?
たとえば、LINQを使用すると、データのハッシュ化、グループ化、並べ替えが簡単になります。
それは以前に言われましたが、それは繰り返す価値があります。
開発者は、パフォーマンステストを実行するまで、パフォーマンスのボトルネックがどこにあるかを知ることはありません。
テクニックAとテクニックBを比較する場合も同じです。劇的な違いがない限り、テストする必要があります。 O(n) vs O(n ^ x))のシナリオがある場合は明白かもしれませんが、LINQはコンパイラの魔術なので、プロファイリングに値します。
さらに、プロジェクトが本番環境にあり、コードのプロファイルを作成し、そのループによって実行速度が低下していることがわかっている場合を除き、読みやすさとメンテナンスの優先度はどちらでもかまいません。時期尚早の最適化は悪魔です。
大きな利点は、Linq-To-Objectsクエリを使用すると、クエリをPLinqに簡単に引き渡して、現在のシステムの正しい数のスレッドでシステムが自動的に操作を実行できるようになることです。
大きなデータセットでこの手法を使用している場合、それは非常に小さなトラブルで簡単に大きな勝利になるでしょう。