gcc/g++編譯優化

1.  介紹
什麼是CFLAGS和CXXFLAGS
人們用環境變量CFLAGS和CXXFLAGS來告訴GNU編譯器集合(GNU Compiler Collection),即gcc,在編譯源代碼時使用哪些選項。CFLAGS用於C代碼,CXXFLAGS用於C++代碼。 
它們可以用來減少程序的調試信息數量,增加錯誤警告等級以及優化代碼的生成。GNU gcc手冊上維護着一個可用的選項及其作用的完整列表。 
如何使用?
使用CFLAGS和CXXFLAGS有兩種方法。第一,在每個程序由automake生成的Makefile中使用。 
但是,安裝Portage樹中的軟件包時並不能這麼幹,而要在/etc/make.conf中設置CFLAGS和CXXFLAGS。這樣所有的軟件包都會用你指定的選項來編譯。 
 
代碼 1.1: /etc/make.conf中的CFLAGS
 
CFLAGS="-march=athlon64 -O2 -pipe"
CXXFLAGS="${CFLAGS}"
 
如你所見,用CFLAGS中的所有選項來設置CXXFLAGS。在多數情況下這就是你想要的。你可以不用爲CXXFLAGS指定額外的選項了。 
誤解
指定CFLAGS和CXXFLAGS是使由源代碼生成的二進制文件更小更快的有效手段。不過也可能產生副作用,如二進制文件體積膨脹,執行緩慢,甚至造成編譯失敗! 
CFLAGS不是萬靈藥;它們不會自動使你的系統跑的更快或減少二進制文件佔用的磁盤空間。大把地增加標記以優化(壓榨)系統是絕對要失敗的方案。過猶不及。 
儘管網上有些人吹的很好,但實際上過激的CFLAGS和CXXFLAGS給你程序帶來的壞處遠多於好處。請記住那些標記一開始之所以出現是因爲它們只適用於特定場合的特定目的。某一個特定的CFLAG對有些代碼有用不代表它就適合用來編譯你機器上安裝的每一個軟件。 
準備好了?
現在你已經瞭解其中的一些風險了,讓我們看一些對你計算機來說健壯、安全的優化。這對你下次向Bugzilla報告問題大有好處,你會因此受到開發者的歡迎。(開發者通常會要求你用最少的CFLAGS重新編譯軟件包以觀察問題是否持續存在。記住,過激的標記會使代碼崩潰。) 
2.  優化
基礎
使用CFLAGS和CXXFLAGS的目的是生成契合你係統的代碼。如果可以的話,應該能在完美運行之外還能生成精簡而快速的代碼。但有時候魚與熊掌不可兼得,所以我們會選擇已知沒有問題的選項組合。理想來說,它們在所有的CPU構架上都有提供。我們也會在稍後提到激進的標記以便讓你知道該注意些什麼。我們不會討論到在gcc手冊中提到的每一個選項(有上百個之多),但是基本的,常用的標記都會涵蓋到。 
注意:  當你不能確定一個標記的作用時,請查閱gcc手冊的相關章節。如果還有疑問,試試Google,或者檢索gcc郵件列表。 
 
-march
第一個重要的選項是-march。這個選項告訴編譯器該爲你的處理器架構(architecture)(或arch)生成何種代碼,它告訴編譯器只爲特定類型的CPU生成代碼。不同的CPU具有不同的能力,支持不同的指令集,以及不同的執行代碼方式。-march標記指示編譯器根據你CPU的能力、特徵、指令集、怪癖等生成特定的代碼。 
即使/etc/make.conf中的CHOST變量指定了所使用的通用構架,-march還是可以用來爲特定的處理器優化程序。x86和x86-64(也包括其他的)的CPU尤其應該利用-march標記。 
你用哪種類型的CPU?用以下命令檢測: 
 
代碼 2.1: 檢測CPU信息
 
$ cat /proc/cpuinfo
 
現在來看一個-march的實際的例子。這是舊的Pentium III芯片的例子: 
 
代碼 2.2: /etc/make.conf: Pentium III
 
CFLAGS="-march=pentium3"
CXXFLAGS="${CFLAGS}"
 
這個則是64位AMD CPU的例子: 
 
代碼 2.3: /etc/make.conf: AMD64
 
CFLAGS="-march=athlon64"
CXXFLAGS="${CFLAGS}"
 
-mtune和-mcpu標記也是可用的。這兩個標記通常只在沒有-march選項的時候纔用到;特定的處理器可能要求使用-mtune甚至-mcpu。糟糕的是,gcc在不同構架上的表現並非完全一致。 
對於x86和x86-64的CPU,-march將使用所指定CPU的全部可用指令集和正確的ABI來生成代碼;並不會向後兼容其他舊的或者不同的CPU。如果你只需要在你當前運行Gentoo的機器上執行代碼,那麼就可以繼續使用-march。只有在爲i386和i486之類的舊CPU生成代碼時,才需要考慮使用-mtune。使用-mtune生成代碼比使用-march更通用;雖然它能爲特定的CPU優化代碼,但是並不會使用特有的指令集和ABI。別在x86或x86-64系統上使用-mcpu,因爲在這兩個構架上已經廢棄了。 
只有非x86/x86-64 CPU(如Sparc、Alpha和PowerPC)可能要求使用-mtune或-mcpu來代替-march。在這些構架上,-mtune/-mcpu的行爲有時就像(x86/x86-64上的)-march,只是換了個名稱。gcc在諸構架上的行爲和標記的命名並不一致。所以請查閱gcc手冊來確認在你的系統上該用哪個。 
注意:  要獲得更多-march/-mtune/-mcpu設置的建議,請閱讀你的構架對應的Gentoo安裝手冊。還有就是gcc手冊上的架構特有選項列表,其中更加詳細地解釋了-march、-mcpu和-mtune之間的區別。 
 
-O
接下來是-O變量。這個選項控制所有的優化等級。使用優化選項會使編譯過程耗費更多的時間,並且佔用更多的內存,尤其是在提高優化等級的時候。 
-O設置一共有五種:-O0、-O1、-O2、-O3和-Os。你只能在/etc/make.conf裏面設置其中的一種。 
除了-O0以外,每一個-O設置都會多啓用幾個選項,請查閱gcc手冊的優化選項章節,以便了解每個-O等級啓用了哪些選項及它們有何作用。 
讓我們來逐一考察各個優化等級: 
•-O0:這個等級(字母“O”後面跟個零)關閉所有優化選項,也是CFLAGS或CXXFLAGS中沒有設置-O等級時的默認等級。這樣就不會優化代碼,這通常不是我們想要的。 
•-O1:這是最基本的優化等級。編譯器會在不花費太多編譯時間的同時試圖生成更快更小的代碼。這些優化是非常基礎的,但一般這些任務肯定能順利完成。 
•-O2:-O1的進階。這是推薦的優化等級,除非你有特殊的需求。-O2會比-O1啓用多一些標記。設置了-O2後,編譯器會試圖提高代碼性能而不會增大體積和大量佔用的編譯時間。 
•-O3:這是最高最危險的優化等級。用這個選項會延長編譯代碼的時間,並且在使用gcc4.x的系統裏不應全局啓用。自從3.x版本以來gcc的行爲已經有了極大地改變。在3.x,-O3生成的代碼也只是比-O2快一點點而已,而gcc4.x中還未必更快。用-O3來編譯所有的軟件包將產生更大體積更耗內存的二進制文件,大大增加編譯失敗的機會或不可預知的程序行爲(包括錯誤)。這樣做將得不償失,記住過猶不及。在gcc 4.x.中使用-O3是不推薦的。 
•-Os:這個等級用來優化代碼尺寸。其中啓用了-O2中不會增加磁盤空間佔用的代碼生成選項。這對於磁盤空間極其緊張或者CPU緩存較小的機器非常有用。但也可能產生些許問題,因此軟件樹中的大部分ebuild都過濾掉這個等級的優化。使用-Os是不推薦的。 
正如前面所提到的,-O2是推薦的優化等級。如果編譯軟件出現錯誤,請先檢查是否啓用了-O3。再試試把CFLAGS和CXXFLAGS倒回到較低的等級,如-O1甚或-O0 -g2 -ggdb(用來報告錯誤和檢查可能存在的問題),再重新編譯。 
-pipe
-pipe是個安全而有趣的標記。它對代碼生成毫無影響,但是可以加快編譯過程。此標記指示編譯器在不同編譯時期使用pipe而不是臨時文件。 
-fomit-frame-pointer
這是個用來削減代碼尺寸的常用標記。在所有不影響除錯(例如x86-64)的構架上,所有的-O等級(除了-O0)中都啓用了它,但是也可能需要手動添加到你的標記中。儘管GNU gcc手冊沒有明確指出-O會啓用這個標記的所有構架,你需要在x86上手動啓用它。使用這個標記會使除錯難以進行。 
特別的,它會使排查Java程序的故障變得困難,儘管Java代碼並不是唯一受此選項影響的代碼。所以此標記雖然有用,但也會使除錯變得困難,特別是backtrace將變得毫無用處。然而,你不準備做軟件除錯並且沒有在CFLAGS中加入-ggdb之類與除錯相關的標記的話,那就可以試試-fomit-frame-pointer。 
重要:  請勿聯合使用-fomit-frame-pointer和與之相似的-momit-leaf-frame-pointer。開啓後者並無多大用處,因爲-fomit-frame-pointer已經把事情搞定了。此外,-momit-leaf-frame-pointer還將降低代碼性能。 
 
-msse, -msse2, -msse3, -mmmx, -m3dnow
這些標記啓用了x86和x86-64構架的SSE、SSE2、SSE3、MMX和3DNow!指令集。他們主要用於多媒體,遊戲,及其他浮點運算密集的任務,雖然也包括了一些其他的數學增強指令。比較新的CPU都具有這些指令。 
重要:  運行cat /proc/cpuinfo以確認你的CPU是否支持這些指令集。輸出會包括所有支持的指令集。注意pni是SSE3的別名。 
 
只要正確設置了-march就不需要添加額外的標記(例如,-march=nocona暗含了-msse3)。要注意的是一些新的VIA和AMD64 CPU包含了-march並不隱含的指令集(例如SSE3)。對於這些CPU就需要在檢查cat /proc/cpuinfo輸出之後手動加入適當的標記。 
注意:  請查閱x86和x86-64特定標記列表以便確認適當的CPU類型標記啓用了哪些指令集。如果某指令集已經列出了,就會由對應的-march設定開啓,不再需要手動加入了。 
 
3.  優化的常見問題
我通過-funroll-loops -fomg-optimize獲得了更好的性能!
並非如此,這樣認爲只是因爲你深信標記越多越好而已。全局的使用激進標記對應用軟件無益。即使gcc手冊也提到使用-funroll-loops和-funroll-all-loops會使代碼膨脹運行減慢。但是由於某些原因,這兩個標記以及-ffast-math、-fforce-mem、-fforce-addr和類似的標記,還在那些想獲得最多吹牛資本的玩家中流行着。 
事實上那些都是危險的過激標誌。好好逛逛Gentoo論壇和Bugzilla看看這些標記有什麼好的:一點也不好! 
你無需在全局範圍的CFLAGS或CXXFLAGS中使用那些標記。他們只會影響性能,就好似迫使一個高性能系統運行在崩潰的邊緣。這些標記除了使代碼膨脹和得到非法或無法解決bug之外並無他用。 
你不需要這些危險的標記。切勿使用。謹遵基本的:-march,-O和-pipe。 
比3還高的-O等級又怎樣?
有些用戶吹噓說使用-O4,-O9等可以獲得更高的性能,但事實上比3更高的-O是無效的。就算編譯器可以接受像-O4之類的CFLAGS,編譯時也會忽略掉。最高只對-O3進行優化。 
需要更多證據?看看gcc源代碼: 
 
代碼 3.1: -O源代碼
 
if (optimize >= 3)
    {
      flag_inline_functions = 1;
      flag_unswitch_loops = 1;
      flag_gcse_after_reload = 1;
      /* Allow even more virtual operators.  */
      set_param_value ("max-aliased-vops", 1000);
      set_param_value ("avg-aliased-vops", 3);
    }
 
如你所見,任何大於3的值都只被當成-O3。 
什麼是冗餘標記?
有些人在有些選項已經被-O等級啓用的情況下仍多餘地把它們加入/etc/make.conf中的CFLAGS和CXXFLAGS。有時候人們這樣做是出於無知,但有時候這樣做是爲了防止標記被過濾或替換。 
在Portage樹中不少ebuild都會過濾或替換標記。這麼做通常是因爲某些軟件包在特定的-O等級下無法編譯,或者源代碼對附加的標記過於敏感。ebuild可能過濾部分或全部CFLAGS和CXXFLAGS,或者將-O替換爲其他等級。 
Gentoo開發者手冊概述了在何處過濾替換及它如何起作用。 
爲一個特定的等級追加該等級已經啓用的標記可以規避-O過濾,比如-O3,可以這樣做: 
 
代碼 3.2: 指定額外的CFLAGS
 
CFLAGS="-O3 -finline-functions -funswitch-loops"
 
然而,這樣做可不好。過濾CFLAGS必有其原因。如果標記被過濾,那就意味着用那些標記來編譯某個軟件包是不安全的。毫無疑問,用-O3編譯整個系統是不安全的,如果開啓了這個等級中的某些標記,對於特定軟件包將產生問題。別以爲你比那些包的維護者更聰明。請相信開發者。過濾和替換標記是爲了你好!如果ebuild指定了其他的標記,就別再動它了。 
如果你用不被接受的標記來構建軟件包,你很可能會不斷地遇到問題。當你到Bugzilla報告問題時,你在/etc/make.conf中使用的標記將會被其他人看到,你將會被告知要去掉那些標記再重新編譯。要想避免重新編譯的麻煩,那就請從一開始就不要使用多餘的標記。別以爲你比開發者更懂。 
什麼是LDFLAGS?
在基本的profile裏Gentoo開發者已經設置了基本的,安全的LDFLAGS,所以沒必要去改動。 
可以爲單個包分別指定標記嗎?
爲單個包指定CFLAGS或者其他變量是不支持的。不過這裏有一個有濫用之嫌的手段可以強制Portage這麼做。 
你不應強制Portage使用單個包的指定標記,因爲這不受任何支持還很可能產生錯綜複雜的問題。只要在/etc/make.conf設置系統範圍的標記就可以了。 
4.  資源
以下資源對優化的深入理解有所幫助: 
•GNU gcc手冊 
•Gentoo安裝手冊的第五章 
•man make.conf
•Wikipedia
•Acovea,是個用來評定不同的編譯器標記對於生成代碼影響的測評套件,雖然它給出的代碼建議不宜在系統範圍使用。可以在Portage裏通過emerge acovea安裝。 

•Gentoo論壇


轉自:http://blog.csdn.net/attagain/article/details/18655485

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章