第一個版本通過將值從內存移到局部變量來進行優化。第二個版本沒有。爲什麼編譯器爲此循環的每次迭代都將一個成員變量寫入內存?
我在期待編譯器可能會選擇在這裏進行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以獲得完整的裝配和編譯器供您選擇!
您鏈接的示例存在另一個問題。首次使用時,「v1」未初始化。這是UB,會導致gcc和clang在優化時間做出怪異的事情。 –
哦,是的 - 公平點 - 這不是真正的代碼,雖然我只是想展示我感興趣的問題,v1的出發點並不重要。實際的代碼更精緻一點。 – JCx
有趣和警示閱讀: https://blog.regehr.org/archives/759 –