[英]Why no compiler appears able to optimize this code?
考慮以下C代碼(假設80位long double
)(注意,我確實知道memcmp
,這只是一個實驗):
enum { sizeOfFloat80=10 }; // NOTE: sizeof(long double) != sizeOfFloat80
_Bool sameBits1(long double x, long double y)
{
for(int i=0;i<sizeOfFloat80;++i)
if(((char*)&x)[i]!=((char*)&y)[i])
return 0;
return 1;
}
我檢查過的所有編譯器(gcc.godbolt.org上的gcc,clang和icc)都生成類似的代碼,這是gcc的示例,帶有選項-O3 -std=c11 -fomit-frame-pointer -m32
:
sameBits1:
movzx eax, BYTE PTR [esp+16]
cmp BYTE PTR [esp+4], al
jne .L11
movzx eax, BYTE PTR [esp+17]
cmp BYTE PTR [esp+5], al
jne .L11
movzx eax, BYTE PTR [esp+18]
cmp BYTE PTR [esp+6], al
jne .L11
movzx eax, BYTE PTR [esp+19]
cmp BYTE PTR [esp+7], al
jne .L11
movzx eax, BYTE PTR [esp+20]
cmp BYTE PTR [esp+8], al
jne .L11
movzx eax, BYTE PTR [esp+21]
cmp BYTE PTR [esp+9], al
jne .L11
movzx eax, BYTE PTR [esp+22]
cmp BYTE PTR [esp+10], al
jne .L11
movzx eax, BYTE PTR [esp+23]
cmp BYTE PTR [esp+11], al
jne .L11
movzx eax, BYTE PTR [esp+24]
cmp BYTE PTR [esp+12], al
jne .L11
movzx eax, BYTE PTR [esp+25]
cmp BYTE PTR [esp+13], al
sete al
ret
.L11:
xor eax, eax
ret
這看起來很丑陋,每個字節都有分支,實際上似乎沒有進行任何優化(但至少展開了循環)。 很容易看出,雖然可以將其優化為與以下代碼等效的代碼(並且通常對於較大的數據使用較大的步幅):
#include <string.h>
_Bool sameBits2(long double x, long double y)
{
long long X=0; memcpy(&X,&x,sizeof x);
long long Y=0; memcpy(&Y,&y,sizeof y);
short Xhi=0; memcpy(&Xhi,sizeof x+(char*)&x,sizeof Xhi);
short Yhi=0; memcpy(&Yhi,sizeof y+(char*)&y,sizeof Yhi);
return X==Y && Xhi==Yhi;
}
現在,此代碼可以獲得更好的編譯結果:
sameBits2:
sub esp, 20
mov edx, DWORD PTR [esp+36]
mov eax, DWORD PTR [esp+40]
xor edx, DWORD PTR [esp+24]
xor eax, DWORD PTR [esp+28]
or edx, eax
movzx eax, WORD PTR [esp+48]
sete dl
cmp WORD PTR [esp+36], ax
sete al
add esp, 20
and eax, edx
ret
所以我的問題是:為什么三個編譯器都不能進行此優化? 在C代碼中看到這非常不常見嗎?
首先,它無法進行此優化,因為您通過過度地使用過多的內存重新解釋來使代碼的含義完全混淆,從而模糊了代碼的含義。 這樣的代碼公正地使編譯器做出反應:“我不知道這是什么,但是如果這就是您想要的,那便是您將得到的”。 我為什么還不知道為什么編譯器會費心將某種類型的內存重新解釋轉換為另一種類型的內存重新解釋(!)。
其次,理論上可以做到這一點,但是它在其優先級列表中的位置可能不是很高。 請記住,代碼優化通常是通過模式匹配算法來完成的,而不是通過某種AI來完成的,這並不是它可以識別的模式之一。
在大多數情況下,您的手動嘗試執行代碼的低級優化會挫敗編譯器執行此操作的努力。 如果您想自己優化它,那么請一直進行下去。 不要指望能夠啟動然后將其交給編譯器來完成您的工作。
兩個long double
值x
和y
可以很容易地完成: x == y
。 如果要進行逐位內存比較,則可以通過在本質上知道memcmp
是什么(內置,內部函數)的編譯器中使用memcmp
來memcmp
編譯器的工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.