下面声明norm
的C ++vector
类中的成员函数标记为const
并且(据我所知)不包含任何副作用。
template <unsigned int N>
struct vector {
double v[N];
double norm() const {
double ret = 0;
for (int i=0; i<N; ++i) {
ret += v[i]*v[i];
}
return ret;
}
};
double test(const vector<100>& x) {
return x.norm() + x.norm();
}
如果我在gcc编译器(版本5.4)norm
的const
实例化vector
(请参见test
上面的函数)上调用了多次,并启用了优化功能(即-O3
),则编译器会内联norm
,但仍会norm
多次计算结果,即使结果不应更改。为什么编译器不优化第二个调用,norm
而只计算一次该结果?该答案似乎表明,如果编译器确定norm
函数没有任何副作用,则编译器应执行此优化。为什么在这种情况下不会发生这种情况?
请注意,我正在确定使用Compiler Explorer生成的编译器,并且下面给出了gcc 5.4版的程序集输出。clang编译器给出类似的结果。还要注意,如果我使用gcc的编译器属性使用手动将其标记norm
为const函数__attribute__((const))
,则第二次调用将根据需要进行优化,但是我的问题是为什么gcc(和clang)不自动执行此操作,因为norm
定义可用?
test(vector<100u>&):
pxor xmm2, xmm2
lea rdx, [rdi+800]
mov rax, rdi
.L2:
movsd xmm1, QWORD PTR [rax]
add rax, 8
cmp rdx, rax
mulsd xmm1, xmm1
addsd xmm2, xmm1
jne .L2
pxor xmm0, xmm0
.L3:
movsd xmm1, QWORD PTR [rdi]
add rdi, 8
cmp rdx, rdi
mulsd xmm1, xmm1
addsd xmm0, xmm1
jne .L3
addsd xmm0, xmm2
ret
编译器可以计算的结果norm
并多次重复使用。例如,使用-Os
开关:
test(vector<100u> const&):
xorps xmm0, xmm0
xor eax, eax
.L2:
movsd xmm1, QWORD PTR [rdi+rax]
add rax, 8
cmp rax, 800
mulsd xmm1, xmm1
addsd xmm0, xmm1
jne .L2
addsd xmm0, xmm0
ret
缺少的优化并不是由于非关联的浮点数或某些可观察到的行为问题。
在未正确互斥的环境中,另一个函数可能会在两次调用规范之间更改数组中的内容
可能会发生,但是对于编译器来说并不是问题(例如https://stackoverflow.com/a/25472679/3235496)。
用-O2 -fdump-tree-all
开关编译该示例,您可以看到:
vector<N>::norm()
为纯函数(输出文件.local-pure-const1
);.einline
)。还要注意的是标记norm
与__attribute__ ((noinline))
该编译器执行CSE:
test(vector<100u> const&):
sub rsp, 8
call vector<100u>::norm() const
add rsp, 8
addsd xmm0, xmm0
ret
马克·格里斯(Marc Glisse)(可能)是对的。
要取消内联循环表达式,需要使用更高级的通用子表达式消除形式。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句