GCC strict aliasing – 嫉妒就是承认自己不如别人
GCC strict aliasing – 嫉妒就是承認自己不如別人
GCC strict aliasing事情是這樣的。我們對tair(淘寶的分布式Key/Value系統)動了一次大手術,更換了網絡框架,經過長時間的測試/調試,終于完全通過了回歸測試。但要打包發布的時候,卻發現服務器可以正常啟動,但卻完全無法接受請求。調試無果,對比打包前后程序的差異,僅在于是否使用-O2選項對程序進行編譯優化。
無頭蒼蠅一樣,Google搜索“gcc optimization problems”,找到StackOverflow上面的這個帖子,“抱著試試看的心態”,在編譯選項中加入-fno-strict-aliasing,bingo!
-fno-strict-aliasing這個選項是做什么的?aliasing又是什么?C和C++的標準給出了說明:
即是說,在strict aliasing規則下,C/C++編譯器認為,“不同類型”的指針(準確說是lvalue)一定不會引用同一個內存區域(即aliasing)。在這個規則的前提下,編譯器就可以進行相應的優化。看下面這個函數:
| 1 2 3 4 5 6 7 8 9 10 | int n; int foo(int *ptr) {n=1;*ptr=3;return n; } int main() {fprintf(stdout, "%d\n", foo(&n));return 0; } |
編譯并運行:
| 1 2 3 4 | $ cc main.c && ./a.out 3 $ cc main.c -O2 && ./a.out 3 |
一切正常,不是嗎?但如果把函數foo的參數類型改作double*,運行結果“可能”會是:
| 1 2 3 4 5 6 | $ cc main.c && ./a.out 3 $ cc main.c -O2 && ./a.out 1 $ cc main.c -O2 -fno-strict-aliasing && ./a.out 3 |
在加-O2選項的情況下程序編譯該程序,輸出竟然是1,難道*ptr=3沒有被執行嗎?不是的,*ptr=3確實是執行了的,全局變量n在函數返回時也確實已經是3了(你可以在fprintf之后打印出n值做驗證),但是foo函數中的語句return n卻被優化成了return 1。為什么呢?因為后者比前者稍了一次內存訪問。編譯器為什么做這樣的優化,為什么在ptr為int*時不做此優化?
這就涉及到strict aliasing的具體規則了。首先定義一下alias:兩個不同的變量引用了同一個對象(內存區域),那么就稱這兩個變量互為alias。下面是C99中可以互為alias的所有情況,除此之外的其他情況下,如果編譯時指定-fstrict-aliasing(-O2及以上優化時自動指定),那么就執行strict aliasing:
- a type compatible with the effective type of the object,
- a qualified version of a type compatible with the effective type of the object,
- a type that is the signed or unsigned type corresponding to the effective type of the object,
- a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
- a character type.
大致是這樣的:兩個類型兼容的變量可以互為alias,即使使用了signed/unsigned和const/volatile等修飾符;一個類型可以與另一個包含與該類型兼容的成員的struct/union類型的變量互為alias;char類型可以與任意類型互為alias。C++中可以互為alias的還可以是父類與子類。
可以使用-fno-strict-aliasing來取消strict aliasing規則,同時也就放棄了這個規則帶來的優化空間,放棄了一定的性能提升。如果你也遇到了文章開頭我遇到的問題,而且擔心-fno-strict-aliasing的性能損失,那就只能找出違反規則的代碼,調整該代碼,或者僅僅取消該代碼的strict aliasing。
基本就是這樣了,最后總結一下。GCC的aliasing與優化緊密相關,在指定-O2及以上優化級別時自動打開-fstrict-aliasing,執行strict aliasing規則以優化編譯程序。如果你的程序不遵守該規則(比如上面foo函數中出現double*ptr和n同時應用n的情況),就極有可能受到懲罰。GCC中與strict aliasing相關的選項除了-fstrict-aliasing/-fno-strict-aliasing,還有warning選項-Wstrict-aliasing=n,這個選項在你違反stict aliasing時給出警告,其中n為檢查的力度,一般設為2。
最后,如果想深入了解strict aliasing,可以參考這篇Understanding Strict Aliasing。另外,GCC的官方文檔中有和優化選項相關的描述,其中也提到了strict aliasing。
總結
以上是生活随笔為你收集整理的GCC strict aliasing – 嫉妒就是承认自己不如别人的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Entropy Broker 2.0 发
- 下一篇: oracle打补丁到11.2.0.3.4