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