如果注释掉这个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之外运行。[更新:重现项目,可用于验证项目设置。]
消除中间的布尔型变量是一种基本优化,是我上世纪80年代的龙书中最简单的优化之一。为什么在生成CIL或JIT 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]
var
的意思是“编译器,请推断出这个变量的类型,并假装我写了它”。在这种情况下,它会自行推断出bool
类型。 - Damien_The_Unbeliever