2013-05-06 7 views
17

私はコードが決して実行されなくても、ロックされたコードが私のコードをどのように遅くすることができるのだろうかと思います。以下はその例です:ロックされたコードによる減速を回避するにはどうすればよいですか?

public void Test_PerformanceUnit() 
{ 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    Random r = new Random(); 
    for (int i = 0; i < 10000; i++) 
    { 
     testRand(r); 
    } 
    sw.Stop(); 
    Console.WriteLine(sw.ElapsedTicks); 
} 

public object testRand(Random r) 
{ 
    if (r.Next(1) > 10) 
    { 
     lock(this) { 
      return null; 
     } 
    } 
    return r; 
} 

このコードは、私のマシンで〜1300ミリ秒で実行されます。ロックブロックを取り除いても(ボディはそのままにしておくと)、750msになります。たとえコードが決して走っていなくても、ほぼダブル!

もちろん、このコードは何もしません。オブジェクトが初期化されているかどうかをコードがチェックし、初期化していない場合にはクラス内でいくつかの遅延初期化を追加していました。問題は、初期化がロックされ、最初の呼び出しの後でもすべてが遅くなることです。

私の質問は以下のとおりです。

  1. ですが、なぜでしょうか?景気減速を回避するために、どのよう
+0

'lock'を集中的に使うつもりがない限り、私は本当に心配しません。 – James

+4

同様の結果が得られますが、ティックは100 * nano *秒です。両方の実行には〜0msが必要です(つまり 'sw.ElapseMilliseconds'を出力した場合)。この" slowdown "(〜0.00006s)は' lock'に 'try/finally'ブロックが含まれている可能性が高いですメソッドが呼び出されたときのセットアップ。 'testRand'の内容をループ自体に入れてみてください。その時点ではほとんど減速しません。 – dlev

+0

あなたは 'AggressiveInline'でメソッドをマークしようとしましたか?おそらく、ロッキングコードは、このメソッドを通常のインライン展開には大きすぎます。 .net JITterは、ILコードのサイズに基づいて、ややばかばかしいヒューリスティックを使用してインライン展開します。 – CodesInChaos

答えて

9

それが起こっている理由について、それはコメントで議論されている:それはlockによって生成try ... finallyの初期化が原因です。


そして、この減速を回避するための方法が実際に呼び出された場合にロック機構のみが初期化されるように、あなたは、新しいメソッドにロック機能を抽出することができます。より少し遅く実行している私のテストコード(:

public object testRand(Random r) 
{ 
    if (r.Next(1) > 10) 
    { 
     return LockingFeature(); 
    } 
    return r; 
} 

private object LockingFeature() 
{ 
    lock (_lock) 
    { 
     return null; 
    } 
} 

そして、ここでは(ティックで)私の時間です:

your code, no lock : ~500 
your code, with lock : ~1200 
my code    : ~500 

EDIT

私はこの単純なコードでそれを試してみましたロックを持たないコード)は実際には静的メソッド上にあったため、コードがオブジェクトの「内部」で実行されている場合、タイミングは同じに見えます。私はそれに従ってタイミングを修正した。

+0

答えてくれてありがとう、これはまさに私が探していたものでした。私のテストでは、あなたのソリューションは 'lock'インラインより速く走っていましたが、' return null'だけでは遅くなりました。コードのインライン展開を避けるために 'LockingFeature'メソッドを' virtual'と定義しました。私は100%パフォーマンスを取り戻しました。 – pieroxy

+0

@pieroxy - あなたの最初のテストについての別のことは、ロック付き 'testRand()'のバージョンがJITにも長くかかるということです。だから、 'StopWatch'が始まる前に' testRand() 'を1回呼び出すだけで(JITコンパイラをウォーミングアップする方法として)、これを式から取り除くことができます。これにより、ギャップが大幅に狭まる。それでも、Zonkoのコードはこれを扱うかなり滑らかな方法です。 –

関連する問題