2017-10-16 32 views
7

第一個版本通過將值從內存移到局部變量來進行優化。第二個版本沒有。爲什麼編譯器爲此循環的每次迭代都將一個成員變量寫入內存?

我在期待編譯器可能會選擇在這裏進行localValue優化,而不是每次循環讀取和寫入內存值。爲什麼不呢?

class Example 
{ 
    public: 
     void processSamples(float * x, int num) 
     { 
      float localValue = v1; 

      for (int i = 0; i < num; ++i) 
      { 
       x[i] = x[i] + localValue; 
       localValue = 0.5 * x[i]; 
      } 

      v1 = localValue; 
     } 

     void processSamples2(float * x, int num) 
     { 

      for (int i = 0; i < num; ++i) 
      { 
       x[i] = x[i] + v1; 
       v1 = 0.5 * x[i]; 
      } 

     } 

    float v1; 
}; 

processSamples組裝到這樣的代碼:

.L4: 
    addss xmm0, DWORD PTR [rax] 
    movss DWORD PTR [rax], xmm0 
    mulss xmm0, xmm1 
    add rax, 4 
    cmp rax, rcx 
    jne .L4 

processSamples2這樣:

.L5: 
    movss xmm0, DWORD PTR [rax] 
    addss xmm0, DWORD PTR example[rip] 
    movss DWORD PTR [rax], xmm0 
    mulss xmm0, xmm1 
    movss DWORD PTR example[rip], xmm0 
    add rax, 4 
    cmp rax, rdx 
    jne .L5 

當編譯器不必擔心螺紋(V1不是原子) 。難道它不能只是假設沒有別的東西會看着這個值,並繼續在循環旋轉時將它保存在寄存器中?

請參閱https://godbolt.org/g/RiF3B4以獲得完整的裝配和編譯器供您選擇!

+0

您鏈接的示例存在另一個問題。首次使用時,「v1」未初始化。這是UB,會導致gcc和clang在優化時間做出怪異的事情。 –

+0

哦,是的 - 公平點 - 這不是真正的代碼,雖然我只是想展示我感興趣的問題,v1的出發點並不重要。實際的代碼更精緻一點。 – JCx

+0

有趣和警示閱讀: https://blog.regehr.org/archives/759 –

回答

11

由於aliasingv1是一個成員變量,它可能是x指向它。因此,寫入x元素之一可能會更改v1

在C99中,您可以在指針類型的函數參數上使用restrict關鍵字來通知編譯器它不會別名在函數範圍內的其他任何別名。一些C++編譯器也支持它,儘管它不是標準的。 (複製自我的評論之一)

+0

哇 - 好的 - 那麼有沒有告訴它的方式不會是這樣? – JCx

+2

@JCx:在C99中,可以在指針類型的函數參數中使用'restrict'關鍵字來通知編譯器它不會別名在函數範圍內的其他任何別名。 [一些C++編譯器也支持它](https://stackoverflow.com/questions/776283/what-does-the-restrict-keyword-mean-in-c#1965502),儘管它不是標準的。 –

+2

好吧 - 用叮叮進行測試。限制整理出來。尼斯:) – JCx

相關問題