2016-04-03 12 views
1

EDIT: [ツール]> [オプション]> [デバッグ]> [モジュールロード時にJIT最適化を抑制]>チェックを外す(デバッグモードでもJIT最適化を行うにはこのオプションをオフにします) 。擬似コードが利用可能な場合、生成されたマシンコードは良くないことがわかります! 高速版では、コードはCPUを直接使用してを登録しますが、それ以外のバージョンでは、すべての繰り返しでメモリからp1.xとp1.yをロードします!私はそのような行動をどうやってコントロールできるのか分かりません!どのように単純なコード行が次のコードの実行時間に大きく影響するか?

パフォーマンステスト用のコードブロックの実行時間を測定しようとしていたとき、私は混乱していました。実際の測定ループの前に(疑似)コードブロックを追加すると、合計経過時間は大きな要因(から120ミリ秒から220ミリ秒、すなわち、2GHzのPCでは約1.8倍遅い!)増加しました。 実際には、これらの擬似コードは、初期化または同様の目的に必要ないくつかの異なるコードです。

注:私はリリースモードでアプリを構築し、すべての最適化が最良の結果を得るために適用されることを保証するために、デバッグなしスタート(Ctrlキー+ F5)てそれを実行します。

私はサンプルとしてここに簡略化されたシナリオを示しています。

void test() 
{ 
    Point p1 = new Point(1, 2); 
    Point p = p1; 
    //*** !!! Comment & UnComment the following 2 lines to see difference: *** 
    p.Offset(p1); p.Offset(p1); p.Offset(p1); p.Offset(p1); 
    p.Offset(p1); p.Offset(p1); p.Offset(p1); p.Offset(p1); 

    Stopwatch timer = new Stopwatch(); 
    double dt = -1; 

    for (int repeat = 1; repeat <= 5; repeat++) 
    { 
     p = p1; //here we reset the initial p 

     timer.Restart(); 
     for (int i = 0; i < 50000000; i++) 
     { 
      p.Offset(p1); 
     } 
     timer.Stop(); 
     dt = timer.ElapsedMilliseconds; 

     textBox1.Text += repeat + "] " + dt + ", p: " + p + "\r\n"; 
     Application.DoEvents(); if (this.IsDisposed) break; 
    } 
} 

private void button1_Click(object sender, EventArgs e) 
{ 
    test(); 
} 

この問題は、私は最適化のために必要な結果の比較から私を防ぎます。 ところで、これはstructタイプ(?)に関連している可能性があります。

理由と解決方法は何ですか。

EDIT(4月4日):この現象はstructのタイプではなく、classタイプでのみ発生することを確認しました。私たちが知っているように、構造体は通常、ヒープではなくスタックの値型です。

別のノート:私は、プロジェクトのプロパティ>ビルドタブで[コードの最適化]オプションのチェックを外すと、コードが実際に速く実行されることも発見しました。

2例のスクリーンショットを以下に示す: Run Screenshot

DIS-アセンブリ:

dis-assembly Screenshot
+0

VSの外でリリースビルドを使用していることを確認してください。 – MarcinJuraszek

+4

私は問題を再現できません。私の結果は、2つの行がコメントされているかどうかに関係なく、同じ実行セッション内で100から600まで変化します。リリースビルド、デバッガなし。 –

+0

注:私は 'release mode'でアプリケーションをビルドし、' Start Without Debug'を押します。これらの設定がなければ、結果はVisual Studioによって最適化されず、毎回変化します! –

答えて

1

興味深い疑問を。

これらの場合、アセンブラは常に真実を伝えます(CILではなく、まったく同じです)。あなたの事が最も内側のループのアセンブラを変更する境界条件を引き起こしたのかもしれません。

私はコードをコンソールアプリケーションに変更し、必要な調整(最適化フラグの抑制など)、モードの解除、ブレークポイントの設定、F5を押し、ctrl-alt-dを押しました。

注:これらのタイミングを得るには、テストケースを10倍に増やす必要があることにも気付きました。タイミングは私が期待した通り全く同じです。

まだ、f.exに問題がある可能性があります。レジスタの割り当てを確認してみましょう。アセンブラーは決して嘘をつかない。

ケース1アセンブラ。

   for (int i = 0; i < 500000000; i++) 
00007FFE23C74526 xor   ecx,ecx 
       { 
        p.Offset(p1); 
00007FFE23C74528 inc   r14d 
00007FFE23C7452B add   r15d,2 
       for (int i = 0; i < 500000000; i++) 
00007FFE23C7452F inc   ecx 
00007FFE23C74531 cmp   ecx,1DCD6500h 
00007FFE23C74537 jl   00007FFE23C74528 
       } 

ケース2アセンブラ

   for (int i = 0; i < 500000000; i++) 
00007FFE23C84526 xor   ecx,ecx 
       { 
        p.Offset(p1); 
00007FFE23C84528 inc   r14d 
00007FFE23C8452B add   r15d,2 
       for (int i = 0; i < 500000000; i++) 
00007FFE23C8452F inc   ecx 
00007FFE23C84531 cmp   ecx,1DCD6500h 
00007FFE23C84537 jl   00007FFE23C84528 
       } 

結論

アセンブラ出力は全く同じであるので、データがある - 換言すれば、性能内側のループもまったく同じです。

まだ説明したように、アセンブラの出力を自分でチェックしてください。別のバージョンの.NET JITをお持ちの場合は、その動作を説明する必要があります。


放射されたアセンブラコードをどのようにテストすべきかについて、明らかに混乱があります。ここではそれを行うための正しい方法です:

  • ツール - >オプション - >デバッグ - モジュールのロード上>抑制するJITの最適化 - >チェックを外します(このオプションをオフにした場合は、最適化されたJITコードをデバッグするが、あなたの能力にすることができますデバッグは制限されている可能性があります)
  • 同じ設定ウィンドウで「自分のコードだけ」を無効にします。
  • ビルドタイプを「リリース」モードに設定します。また、構成マネージャーを確認してください。
  • プロジェクト - >プロパティ - >ビルドタブを右クリックします。 「コードの最適化」をチェックしてください。
  • オプション、同じタブ:「32ビットコードを優先」がチェックされていないことを確認してください。 (注:32ビットのJITと64ビットのJITは、2つの異なるものです。)

セットブレークポイント、F5、Ctrl + Alt + D、そうでない場合は、ブレークポイントがヒットした祈るどこかにそれを設定してみてください再び。


私はコメントのいくつかによって、少し驚いているので、私はそれらの上に手放す:

  • @Douglasは長い方法はあまり効率的なコードになることを指摘しています。はい、私は実際にこれについてMicrosoft CoreCLRチームにバグレポートを作成しました。これは当てはまりますが、非常に大きなメソッドをコンパイルするときにのみJITオプティマイザが無効になるという注意があります。この文脈では、60,000行のILコードが非常に大きい。正確な制限はここにあります:https://github.com/dotnet/coreclr/blob/01a5e9b4580cf6ea21de672f627402c30658ef22/src/jit/compiler.h#L7131。この場合、それは完全に無関係です。
  • @ Noldorinは、OPがILコードを調べるべきであると述べています。発行されたILコードは、両方のシナリオでまったく同じであることを指摘したいと思います。
+0

"最適化フラグを抑止してください"という意味を正確に説明できますか? –

+0

メモリが役立つ場合、Visual Studioのコンパイラ設定のほんの一部です。簡単に見つけることができます。通常、デバッガ(またはデバッグモード)のコードを実行すると、JITの最適化が妨げられ、誤った結果が得られます。私はまた、32ビットのデフォルトジッタを無効にすることを習慣にしています(プロジェクトのプロパティで見つけることができます)。 – atlaste

+0

あなたはコードをテストしましたか?投稿で言及したように 'Start Without Debug'ですか? 「32ビットのデフォルトジッタを無効にする」とはどういう意味ですか?正確な設定をお願いしますか?私は知っているように、C#には最適化を制御するオプションがたくさんあります。プロジェクトプロパティ>ビルドタブで、 'Config Manager:Release'と 'Optimize Code'チェックボックスをオンにします。 –

関連する問題