参考文献是否经过优化?

时间:2017-05-29 12:32:28

标签: c++ c++14 compiler-optimization c++17

考虑这段代码示例:

// Static variables
void Object::f_v1()
{
    static const int& foo = dataObjectConstRef.dataField.foo;
    static const int& bar = dataObjectConstRef.dataField.bar;
    static const int& baz = dataObjectConstRef.dataField.baz;
    return foo * foo + baz * bar + baz / bar + baz;
}

// References
void Object::f_v2()
{
    const int& foo = dataObjectConstRef.dataField.foo;
    const int& bar = dataObjectConstRef.dataField.bar;
    const int& baz = dataObjectConstRef.dataField.baz;
    return foo * foo + baz * bar + baz / bar + baz;
}

// Everything written out
void Object::f_v3()
{
    return dataObjectConstRef.dataField.foo * dataObjectConstRef.dataField.foo + 
         dataObjectConstRef.dataField.baz * dataObjectConstRef.dataField.bar + 
         dataObjectConstRef.dataField.baz / dataObjectConstRef.dataField.bar + 
         dataObjectConstRef.dataField.baz;
}

在打开编译器优化的情况下,哪一个是性能最佳的?

  • 静态版本只构造一次引用,但总是如此 之后检查互斥锁。
  • 正常参考版本 应该只计算一次地址,但快捷方式是 在这个版本或最后一个版本中完全优化了 版?
    • 如果没有,那么更快,每次检查一个互斥锁 或生成参考?

我正在使用 gcc 7.1和-O3。

2 个答案:

答案 0 :(得分:4)

  

参考文献是否经过优化?

如果他们可以,是的,他们通常会被优化掉。

除非f_v1本身引用静态对象,否则

dataObjectConstRef具有与其他行为不同的行为。由于在从多个线程调用函数时需要同步,因此它也会有一些性能损失。根据经验:线程同步往往比指针/引用的分配慢。但要衡量它是否重要。

假设这些字段实际上具有int类型(因此不涉及转换)f_v2f_v3具有相同的行为。编译器应该很容易证明这一点,我希望优化编译器能够为两者生成完全相同的代码。

答案 1 :(得分:3)

编写一个最小的例子:

struct DataObject
{
  struct DataField
  {
    double foo, bar, baz;
  } dataField;
};

struct Object
{

// Everything written out
double f_v3()
{
    return dataObjectConstRef.dataField.foo * dataObjectConstRef.dataField.foo + 
         dataObjectConstRef.dataField.baz * dataObjectConstRef.dataField.bar + 
         dataObjectConstRef.dataField.baz / dataObjectConstRef.dataField.bar + 
         dataObjectConstRef.dataField.baz;
}
  DataObject const& dataObjectConstRef;
};

extern const DataObject const& getData();

int main()
{
  Object o { getData() };
  return o.f_v3();
}

使用clang 3.9,-O3:

main:                                   # @main
        push    rax
        call    getData()
        vmovsd  xmm0, qword ptr [rax]   # xmm0 = mem[0],zero
        vmovsd  xmm1, qword ptr [rax + 8] # xmm1 = mem[0],zero
        vmulsd  xmm0, xmm0, xmm0
        vmovsd  xmm2, qword ptr [rax + 16] # xmm2 = mem[0],zero
        vmulsd  xmm3, xmm2, xmm1
        vaddsd  xmm0, xmm0, xmm3
        vdivsd  xmm1, xmm2, xmm1
        vaddsd  xmm0, xmm1, xmm0
        vaddsd  xmm0, xmm2, xmm0
        vcvttsd2si      eax, xmm0
        pop     rcx
        ret

只有3次加载(vmovsd)。

所以是的,在这种情况下,引用的解除引用已被优化。

c ++标准允许编译器执行此操作,因为优化的代码会产生相同的结果,就像原始源代码已逐字执行一样。