2012-07-27 46 views
1

的要求是像下面:效率使用位運算符

/* length must be >= 18 */ 

int calcActualLength(int length) { 
    int remainder = (length - 18) % 8; 
    if (remainder == 0) 
     return length; 
    return length + 8 - remainder; 
} 

使用位運算符,我可以重構一號線

int remainder = (length - 2) & 7; 

是否可以進一步優化?

+5

除非你的編譯器死腦筋,否則幾乎肯定會比單純的凡人優化得更好:-) – paxdiablo 2012-07-27 08:49:35

+2

爲什麼你認爲你甚至需要「優化」這個特定的函數?你有沒有對其進行分析並確定性能問題? – 2012-07-27 08:55:27

+0

「優化」是什麼意思?如果你的意思是提高運行時間,我懷疑你正在試圖解決錯誤的問題,並且你會比編譯器開關更好地運行,而不是試圖微調函數。也就是說,如果你真的想優化這個功能,我懷疑你最好的選擇是潛入彙編代碼並在那裏優化;你可能會修剪掉一兩個多餘的操作。您可能還會發現重構對生成的彙編代碼沒有任何影響。 – 2012-07-27 09:05:01

回答

2

((length+5)&~7)+2

int calcActualLength(int length) { 
    int remainder = (length - 18) % 8; 
    if (remainder == 0) 
     return length; 
    return length + 8 - remainder; 
} 
==> 
int HELPER_calcActualLength(int length) { 
    int remainder = length % 8; 
    if (remainder == 0) 
     return length; 
    return length + 8 - remainder; 
} 
int calcActualLength(int length) { 
    return 18 + HELPER_calcActualLength(length - 18); 
} 

而且HELPER_calcActualLength()等於在語義ROUNDUP_8()當參數> = 0

而更簡單ROUNDUP_8()可以是:

#define ROUNDUP_8(x) (((x)+7)&~7) 

int calcActualLength(int length) { 
    return 18 + ROUNDUP_8(length - 18); 
} 
==> 2 + ROUNDUP_8(length - 18 + 16); 
==> 2 + ROUNDUP_8(length - 2); 
==> 2 + (((length - 2)+7)&~7) 
==> ((length+5)&~7)+2 
2

原始代碼產生在使用gcc -O3進行編譯時遵循64位彙編:

 movl %edi, %eax 
     leal -18(%rax), %ecx 
     movl %ecx, %edx 
     sarl $31, %edx 
     shrl $29, %edx 
     addl %edx, %ecx 
     andl $7, %ecx 
     subl %edx, %ecx 
     je  .L2 
     addl $8, %eax 
     subl %ecx, %eax 
.L2: 
     rep 

正如在評論你的問題建議,改變參數unsigned int允許在以下裝配更大的優化和結果:

 leal -18(%rdi), %edx 
     movl %edi, %eax 
     andl $7, %edx 
     je  .L3 
     leal 8(%rdi), %eax 
     subl %edx, %eax 
.L3: 
     rep 

四捨五入到8倍數可以通過增加執行7並用~7掩蓋。它的工作原理是這樣的:如果最後三位不全爲零,則將7加入第4位,否則不進位。所以,你的功能可以簡化爲:

return (((length - 18) + 7) & ~7) + 18; 

或簡單:

return ((length - 11) & ~7) + 18; 

GCC編譯的最後一行簡單:

 leal -11(%rdi), %eax 
     andl $-8, %eax 
     addl $18, %eax 

注意,lea(加載有效地址)instruciton經常被「濫用」,因爲它能夠計算簡單的線性組合,如reg1 + size*reg2 + offset