私はLINQを理解しようとしています。最も気にかかるのは、構文をよりよく理解していても、表現力のために意図せずにパフォーマンスを犠牲にしたくないということです。
それらは、「Effective LINQ」の情報や書籍の優れた一元化されたリポジトリですか?それに失敗すると、あなた自身のお気に入りの高性能LINQテクニックは何ですか?
私は主にLINQ to Objectsに関心がありますが、LINQ to SQLおよびLINQ to XMLに関するすべての提案ももちろん歓迎します。ありがとう。
内部的にLINQが何をしているのかを理解するだけで、パフォーマンスが低下しているかどうかを知るのに十分な情報が得られます。
LINQがパフォーマンスに役立つ簡単な例を次に示します。この典型的な旧式のアプローチを検討してください。
List<Foo> foos = GetSomeFoos();
List<Foo> filteredFoos = new List<Foo>();
foreach(Foo foo in foos)
{
if(foo.SomeProperty == "somevalue")
{
filteredFoos.Add(foo);
}
}
myRepeater.DataSource = filteredFoos;
myRepeater.DataBind();
したがって、上記のコードは2回繰り返され、フィルターされた値を保持するために2番目のコンテナーを割り当てます。なんて無駄だ!と比べて:
var foos = GetSomeFoos();
var filteredFoos = foos.Where(foo => foo.SomeProperty == "somevalue");
myRepeater.DataSource = filteredFoos;
myRepeater.DataBind();
これは1回だけ反復されます(リピーターがバインドされている場合)。元のコンテナのみを使用します。 filteredFoos
は単なる中間列挙子です。何らかの理由で、後でリピーターをバインドしないことに決めた場合、何も無駄になりません。一度も繰り返したり評価したりすることはありません。
非常に複雑なシーケンス操作を行う場合、潜在的に LINQの固有のチェーンと遅延評価の使用を活用することにより、多くの利益を得ることができます。繰り返しますが、何でもそうですが、実際に何をしているかを理解するだけです。
Linqは組み込みテクノロジーであるため、パフォーマンスの利点と欠点があります。拡張メソッドの背後にあるコードは、.NETチームによってかなりのパフォーマンスの注意が払われており、遅延評価を提供する能力は、オブジェクトのセットでほとんどの操作を実行するコストが、操作されたセットを必要とするより大きなアルゴリズムに分散することを意味します。ただし、コードのパフォーマンスを左右する可能性があることを知っておく必要があります。
何よりもまず、Linqは、操作を実行するのに必要な時間やメモリを魔法のようにプログラムに保存しません。絶対に必要になるまでこれらの操作を遅らせるだけです。 OrderBy()はQuickSortを実行します。これは、独自のQuickSorterを記述した場合やList.Sort()を適切なタイミングで使用した場合と同じようにnlogn時間かかります。そのため、クエリを作成するときに、Linqにシリーズに対して行うことを常に求めていることに注意してください。操作が不要な場合は、クエリまたはメソッドチェーンを再構築して回避してください。
同様に、特定の操作(並べ替え、グループ化、集計)には、操作対象のセット全体の知識が必要です。シリーズの最後の要素は、操作がイテレータから返す必要がある最初の要素です。それに加えて、Linq操作はソースの列挙型を変更すべきではありませんが、使用するアルゴリズムの多くはインプレースソートを行うため、これらの操作は列挙型全体を評価するだけでなく、具体的な有限構造にコピーします、操作を実行し、それを実行します。したがって、ステートメントでOrderBy()を使用し、最終結果から要素を要求すると、指定されたIEnumerableが生成できるすべてが評価され、配列としてメモリに格納され、並べ替えられてから、1つの要素が返されます時間。教訓は、列挙可能なものの代わりに有限セットを必要とする操作は、クエリのできるだけ遅い場所に配置する必要があり、Where()やSelect()などの他の操作がソースセットのカーディナリティとメモリフットプリントを削減できるようにすることです。
最後に、Linqメソッドは、システムの呼び出しスタックサイズとメモリフットプリントを大幅に増加させます。セット全体を知る必要がある各操作は、最後の要素が繰り返されるまでソースセット全体をメモリに保持します。各要素の評価には、チェーンまたは句のメソッドの数の少なくとも2倍の深さの呼び出しスタックが含まれます。インラインステートメント(各イテレータのMoveNext()の呼び出しまたはGetEnumeratorの生成、および途中で各ラムダの少なくとも1つの呼び出し)。これは単純に、同じ操作を実行するインテリジェントに設計されたインラインアルゴリズムよりも大きくて遅いアルゴリズムになります。 Linqの主な利点は、コードがシンプルであることです。グループ値のリストのディクショナリを作成してからソートすることは、あまり理解しやすいコードではありません(私を信じてください)。マイクロ最適化により、さらに難読化される可能性があります。パフォーマンスが主な関心事である場合は、Linqを使用しないでください。約10%の時間オーバーヘッドと、リストをその場で操作する場合のメモリオーバーヘッドの数倍が追加されます。ただし、通常、保守性は開発者の主な関心事であり、Linq DEFINITELYがそこで役立ちます。
パフォーマンスキック:アルゴリズムのパフォーマンスが神聖で妥協のない最優先事項である場合、C++などのアンマネージ言語でプログラミングすることになります。 .NETは、JITネイティブコンパイル、管理されたメモリ、追加のシステムスレッドを備えた管理されたランタイム環境であるため、はるかに遅くなります。 「十分に良い」という哲学を採用します。 Linqはその性質上、速度低下を引き起こす可能性がありますが、違いを認識できず、クライアントが違いを認識できない場合は、実際的な目的のために違いはありません。 「早すぎる最適化はすべての悪の根源です」;あなたとあなたのクライアントがそれで十分であると同意するまで、それを機能させて、それからそれをより高性能にする機会を探してください。それは常に「より良い」可能性がありますが、マシンコードを手で詰めたいと思わない限り、勝利を宣言して先に進むことができる点よりも短い点を見つけるでしょう。
パフォーマンスに影響するさまざまな要因があります。
多くの場合、LINQを使用してソリューションを開発すると、システムが実際にクエリを実行することなくクエリを表す式ツリーを構築できるため、かなり合理的なパフォーマンスが得られます。結果を反復処理する場合にのみ、この式ツリーを使用してクエリを生成および実行します。
絶対的な効率という点では、事前定義されたストアドプロシージャに対して実行するとパフォーマンスが低下する場合がありますが、一般的には、適切なパフォーマンス(LINQなど)を提供し、数パーセントの損失を心配しないシステムを使用してソリューションを開発しますパフォーマンスの。クエリの実行が遅い場合は、おそらく最適化を検討します。
現実には、クエリの大部分は、LINQを介して行われることに関してわずかな問題を抱えることはありません。もう1つの事実は、クエリの実行が遅い場合、クエリ自体よりもインデックス作成、構造などの問題である可能性が高いため、最適化を検討する場合でも、LINQには触れないことが多いということです。対象のデータベース構造。
XMLを処理するために、ドキュメントが読み込まれてメモリに解析されると(DOMモデル、XmlDocumentなどに基づいて)、イベントを発生させるようなシステムよりも多くのメモリ使用量が得られます。開始タグまたは終了タグを見つけることを示しますが、ドキュメントの完全なメモリ内バージョン(SAXやXmlReaderなど)は作成しません。欠点は、イベントベースの処理が一般にかなり複雑になることです。繰り返しますが、ほとんどのドキュメントでは問題はありません-ほとんどのシステムには数GBのRAMがあるため、単一のXMLドキュメントを表す数MBを使用しても問題はありません(そして、少なくともある程度のXMLドキュメントを処理することはよくあります)順次)。特定の選択について心配するのは、数百MBを占有する巨大なXMLファイルがある場合のみです。
LINQを使用すると、メモリ内のリストなどを繰り返し処理できるため、状況によっては(関数で結果のセットを何度も使用する場合など)、. ToListを使用できます。または.ToArrayを使用して結果を返します。これは便利な場合もありますが、一般的には、メモリ内ではなくデータベースのクエリを使用することを試みます。
個人的なお気に入り-NHibernate LINQ-については、クラスを定義し、マッピングの詳細を定義し、クラスからデータベースを生成できるようにするオブジェクトリレーショナルマッピングツールです。良い(SubSonicの同類よりも確かに良い)。
I4oと呼ばれるコードプレックスプロジェクトがあります。これはしばらく前に使用しました。
from p in People
where p.Age == 21
select p;
http://i4o.codeplex.com/ .Net 4でテストしていないので、まだ動作するが安全だとは言えないが、チェックする価値はある。それを魔法のように動作させるには、ほとんどの場合、クラスをいくつかの属性で装飾して、インデックスを作成するプロパティを指定する必要があります。私が前にそれを使用したとき、それは等価比較でのみ動作します。
Linq to SQLでは、パフォーマンスをそれほど気にする必要はありません。最も読みやすいと思われる方法ですべてのステートメントを連鎖させることができます。 Linqは、すべてのステートメントを最後に1つのSQLステートメントに変換するだけで、最後に呼び出される/実行されるだけです(.ToList()
を呼び出すときなど)
さまざまな条件でさまざまな追加ステートメントを適用する場合、var
には実行せずにこのステートメントを含めることができます。最後に実行するのは、ステートメントをオブジェクトまたはオブジェクトのリストのような結果に変換する場合のみです。