36

コードは次のとおりです。

using (var context = new AventureWorksDataContext())
{
    IEnumerable<Customer> _customerQuery = from c in context.Customers
                                           where c.FirstName.StartsWith("A")
                                           select c;

    var watch = new Stopwatch();
    watch.Start();

    var result = Parallel.ForEach(_customerQuery, c => Console.WriteLine(c.FirstName));

    watch.Stop();
    Debug.WriteLine(watch.ElapsedMilliseconds);

    watch = new Stopwatch();
    watch.Start();

    foreach (var customer in _customerQuery)
    {
        Console.WriteLine(customer.FirstName);
    }

    watch.Stop();
    Debug.WriteLine(watch.ElapsedMilliseconds);
}

問題は、約 40ms かかるParallel.ForEach通常の に対して約 400ms かかることです。foreach正確に何が間違っているのですか?なぜこれが期待どおりに機能しないのですか?

4

5 に答える 5

188

実行するタスクがあるとします。あなたが数学の教師で、採点するレポートが 20 あるとします。論文の採点に 2 分かかるので、約 40 分かかります。

ここで、レポートの採点を手伝ってくれるアシスタントを何人か雇うことにしたとしましょう。4 人のアシスタントを見つけるのに 1 時間かかります。あなたはそれぞれ4枚の紙を取り、8分ですべて完了します. 40 分の作業を、アシスタントを探すための余分な 1 時間を含めて合計 68 分の作業と交換したため、これは節約にはなりません。アシスタントを見つけるためのオーバーヘッドは、自分で作業を行うためのコストよりも大きくなります。

ここで、採点するレポートが 2 万件あると仮定すると、約 40000 分かかります。アシスタントを探すのに 1 時間も費やしたら、それは勝ちです。1 人あたり 4000 枚の論文を処理し、合計 40000 分ではなく 8060 分で完了し、ほぼ 5 分の 1 の節約になります。アシスタントを見つけるためのオーバーヘッドは、基本的に無関係です。

並列化は無料ではありません異なるスレッド間で作業を分割するコストは、スレッドごとに実行される作業量に比べてごくわずかである必要があります。

参考文献:

アムダールの法則

リソースが改善されたシステムで期待できる、一定のワークロードでのタスクの実行の待ち時間の理論上の高速化を示します。

グスタフソンの法則

リソースが改善されたシステムで期待できる、一定の実行時間でのタスクの実行のレイテンシの理論的な高速化を示します。

于 2011-05-17T20:20:31.323 に答える
12

最初に理解する必要があるのは、すべての並列処理が有益であるとは限らないということです。並列処理にはある程度のオーバーヘッドがあり、このオーバーヘッドは、並列化される内容の複雑さに応じて重要な場合と重要でない場合があります。並列機能での作業は非常に小さいため、並列処理が行う必要のある管理のオーバーヘッドが大きくなり、全体的な作業が遅くなります。

于 2011-05-17T19:36:42.313 に答える
10

列挙可能な VS のすべてのスレッドを作成して、numerable を実行するだけの追加のオーバーヘッドが、速度低下の原因である可能性が高くなります。Parallel.ForEach包括的なパフォーマンス向上の動きではありません。各要素に対して完了する操作がブロックされる可能性があるかどうかを検討する必要があります。

たとえば、単にコンソールに書き込むのではなく、Web リクエストなどを行う場合は、並列バージョンの方が高速である可能性があります。そのままでは、コンソールへの単純な書き込みは非常に高速な操作であるため、スレッドの作成と開始のオーバーヘッドは遅くなります。

于 2011-05-17T19:37:27.180 に答える
6

以前のライターがに関連するオーバーヘッドがあると言ったようにParallel.ForEach、それはあなたがあなたのパフォーマンスの改善を見ることができない理由ではありません。Console.WriteLineは同期操作であるため、一度に1つのスレッドのみが機能します。ボディをブロックしないものに変更してみてください。パフォーマンスが向上します(ボディの作業量がオーバーヘッドを上回るのに十分な大きさである限り)。

于 2013-02-26T11:14:43.920 に答える