为什么注释掉这个 for 循环的前两行并取消注释第三行会导致 42% 的加速?
int count = 0;
for (uint i = 0; i < 1000000000; ++i) {
var isMultipleOf16 = i % 16 == 0;
count += isMultipleOf16 ? 1 : 0;
//count += i % 16 == 0 ? 1 : 0;
}
时序背后是截然不同的汇编代码:循环中有 13 条与 7 条指令。该平台是运行 .NET 4.0 x64 的 Windows 7。启用了代码优化,并且测试应用在 VS2010 之外运行。[更新: 复制项目,用于验证项目设置。]
消除中间布尔值是一项基本优化,是我 1980 年代Dragon Book中最简单的优化之一。在生成 CIL 或 JITing x64 机器代码时,如何优化没有得到应用?
是否有“真正的编译器,我希望你优化这段代码,请”开关?虽然我对过早优化类似于爱钱的观点表示同情,但我可以看到尝试分析一个复杂算法时的挫败感,这种算法在其例程中散布着这样的问题。您将通过热点工作,但没有任何迹象表明可以通过手动调整我们通常认为编译器理所当然的内容来大大改善更广泛的温暖区域。我当然希望我在这里遗漏了一些东西。
更新: x86 也存在速度差异,但取决于方法的即时编译顺序。请参阅为什么 JIT 顺序会影响性能?
汇编代码(根据要求):
var isMultipleOf16 = i % 16 == 0;
00000037 mov eax,edx
00000039 and eax,0Fh
0000003c xor ecx,ecx
0000003e test eax,eax
00000040 sete cl
count += isMultipleOf16 ? 1 : 0;
00000043 movzx eax,cl
00000046 test eax,eax
00000048 jne 0000000000000050
0000004a xor eax,eax
0000004c jmp 0000000000000055
0000004e xchg ax,ax
00000050 mov eax,1
00000055 lea r8d,[rbx+rax]
count += i % 16 == 0 ? 1 : 0;
00000037 mov eax,ecx
00000039 and eax,0Fh
0000003c je 0000000000000042
0000003e xor eax,eax
00000040 jmp 0000000000000047
00000042 mov eax,1
00000047 lea edx,[rbx+rax]