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

时间:2017-03-06 06:37:15

标签: c++ gcc optimization clang++

下面声明的C ++ norm类中的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的第二次调用,只计算一次这个结果? This answer似乎表明如果编译器确定norm函数没有任何副作用,编译器应该执行此优化。为什么在这种情况下不会发生这种情况?

请注意,我确定了编译器使用Compiler Explorer生成的内容,并且下面给出了gcc版本5.4的程序集输出。 clang编译器给出了类似的结果。另请注意,如果我使用gcc的编译器属性来手动将__attribute__((const))标记为使用norm的const函数,那么第二个调用会根据我的需要进行优化,但我的问题是为什么gcc (和clang)不会自动执行此操作,因为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 定义可用吗?

foreach($j=2; $j<=10; $j++) {
   $objPHPExcel->getActiveSheet()->getCell("D$j")->setDataValidation(clone $objValidation);
}

1 个答案:

答案 0 :(得分:4)

编译器可以计算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-mathsome observable-behavior-issue

  

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

可能会发生,但编译器并不关心(例如https://stackoverflow.com/a/25472679/3235496)。

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

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

另请注意,使用norm compiler performs CSE标记__attribute__ ((noinline))

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

Marc Glisse(可能)是正确的。

un-inline the recurrent expression需要更高级的 Common Subexpression Elimination 形式。