2

私は単純なwhileループを持っています

IEnumerable<Foo> collection;
while (!bc.IsCompleted)
{
   collection = bc.Take();
}

bc はBlockingCollection<IEnumerable<Foo>>. bc には、9 つ​​の IEnumerable コレクションと合計 260 万の Foo オブジェクトが含まれています。私のマシンでループを実行するには、約 640 ミリ秒かかります。while ループ内の Take() の後に foreach ループを追加するとすぐに、実行にかかる時間が 2400 ミリ秒に爆発します。

foreach(Foo foo in collection)
{
}

個別に設定した List または Foo[] または IEnumerable 内で 260 万を超える要素を反復処理すると、約 54 ミリ秒かかりました。

foreach ループの代わりに、次のようなコレクション変換を追加するだけでも同じことが起こります。

List<Foo> fooList = collection.ToList();

また

Foo[] fooArray = collection.ToArray();

突然、実行に 2000 ミリ秒もかかります。

どうすればいいの?説明や考えられる理由が完全に不足しています。ここで欠けているものを教えてくれる人はいますか? 比較間で BlockingCollection にアクセスする方法を変更していないため、ロック/ブロックによって速度が低下することはありません。

ご意見ありがとうございます。

4

3 に答える 3

1

他の LINQ メソッドと同様に (そして、LINQ のテイクを使用していると推測しています)、このメソッドは遅延実行で機能します。

このメソッドは、遅延実行を使用して実装されます。即時の戻り値は、アクションを実行するために必要なすべての情報を格納するオブジェクトです。このメソッドによって表されるクエリは、GetEnumerator メソッドを直接呼び出すか、Visual C# の foreach または Visual Basic の For Each を使用して、オブジェクトが列挙されるまで実行されません。

これは、foreach ループの ToList() 呼び出しを追加しない場合、Take への唯一の呼び出しは実際には結果を生成せず、実際の結果は (foreach/tolist の) イテレーターを使用している場合にのみもたらされることを意味します。性能差。

リストを反復するだけと比較しても、正確な結果が得られない場合があります。時間がかかるのは List(foo) の繰り返しではなく、おそらく使用しているブロッキングコレクションからの要素の選択がすべてを遅くします。
MSDNは、BlockingCollection で通常の foreach を使用すると (これはおそらく、この場合は IEunmerable で動作する LINQ 提供の Take を使用するときに発生することです)、基になるコレクションのスナップショットを使用し、これにより巨大なコレクションの処理が確実に遅くなる可能性があると主張しています。 .

于 2012-05-28T11:06:04.480 に答える
1

どのようなものをIEnumerableキューに入れていますか?

LINQ クエリは遅延実行を使用していることに注意してください。コードは、コンシューマー スレッドでクエリを評価することになる場合があります。ToList()要素をキューに入れる前に、プロデューサー スレッドで呼び出してみてください。

于 2012-05-28T11:04:35.783 に答える
1

IEnumerableは遅延操作を表すことができます。場合によっては (たとえば、LINQ やイテレータ ブロックを使用すると)、反復処理が行われるまで、ienumerable の内容が実際には生成されないことがあります。

したがって、IEnumerable<Foo>を生成するのに十分な情報が含まれている可能性がありますがFoo、実際には、foreachまたはを使用して列挙型を反復処理するまでそうしませんToList。そのため、これらの操作には時間がかかります。

于 2012-05-28T11:05:21.317 に答える