2017-05-29 70 views
0

考慮這段代碼示例:引用通常被優化掉嗎?

// 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; 
} 

其中哪一個是最好的performancewise,與編譯器的優化已經開啓?

  • 靜態版本僅構建一次引用,但總是在此之後檢查互斥鎖。
  • 正常參考版本 應該只計算一次地址,但快捷方式是 完全優化了在這個版本或在最後的 版本嗎?
    • 如果不是,速度更快,每次檢查互斥鎖的時間是 還是生成引用?

我使用GCC 7.1 -O3。

+9

你爲什麼不看每個函數的生成代碼? –

+0

對我感到羞恥,但我不能讀大會。 –

+3

是否可能或不可能從可能降低可讀性的特定形式中獲得極小的速度增益? – InternetAussie

回答

4

引用通常被優化掉嗎?

如果他們可以,是的,他們通常是優化了。

f_v1與其他人有不同的行爲,除非dataObjectConstRef本身引用靜態對象。由於在多線程中調用該函數的情況下需要同步,所以它也有一些性能損失。作爲一個經驗法則:線程同步往往比分配指針/引用慢。但要衡量它是否重要。

假設這些字段實際上具有類型int(因此不涉及轉換)f_v2f_v3具有相同的行爲。編譯器應該很容易證明這一點,並且我希望優化編譯器能夠爲兩者生成完全相同的代碼。

+0

很好的答案;我注意到在C++標準中採取了一些謹慎的措施,以確保引用沒有單獨的「標識」來引用它們;它們的大小沒有指定,你不能得到指向它們的指針,它們所在的結構與沒有指向的結構是不同的,等等。 – Yakk

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(); 
} 

隨着鐺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++標準允許編譯器執行此操作,因爲優化的代碼導致相同的結果好像原始源代碼已逐字執行。