淺談編譯器優化
回顧
在“圖靈社群:檢測 2 的冪”中,我給出了以下函式:
int isPowerOf2a(long x) { if (x <= 0) return 0; while (x % 2 == 0) x /= 2; return x == 1; }
在二進位制計算機上,以上對 2 取模和除以 2 的運算可以使用按位與和向右移位運算代替:
while ((x & 1) == 0) x >>= 1;
在 Arch Linux 64-bit 作業系統中使用 clang 編譯器進行優化編譯後,得到以下結果:
$ clang -O isPowerOf2.c && ./a.out
isPowerOf2a: 49.7 seconds, 34 isPowerOf2b: 41.5 seconds, 34 isPowerOf2c: 29.7 seconds, 34 isPowerOf2d: 32.7 seconds, 34
反彙編
使用 objdump -d a.out 進行反彙編,經整理後得到:
可以看出,在 isPowerOf2a 函式中,clang 編譯器優化x % 2
為x & 1
,避免了除法運算。但是,x /= 2
被優化為以下 4 條指令(在此之前,已經執行了z = x
):
z >>>= 63;
// z = (x < 0) ? 1 : 0;z += x;
// z = (x < 0) ? (x+1) : x;z >>= 1;
// z /= 2;x = z;
這是為了處理負整數的情形。負整數用二進位制補碼錶示時,算術右移 1 位的效果,相當於除以 2 時向負無窮大截斷。而在主流的 C 編譯器中,整數除法是向 0 截斷的。
如果能夠保證x
是非負整數,x /= 2
可以直接優化為x >>= 1
。實際上,因為前面有:
if (x <= 0) return 0;
執行到這裡時,x
必定是非負整數,但是 clang 編譯器的優化功能還沒有達到這個水平。
使用無符號整數
把 isPowerOf2.c 中的所有 long 修改為 unsinged long 後,再次編譯和執行,得到:
$ clang -O isPowerOf2u.c && ./a.out
isPowerOf2a: 38.1 seconds, 34
isPowerOf2b: 38.9 seconds, 34
isPowerOf2c: 25.0 seconds, 34
isPowerOf2d: 32.9 seconds, 34
使用 objdump -d a.out 進行反彙編,整理後得到:
0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: |
0000000000400520 <isPowerOf2a>: 400520: xor %eax,%eax 400522: test %rdi,%rdi 400525: je 400544 400530: mov %rdi,%rax 400533: shr %rdi 400536: test $0x1,%al 400538: je 400530 40053a: cmp $0x1,%rax 40053e: sete %al 400541: movzbl %al,%eax 400544: retq |
0000000000400550 <isPowerOf2b>: 400550: xor %eax,%eax 400552: test %rdi,%rdi 400555: je 400574 400560: mov %rdi,%rax 400563: shr %rdi 400566: test $0x1,%al 400568: je 400560 40056a: cmp $0x1,%rax 40056e: sete %al 400571: movzbl %al,%eax 400574: retq |
可見,對於非負整數,clang 編譯器能夠很好地優化x /= 2
為x >>= 1
。與上一小節的使用有符號整數的 isPowerOf2b 函式對比:
- 第 3 行:
jle
指令改為je
指令,即x <= 0
改為x == 0
。 - 第 5 行:
sar
指令改為shr
指令,即算術右移
改為邏輯右移
。
奇怪的是,isPowerOf2a 居然比 isPowerOf2b 更快,這沒道理啊,兩者的組合語言程式碼是一樣的。
相關文章
- 淺談彙編器、編譯器和直譯器編譯
- 淺談交叉編譯編譯
- C++編譯器優化C++編譯優化
- 淺談optimizer_mode優化器模式優化模式
- 淺談 TCP 優化TCP優化
- 淺談webpack優化Web優化
- 淺談程式優化優化
- 編譯器優化:方法內聯編譯優化
- 淺談Tomcat伺服器優化方法Tomcat伺服器優化
- V8 之旅:優化編譯器優化編譯
- 淺談優化if...else優化
- 淺談 Web 影象優化Web優化
- 【MySQL】淺談MySQL優化MySql優化
- JVM編譯優化JVM編譯優化
- webpack 編譯優化Web編譯優化
- 淺談小程式效能優化優化
- 效能優化,實踐淺談優化
- 淺談優化程式效能(下)優化
- Linux 下如何繞過編譯器優化Linux編譯優化
- Android 編譯優化Android編譯優化
- 優化 Swift 編譯速度優化Swift編譯
- 淺談mysql配置優化和sql語句優化MySql優化
- 淺談查詢優化器中的JOIN演算法優化演算法
- 淺談webpack4.0 效能優化Web優化
- 淺談高併發-前端優化前端優化
- 淺談JavaScript程式碼效能優化JavaScript優化
- 3個方面淺談程式優化優化
- [譯] 優化 Swift 的編譯時間優化Swift編譯
- Vue 原始碼解讀(9)—— 編譯器 之 優化Vue原始碼編譯優化
- VS編譯器優化誘發一個的Bug編譯優化
- 探索c#之尾遞迴編譯器優化C#遞迴編譯優化
- 淺談Android的資源編譯過程Android編譯
- 後端編譯與優化後端編譯優化
- maven-編譯速度優化Maven編譯優化
- 淺談Android記憶體優化Android記憶體優化
- 淺談前端優化的幾個思路前端優化
- Vue.js 程式碼優化淺談Vue.js優化
- 淺談JavaScript程式碼效能優化2JavaScript優化