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

马丁·罗宾逊

下面声明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)normconst实例化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开关编译该示例,您可以看到:

  • 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

马克·格里斯Marc Glisse)(可能)是对的。

取消内联循环表达式,需要使用更高级的通用子表达式消除形式

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么在调用C函数时未引发编译器时间错误

为什么编译器不能匹配此函数类型?

编译器抱怨“未声明opcode_table(此函数首次使用)”,为什么?

C函数调用C ++成员函数-使用C编译器编译C代码

当类具有引用成员时,为什么C ++编译器不删除副本构造函数?

为什么C ++编译器没有优化未使用的参考变量?

为什么C ++编译器不优化对结构数据成员的读写操作,而不是对不同的局部变量进行优化?

是否允许C编译器优化未使用的函数参数?

涉及“使用”别名的成员函数重载解析中的Intel C ++编译器错误?

为什么编译器会警告标有@discardableResult的函数未使用的结果?

为什么我的C ++编译器说虚拟成员没有定义?

为什么与普通函数相比,编译器可以更好地优化lambda?

成员函数C ++的无效使用

为什么C ++编译器(VS2013)选择错误的函数?

在constexpr函数中返回C字符串:为什么编译器没有警告?

为什么某些C编译器会在奇怪的地方设置函数的返回值?

为什么通过引用传递给函数的数组大小对于C ++的编译器是已知的?

为什么CodeBlocks编译器无法识别我在C的头文件中声明的函数?

OCaml使用字节码编译器从C函数返回未装箱的float

为什么编译器报告“可能在此函数中未初始化使用”?

为什么在使用decltype时,编译器为什么不推断成员的类型?

虚拟函数编译器优化C ++

为什么编译器不优化此代码

为什么编译器没有优化此负载

C++ 为什么在不使用被删除的函数时编译器失败,错误代码为 C2280

为什么c ++编译器在对两种不同类型的数字变量使用`std :: max()`函数时会给出错误

为什么在尝试使用C ++ 11样式初始化对象数组时编译器隐式删除构造函数

C ++:为什么成员函数优先于全局函数

如果使用优化(-O2,-O3),为什么此代码的行为会有所不同?