为什么编译器使用-O3不能优化此C++成员函数?

Mar*_*son 9 c++ optimization gcc clang++

下面声明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();
}
Run Code Online (Sandbox Code Playgroud)

如果我打电话norm多次上const的实例vector(见test上面的函数)与GCC编译器(5.4版本)和优化开启(即-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
Run Code Online (Sandbox Code Playgroud)

man*_*lio 4

编译器可以计算结果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
Run Code Online (Sandbox Code Playgroud)

缺失的优化不是由于非关联浮点数学某些可观察行为问题造成的。


在未正确互斥的环境中,另一个函数可能会在调用norm之间更改数组中的内容

它可能会发生,但这不是编译器关心的问题(例如/sf/answers/1783087561/)。


使用开关编译示例,-O2 -fdump-tree-all您可以看到:

  • g++ 正确检测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
Run Code Online (Sandbox Code Playgroud)

马克·格利斯(可能)是对的。

需要更高级形式的公共子表达式消除来取消内联循环表达式


归档时间:

查看次数:

334 次

最近记录:

8 年,7 月 前