循环中的常量条件:编译器优化
Posted
技术标签:
【中文标题】循环中的常量条件:编译器优化【英文标题】:Constant condition in a loop: compiler optimization [duplicate] 【发布时间】:2013-08-15 16:11:18 【问题描述】:考虑以下代码:
// 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
?
【问题讨论】:
对于这段代码,我希望编译器完全消除检查,只为x == 0
构建代码。您当然可以提出编译器无法做到这一点的场景,但在这种情况下,当然可以。 [当然,如果你真的关心你的特殊情况,那么用你的编译器和你的真实代码对它进行基准测试!]
【参考方案1】:
Godbolt page 告诉我以下事情:
#6
,#7
如果永远不会进入循环,则立即退出 (size()
#8
,#9
检查x
是否不为0,如果为真,则选择一个循环(.L3
+ .L6
),否则将进行第二个循环(.L5
) .
如您所见,执行了两项优化,包括您查询的一项。
【讨论】:
【参考方案2】:检查您的编译器是否将条件提升到循环之外的最佳方法是在使用完全优化编译后真正检查程序集。
在构建您的示例之后:
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 能够在启用完全优化的情况下将条件提升到循环之外。
【讨论】:
以上是关于循环中的常量条件:编译器优化的主要内容,如果未能解决你的问题,请参考以下文章