2012-04-22 4 views
1

私はBlockingCollectionよりも非同期メソッドを実行する方が良いでしょうか?

using System; 
using System.Collections.Concurrent; 
using System.Diagnostics; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestBlockingCollection 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      BlockingCollection<int> blockingCollection = new BlockingCollection<int>(); 
      Stopwatch sw = Stopwatch.StartNew(); 

      Task.Factory.StartNew(() => 
      { 
       int i = 0; 
       while (true) 
       { 
        Console.WriteLine("Adding " + i); 
        sw = Stopwatch.StartNew(); 
        blockingCollection.Add(i++); 
        Thread.Sleep(1000); 
       } 
      }); 

      Task.Factory.StartNew(() => 
      { 
       while (true) 
       { 
        int i = blockingCollection.Take(); 
        sw.Stop(); 
        long microseconds = sw.ElapsedTicks/(Stopwatch.Frequency/(1000L * 1000L)); 
        Console.WriteLine("Received " + i + ". Spent " + microseconds + " microseconds."); 
       } 
      }); 

      while(true) 
      { 
       Thread.Sleep(1000); 
      } 
     } 
    } 
} 

結果は期待はずれですどのくらいの速BlockingCollection非同期実行のために測定するために、このような例を書いた:平均で

Adding 0 
Received 0. Spent 19593 microseconds. 
Adding 1 
Received 1. Spent 220 microseconds. 
Adding 2 
Received 2. Spent 38 microseconds. 
Adding 3 
Received 3. Spent 104 microseconds. 
Adding 4 
Received 4. Spent 46 microseconds. 
Adding 5 
Received 5. Spent 37 microseconds. 
Adding 6 
Received 6. Spent 112 microseconds. 
Adding 7 
Received 7. Spent 103 microseconds. 
Adding 8 
Received 8. Spent 104 microseconds. 
Adding 9 
Received 9. Spent 384 microseconds. 
Adding 10 
Received 10. Spent 102 microseconds. 
Adding 11 
Received 11. Spent 39 microseconds. 
Adding 12 
Received 12. Spent 51 microseconds. 
Adding 13 
Received 13. Spent 42 microseconds. 
Adding 14 
Received 14. Spent 40 microseconds. 
Adding 15 
Received 15. Spent 40 microseconds. 
Adding 16 
Received 16. Spent 42 microseconds. 
Adding 17 
Received 17. Spent 40 microseconds. 
Adding 18 
Received 18. Spent 41 microseconds. 
Adding 19 
Received 19. Spent 42 microseconds. 
Adding 20 
Received 20. Spent 62 microseconds. 
Adding 21 
Received 21. Spent 36 microseconds. 
Adding 22 
Received 22. Spent 39 microseconds. 
Adding 23 
Received 23. Spent 35 microseconds. 
Adding 24 
Received 24. Spent 40 microseconds. 
Adding 25 
Received 25. Spent 63 microseconds. 
Adding 26 
Received 26. Spent 56 microseconds. 
Adding 27 
Received 27. Spent 42 microseconds. 
Adding 28 
Received 28. Spent 41 microseconds. 
Adding 29 
Received 29. Spent 42 microseconds. 
Adding 30 
Received 30. Spent 41 microseconds. 
Adding 31 
Received 31. Spent 651 microseconds. 
Adding 32 
Received 32. Spent 43 microseconds. 
Adding 33 
Received 33. Spent 58 microseconds. 
Adding 34 
Received 34. Spent 43 microseconds. 
Adding 35 
Received 35. Spent 41 microseconds. 
Adding 36 
Received 36. Spent 59 microseconds. 
Adding 37 
Received 37. Spent 38 microseconds. 
Adding 38 
Received 38. Spent 38 microseconds. 
Adding 39 
Received 39. Spent 38 microseconds. 
Adding 40 
Received 40. Spent 42 microseconds. 
Adding 41 
Received 41. Spent 59 microseconds. 
Adding 42 
Received 42. Spent 40 microseconds. 
Adding 43 
Received 43. Spent 42 microseconds. 
Adding 44 
Received 44. Spent 41 microseconds. 
Adding 45 
Received 45. Spent 39 microseconds. 
Adding 46 
Received 46. Spent 42 microseconds. 
Adding 47 
Received 47. Spent 41 microseconds. 
Adding 48 
Received 48. Spent 41 microseconds. 
Adding 49 
Received 49. Spent 42 microseconds. 
Adding 50 
Received 50. Spent 35 microseconds. 
Adding 51 
Received 51. Spent 42 microseconds. 
Adding 52 
Received 52. Spent 39 microseconds. 
Adding 53 
Received 53. Spent 43 microseconds. 
Adding 54 
Received 54. Spent 35 microseconds. 
Adding 55 
Received 55. Spent 60 microseconds. 
Adding 56 
Received 56. Spent 59 microseconds. 
Adding 57 
Received 57. Spent 55 microseconds. 
Adding 58 
Received 58. Spent 74 microseconds. 
Adding 59 
Received 59. Spent 56 microseconds. 
Adding 60 
Received 60. Spent 42 microseconds. 

は、私は約50マイクロ秒を費やしたが、時々私は600マイクロ秒まで過ごしました!

私の遅いPentium U5400を使用しても、それはいくつかの定数で、10を超えてはならず、10マイクロ秒を超えることはありません。

.NETがasync execに対して高速な方法はありますか?非同期execがスケジュールされた後、私はそれをできるだけ早く開始する必要があります。これは財務的に時間の影響を受けやすい計算です。

ブロックコレクションは順序やアイテムは一つずつ処理されるgaranteesをgaranteesので、この質問は、実際に含まれている2質問

  1. 私は順序が必要な場合は、我々はより高速な何かを持っていると私は内で処理すべき項目が必要なのですか彼らは登場しましたか?私。私はFIFOクエリが必要です。
  2. 私が注文を気にしない場合、私たちはもっと速いものを持っていますか?アイテムが1つずつまたは平行して処理されるかどうか気にしませんか?私は答えがあることを推測する

:私はそれを参照してくださいis it good to use BlockingCollection<T> as single-producer, single-consumer FIFO query?

  • ためBlockingCollectionを使用する必要が

    1. いいえ、私はおそらく、デリゲートを試すことができますか? http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx

  • +1

    スレッドがブロックされた後に実行を再開するのにかかる時間を測定しています。はい、時間がかかります。 Trueを返すまでループ内でTryTake()を呼び出すことにより、ホールド・アンド・キャッチ・ファイア・ループと比較してください。スレッドは、ブロックしないと最も効果的です。 –

    答えて

    0

    まずあなたが本当にマイクロ心配している場合(それはリアルタイムOSではないので)、多分あなたは、.NET(そのガベージコレクタはあなたに予測不可能な遅延を与えることができるため)またはWindowsを使用しないでください。実際にあなたの質問に答えるために

    1. 私は50マイクロ秒の時間とBlockingCollection<T>の本当に小さな量が高度に最適化されていると思うので、私はあなたがはるかに速くそれを行うことができるとは思いません。あなたはスピンロックを使ってそれを行うことができるかもしれませんが、CPU時間を無駄にする無駄を犠牲にします。

    2. デリゲートを非同期に呼び出す方法は、ThreadPool.QueueUserWorkItem()を使用する方法とほぼ同じです。だから、これはおそらくあなたが得ることのできる最高のものに近いでしょう。同期呼び出しは常に高速になりますが。

    +0

    私のアプリケーションでは50マイクロ秒が良い時間です。しかし、サーバー上で私はより良い結果を持っています - ちょうど14マイクロ秒と新しいサーバーで私は何か良いことができることを願っています。しかし、私はそれほど多くはありませんので、すべてのマイクロ秒が重要です。何百回も行うには100〜200マイクロ秒です。私はリアルタイムOSは必要ありません。これまで.NETを使用しなければならないのですが、.NETに書かれているすべてのものがあります。 – javapowered

    2

    実験の良い数時間後、私はそれが途方もない測定だと思う。私は10000のために実行するように変更し、結果は以下の通りです:

    合計100.0回の100000回の反復、平均0。00214 ms

    コードはあなたのものとほとんど同じです。私はリリースビルドをしました

    私はBarrierを使って実験しましたが、それは遅かったです。私はまたロックを使ってやろうとしましたが、その作業をすることに失敗しました。

    static void Main(string[] args) 
        { 
         var barrier = new Barrier(2); 
         var collection = new List<int>(); 
         var num_iterations = 100000; 
         var iterations = num_iterations; 
         var total_time_ms = 0.0M; 
    
         Stopwatch sw = new Stopwatch(); 
    
    
         total_time_ms = 0.0M; 
         iterations = num_iterations; 
    
         var blockingCollection = new BlockingCollection<int>(); 
    
         Task.Factory.StartNew(() => 
         { 
          int i = 0; 
    
          while (iterations-- > 0) 
          { 
           sw.Restart(); 
           blockingCollection.Add(i++); 
          } 
         }); 
    
    
         Task.Factory.StartNew(() => 
         { 
          var expected_value = 0; 
    
          while (iterations > 0) // stop when performed certain number 
          { 
           int i = blockingCollection.Take(); 
           sw.Stop(); 
           long microseconds = sw.ElapsedTicks/(Stopwatch.Frequency/(1000L * 1000L)); 
           total_time_ms += microseconds; 
           if (i != expected_value) 
            Console.WriteLine(String.Format("** expected {0} got {1}", i, expected_value)); 
           expected_value++; 
          } 
         }); 
         while (iterations > 0) 
         { 
          Thread.Sleep(1000); 
         } 
         Console.WriteLine(String.Format("Total {0} for {1} iterations, average {2}", total_time_ms, num_iterations, total_time_ms/num_iterations)); 
    

    私はBlockingCollectionがこれに使うには良いコレクションだと思います。それを行う他の方法がありますが、これは複雑な領域であり、これより速く何かを得る機会はありそうもありません。

    +0

    この改善はそれほど変わっていません。私のコンピュータでは、私も少し少ない番号を持っています。サーバー上では両方のバージョンが "14マイクロ秒"を生成します。 – javapowered

    +0

    私はもう少し調査をしました - 私はそれが測定と睡眠であり、それがパフォーマンスに影響を与えていると思っています。 –

    +0

    あなたの 'sw'変数へのアクセスはスレッドセーフではありません。この結果を信じてはいけません。 'Stop'を呼び出す前に' Restart'を何度も呼び出す必要があるからです。あなたはconcurently次の値を押して、それから "平均"値を計算する運賃ではないので、前の値を取る。 – javapowered

    関連する問題