请考虑以下代码:
// Preprocessor
#include <iostream>
#include <vector>
// Internal branching
void f1(std::vector<int>& v, const int x = 0)
{
for (unsigned int i = 1; i < v.size(); ++i) {
v[i] = (x != 0) ? (v[i-1]*x) : (v[i-1]+v[i-1]);
}
}
// External branching
void f2(std::vector<int>& v, const int x = 0)
{
if (x != 0) {
for (unsigned int i = 1; i < v.size(); ++i) {
v[i] = v[i-1]*x;
}
} else {
for (unsigned int i = 1; i < v.size(); ++i) {
v[i] = v[i-1]+v[i-1];
}
}
}
// Main
int main()
{
std::vector<int> v(10, 2);
f1(v);
f2(v);
return 0;
}
它说明了产生相同结果的两个函数的行为:
f1
:条件在循环内部进行测试f2
:条件在循环之外进行测试分支基于x
,声明为const
。
我的问题是:当所有优化级别都打开时,编译器是否足够聪明,可以在f1
中转换f2
?
答案 0 :(得分:3)
检查编译器是否将循环条件提升为循环的最佳方法是在使用完全优化编译它之后真正检查程序集。
用以下代码构建示例后
g++ -O3 -c example.cpp -o example.o
objdump -d -M intel example.o > example.S
以下是f1
的内容:
00000020 <f1(std::vector<int, std::allocator<int> >&, int)>:
; ...
23: 8b 54 24 10 mov edx,DWORD PTR [esp+0x10]
27: 8b 7c 24 14 mov edi,DWORD PTR [esp+0x14]
2b: 8b 02 mov eax,DWORD PTR [edx]
2d: 8b 4a 04 mov ecx,DWORD PTR [edx+0x4]
30: 29 c1 sub ecx,eax
32: c1 f9 02 sar ecx,0x2
35: 83 f9 01 cmp ecx,0x1
38: 76 d jbe 57 <f1(std::vector<int, std::allocator<int> >&, int)+0x37>
3a: 31 db xor ebx,ebx
3c: 85 ff test edi,edi
3e: ba 01 00 00 00 mov edx,0x1
43: 75 b jne 60 <f1(std::vector<int, std::allocator<int> >&, int)+0x40>
45: 8b 34 18 mov esi,DWORD PTR [eax+ebx*1]
48: 83 c3 04 add ebx,0x4
4b: 01 f6 add esi,esi
4d: 89 34 90 mov DWORD PTR [eax+edx*4],esi
50: 83 c2 01 add edx,0x1
53: 39 d1 cmp ecx,edx
55: 75 ee jne 45 <f1(std::vector<int, std::allocator<int> >&, int)+0x25>
57: 5b pop ebx
58: 5e pop esi
59: 5f pop edi
5a: c3 ret
5b: 90 nop
5c: 8d 74 26 00 lea esi,[esi+eiz*1+0x0]
60: 8b 34 18 mov esi,DWORD PTR [eax+ebx*1]
63: 83 c3 04 add ebx,0x4
66: 0f af f7 imul esi,edi
69: 89 34 90 mov DWORD PTR [eax+edx*4],esi
6c: 83 c2 01 add edx,0x1
6f: 39 ca cmp edx,ecx
71: 75 ed jne 60 <f1(std::vector<int, std::allocator<int> >&, int)+0x40>
73: eb e2 jmp 57 <f1(std::vector<int, std::allocator<int> >&, int)+0x37>
在第3c行,您会发现要检查的条件:
; if(x != 0)
3c: 85 ff test edi,edi
43: 75 b jne 60 ; ...
从检查后的那一点开始,x
永远不会再次测试,并且只为每个部分执行循环。从{45}到第55行的第一个循环是在x == 0
时完成的。从第60行到第71行的第二个循环在x != 0
时完成。
所以是的,至少在这种情况下,gcc能够在完全优化启用的情况下将条件提升出循环。
答案 1 :(得分:0)
Godbolt page告诉我以下事项:
#6
,#7
如果不输入循环,则立即退出(size()
&lt; = 1)。
#8
,#9
检查x
是否为0,如果为真,则选择一个循环(.L3
+ .L6
),否则将采用第二个循环(.L5
)。
正如您所看到的那样,我们进行了两次优化,包括您正在查询的优化。