2012-04-22 7 views
0

私はいくつかの同期コードに同時実行性を追加したいと思って、理解しにくいプロセスでパフォーマンスの問題を発見しました。Task.WaitAll()と実行Task.Waitのパフォーマンスのギャップの問題

以下のコードの実行結果は、次のとおり

Mission Fibonacci1Async cost 9.4195388 seconds, value 75025 
Mission Fibonacci2Async cost 0.2260129 seconds, value 75025 

異なるのみ2RD機能はラインを添加するTask.WhenAll(新しいタスク[] {T1、T2を})待ちます。の場合、パフォーマンスを40倍向上させます。

誰でも私に説明できますか?

static Task<int> Fibonacci1Async(int n) 
    { 
     return Task.Run<int>(() => Fibonacci1(n)); 
    } 

    static int Fibonacci1(int n) 
    { 
     if (n == 0) return 0; 
     else if (n == 1) return 1; 
     else 
     { 
      var t1 = Fibonacci1Async(n - 1); 
      var t2 = Fibonacci1Async(n - 2); 
      return t1.Result + t2.Result; 
     } 
    } 

    static Task<int> Fibonacci2Async(int n) 
    { 
     return Task.Run<int>(() => Fibonacci2(n)); 
    } 

    static int Fibonacci2(int n) 
    { 
     if (n == 0) return 0; 
     else if (n == 1) return 1; 
     else 
     { 
      var t1 = Fibonacci2Async(n - 1); 
      var t2 = Fibonacci2Async(n - 2); 

      Task.WaitAll(new Task[] { t1, t2 }); 
      return t1.Result + t2.Result; 
     } 
    } 

    static void Benchmark(Func<int, Task<int>> func) 
    { 
     DateTime time = DateTime.Now; 
     var task = func(25); 
     task.Wait(); 
     TimeSpan cost = DateTime.Now - time; 
     Console.WriteLine("Mission {0} cost {1} seconds value {2}", func.Method.Name, cost.TotalSeconds, task.Result); 
    } 

    static void Main(string[] args) 
    { 
     Benchmark(Fibonacci1Async); 
     Benchmark(Fibonacci2Async); 
     Console.ReadKey(); 
     return; 
    } 

答えて

1

回答はTask.Wait inliningとする必要があります。

t1.Result + t2.Resultでは、+オペレータは、その引数を(左から右へ)引数を(シリアルに)評価します。したがって、t1をブロックし、次にt2をブロックします。

あなたのシステムでは、多くの場合、t1は既に起動していますが、t2ではなくなっていると思います。この場合、Task.WaitAllは、新しいスレッドプールタスクを開始するのではなく、t2を現在のスレッドプールタスクに「インライン化」できますが、+はブロックするでしょう。t1です。

これは単なる推測です。プロファイラを使用して、何が起こっているかを正確に把握する必要があります。

これは私のシステムでは再現できませんでした。私はいつも両方のバージョンがプロセスに適用されたプロセッサの親和性であってもほぼ同じであると見ています。

P.S.命名規則Asyncは実際にはここでは適用されません。このコードはasync/awaitを使用していません - タスク並列ライブラリを使用しています。

+0

命名規則は、await/asyncとは関係ありません。これは実装の詳細です。これは、呼び出し側がタスクを受け取り、非ブロック動作を取得することを指定します。 – usr

+0

ポスト 'Async'と戻り値の型を組み合わせると、メソッドはタスクベースの非同期パターンを使用します。これらの '* Async'メソッドがTAPであるケースを作ることができると思いますが、確かにTAP実装の良い例ではありません。 –

+0

FileStream.ReadAsyncは、内部的にawaitを使用しません。 IO完了ポートを使用します。何も間違っていません。発信者は知る必要はありません。 – usr

関連する問題