for (int i = 0; i < 100000000; i++)
y += t.X;
これは、プロファイリングが非常に難しいコードです。Debug + Windows + Disassembly で生成されたマシン コードを見ると、それがわかります。x64 コードは次のようになります。
0000005a xor r11d,r11d ; i = 0
0000005d mov eax,dword ptr [rbx+0Ch] ; read t.X
00000060 add r11d,4 ; i += 4
00000064 cmp r11d,5F5E100h ; test i < 100000000
0000006b jl 0000000000000060 ; for (;;)
これは大幅に最適化されたコードです。+= 演算子が完全になくなったことに注意してください。ベンチマークで間違いを犯したため、これが発生することを許可しました。計算された y の値をまったく使用していません。ジッターはこれを知っているので、無意味な追加を単純に削除しました。4 ずつインクリメントすることにも説明が必要です。これは、ループ展開の最適化の副作用です。後で使用されます。
したがって、ベンチマークを現実的なものにするために変更を加える必要があります。最後に次の行を追加します。
sw.Stop();
Console.WriteLine("{0} msec, {1}", sw.ElapsesMilliseconds, y);
これにより、y の値が強制的に計算されます。今では完全に異なって見えます:
0000005d xor ebp,ebp ; y = 0
0000005f mov eax,dword ptr [rbx+0Ch]
00000062 movsxd rdx,eax ; rdx = t.X
00000065 nop word ptr [rax+rax+00000000h] ; align branch target
00000070 lea rax,[rdx+rbp] ; y += t.X
00000074 lea rcx,[rax+rdx] ; y += t.X
00000078 lea rax,[rcx+rdx] ; y += t.X
0000007c lea rbp,[rax+rdx] ; y += t.X
00000080 add r11d,4 ; i += 4
00000084 cmp r11d,5F5E100h ; test i < 100000000
0000008b jl 0000000000000070 ; for (;;)
まだ非常に最適化されたコードです。奇妙な NOP 命令は、アドレス 008b でのジャンプが効率的であることを保証し、16 にアラインされたアドレスへのジャンプは、プロセッサ内の命令デコーダ ユニットを最適化します。LEA 命令は、アドレス生成ユニットに加算を生成させるための古典的なトリックであり、メイン ALU が同時に他の作業を実行できるようにします。ここで行う作業は他にありませんが、ループ本体がもっと複雑な場合は行うことができます。また、ループは分岐命令を避けるために 4 回展開されました。
とにかく、削除されたコードではなく、実際のコードを実際に測定していることになります。テストを 10 回繰り返した私のマシンでの結果 (重要!):
y += t.X: 125 msec
y += t.Y: 125 msec
まったく同じ時間です。もちろん、その通りであるべきです。物件にお金はかかりません。
ジッタは、高品質のマシン コードを生成する優れた仕事をします。奇妙な結果が得られた場合は、常に最初にテスト コードを確認してください。一番間違いやすいコードです。ジッターではなく、徹底的にテストされています。