考虑以下 C 程序:
(当然,人们可以更喜欢在相关块内使用返回语句来限制缩进大小。)
void bar();
void baz();
void foo( int a ) {
if ( a ) {
bar();
}
else {
baz();
}
}
在我的基于x86-64的计算机上,使用GCC进行-O1优化级别生成的指令如下:
0: sub $0x8,%rsp
4: test %edi,%edi
6: je 14 <foo+0x14>
8: mov $0x0,%eax
d: callq 12 <foo+0x12> # relocation to bar
12: jmp 1e <foo+0x1e>
14: mov $0x0,%eax
19: callq 1e <foo+0x1e> # relocation to baz
1e: add $0x8,%rsp
22: retq
如果添加 -freorder-blocks 优化参数(包含在 -O2 中),则代码会变成:
0: sub $0x8,%rsp
4: test %edi,%edi
6: jne 17 <foo+0x17>
8: mov $0x0,%eax
d: callq 12 <foo+0x12> # relocation to baz
12: add $0x8,%rsp
16: retq
17: mov $0x0,%eax
1c: callq 21 <foo+0x21> # relocation to bar
21: add $0x8,%rsp
25: retq
主要是从“跳转等于”变成“跳转不等于”。我知道在 Pentium 4 之前,处理器认为条件向前跳转的静态分支预测不会被采用(似乎在后来的 Intel 处理器上,静态预测变成了随机预测),因此我想这种优化是针对这个问题而进行的。
假设并参考经过优化的“jne”版本,这意味着实际上在程序流程中,else 块比 if 块更可能被执行。
但这到底意味着什么?由于编译器在 foo 函数中没有对 a 值做任何假设,因此这种概率仅依赖于程序员的编写(实际上程序员可以使用 if ( !a )
而不是 if ( a )
并反转函数调用)。
这是否意味着应该将 if 条件块视为异常情况(而不是正常执行流程)?
也就是说:
if ( !cond ) {
// exceptional code
}
else {
// normal continuation
}
改为:
if ( cond ) {
// normal continuation
}
else {
// exceptional code
}
(当然,人们可以更喜欢在相关块内使用返回语句来限制缩进大小。)
je
版本上实现。GCC有意识地重新排列if/else块:在初始程序中将if(a)
更改为if(!a)
,编译结果完全相反:从jne
(非优化)版本到je
(分支顺序优化)版本。我不敢相信GCC只是为了取笑我而做出这种改变! :) - lledr