以下のコードスニペットの違いは何ですか?どちらもスレッドプールスレッドを使用しませんか?
たとえば、コレクション内の各アイテムの関数を呼び出したい場合、
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
以下のコードスニペットの違いは何ですか?どちらもスレッドプールスレッドを使用しませんか?
たとえば、コレクション内の各アイテムの関数を呼び出したい場合、
Parallel.ForEach<Item>(items, item => DoSomething(item));
vs
foreach(var item in items)
{
Task.Factory.StartNew(() => DoSomething(item));
}
回答:
最初ははるかに良いオプションです。
Parallel.ForEachは、内部で、Partitioner<T>
コレクションを作業項目に配布するためにを使用します。アイテムごとに1つのタスクを実行するのではなく、これをバッチ処理して、関連するオーバーヘッドを削減します。
2番目のオプションはTask
、コレクション内のアイテムごとに1 つをスケジュールします。結果は(ほぼ)同じですが、特に大規模なコレクションの場合、必要以上のオーバーヘッドが発生し、全体的なランタイムが遅くなります。
FYI-使用するパーティショナーは、必要に応じてParallel.ForEachへの適切なオーバーロードを使用して制御できます。詳細については、MSDNのカスタムパーティショナーを参照してください。
実行時の主な違いは、2番目が非同期で動作することです。これは、Parallel.ForEachを使用して複製できます。
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
これにより、パーティショナーを利用できますが、操作が完了するまでブロックしないでください。
「Parallel.For」と「Task」オブジェクトを使用してメソッドを「1,000,000,000(10億)」回実行する小さな実験を行いました。
プロセッサ時間を測定したところ、Parallelの方が効率的でした。Parallel.Forは、タスクを小さな作業項目に分割し、最適な方法ですべてのコアで並列に実行します。多くのタスクオブジェクトを作成している間(FYI TPLはスレッドプーリングを内部で使用します)、各タスクのすべての実行を移動し、以下の実験から明らかなように、ボックス内により多くのストレスを作成します。
また、基本的なTPLを説明する小さなビデオを作成し、Parallel.Forがコアをhttp://www.youtube.com/watch?v=No7QqSc5cl8を通常のタスクやスレッドと比較してより効率的に利用する方法も示しました。
実験1
Parallel.For(0, 1000000000, x => Method1());
実験2
for (int i = 0; i < 1000000000; i++)
{
Task o = new Task(Method1);
o.Start();
}
Mehthod1()
この例では何をしますか?
私の考えでは、最も現実的なシナリオは、タスクを完了するために重い操作がある場合です。Shivprasadのアプローチは、計算自体よりもオブジェクトの作成/メモリの割り当てに重点を置いています。私は次の方法を呼び出す研究をしました:
public static double SumRootN(int root)
{
double result = 0;
for (int i = 1; i < 10000000; i++)
{
result += Math.Exp(Math.Log(i) / root);
}
return result;
}
このメソッドの実行には約0.5秒かかります。
Parallelを使用して200回呼び出しました。
Parallel.For(0, 200, (int i) =>
{
SumRootN(10);
});
次に、旧式の方法を使用して200回呼び出しました。
List<Task> tasks = new List<Task>() ;
for (int i = 0; i < loopCounter; i++)
{
Task t = new Task(() => SumRootN(10));
t.Start();
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
最初のケースは26656msで完了し、2番目のケースは24478msで完了しました。何度も繰り返しました。2番目のアプローチの方がわずかに速くなります。