为什么这个 C++ 成员函数没有被编译器用 -O3 优化?

Why isn't this C++ member function optimized by the compiler with -O3?

下面声明的C++ vector class中的norm成员函数被标记为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)在 vectorconst 实例化(参见上面的 test 函数)上多次调用 norm 并进行优化on(即 -O3),则编译器内联 norm,但仍会多次计算 norm 的结果,即使结果不应更改。为什么编译器不优化对 norm 的第二次调用而只计算一次这个结果? This answer 似乎表明如果编译器确定 norm 函数没有任何副作用,则编译器应该执行此优化。为什么在这种情况下没有发生这种情况?

请注意,我正在确定编译器使用 Compiler Explorer 生成的内容,下面给出了 gcc 5.4 版的汇编输出。 clang 编译器给出了类似的结果。另请注意,如果我使用 gcc 的编译器属性手动将 norm 标记为使用 __attribute__((const)) 的 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的结果并多次重复使用。例如。 with the -Os switch:

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

缺少优化不是由于 not-associative-floating-point-math or some observable-behavior-issue


In a not properly mutexed environment another function might change the contents in the array in between the calls of norm

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


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

  • g++ 将 vector<N>::norm() 正确检测为纯函数(输出文件 .local-pure-const1);
  • 内联发生在早期阶段(输出文件 .einline)。

还要注意用 __attribute__ ((noinline)) 标记 norm compiler performs CSE:

test(vector<100u> const&):
    sub     rsp, 8
    call    vector<100u>::norm() const
    add     rsp, 8
    addsd   xmm0, xmm0
    ret

(可能)是对的。

公共子表达式消除的更高级形式需要un-inline the recurrent expression