4

読み取りのロック競合を最小限に抑えようとする並行コレクションを最適化しようとしています。最初のパスは、リンクされたリストを使用していました。これにより、多くの同時読み取りがブロックされずに続行できる一方で、書き込みのみをロックできました。これは、カスタムIEnumeratorを使用して次のリンク値を生成しました。コレクションの反復をプレーンと比較し始めると、実装が約半分の速さであることにList<T>気付きました ( 1 from x in c select x*m* アイテムのコレクションでは、コレクションで 24ミリ秒、コレクションで49 ミリ秒)。List<T>

そこで、aを使用ReaderWriteLockSlimして、読み取り時の競合を少し犠牲にして、aList<T>を内部ストレージとして使用できるようにしようと考えました。繰り返しの開始時に読み取りロックを取得し、完了時にそれを解放する必要があるため、最初に my の yield パターンを実行し、 internalIEnumerableに対してforeachList<T>を実行しました。今、私は66msしか得ていませんでした。

List が実際に何をするかを調べたところ、内部ストアと、インデックスを前方に移動して現在のインデックス値を返すT[]カスタムが使用されています。IEnumerator現在、ストレージとして手動で使用T[]すると、メンテナンス作業が大幅に増えます、マイクロ秒を追いかけています。

ただしIEnumerator、配列のインデックスの移動を模倣しても、できることは約~38ms でした。では、List<T>その秘密のソースを提供するものは何ですか? あるいは、イテレータのより高速な実装は何ですか?

更新:私の主な速度の犯人は、List<T>明らかにリリース コンパイルである一方で、デバッグ コンパイルを実行していたことが判明しました。リリースでは、私の実装はまだ よりも少し遅いですがList<T>、モノラルではより高速になりました。

友人から得たもう 1 つの提案は、BCL は GAC にあり、システムによってプリコンパイルできるため、高速であるというものです。その理論をテストするには、私のテストをGACに入れる必要があります。

4

1 に答える 1

4

繰り返しのたびにロックを取得して解放するのは悪い考えのように思えます。なぜなら、リストを繰り返し処理しているときにAddorを実行すると、イテレータが無効になるからです。たとえば、それは確かに好きではありません。RemoveList<T>

あなたのユースケースでは、発信者はReaderWriterLockSlimアイテムごとではなく、反復プロセス全体を実行できますか? それはより効率的より堅牢になります。そうでない場合、並行性の問題にどのように対処する予定ですか? ライターが必要な場所よりも前に要素を追加した場合、単純な実装では同じ要素が 2 回返されます。削除では逆のことが起こります - イテレータは要素をスキップします。

最後に、.NET 4.0 はオプションですか? 高度に最適化された並行コレクションがいくつかあることは知っています...

編集:イテレータを手動で構築するという点で、あなたの現在の状況はよくわかりませんが、調査したいことの1つは、の構造体を使用しIEnumerator<T>、コレクションがそれを返すことを明示的に宣言することです-それは何をList<T>します。これ、世界中の子猫を泣かせる可変構造体を使用することを意味しますが、これがパフォーマンスにとって絶対に重要であり、恐怖に耐えることができると思われる場合は、少なくとも試してみる価値があります.

于 2009-11-18T06:38:45.340 に答える