看板 NTUGIEE_EDA 關於我們 聯絡資訊
Optimizing Code with GCC (感謝aknow提供:http://blog.csdn.net/daidodo/archive/2008/03/15/2185222.aspx ) 現在的編譯器越來越聰明,功能越來越強,從簡單的函數內聯,到複雜的寄存器分析,一 系列代碼革命使程序運行得越來越快。大多數時候,更快比更小重要,因為磁盤空間和內 存都變得便宜了。但是在嵌入式系統裡,更小和更快是一樣重要的,所以把代碼進行優化 是非常有意義的工作。 如果你已經知道了怎樣用gcc編譯你的代碼,現在是時候讓你的代碼更快或者更小了,這 也是本章的內容。如果學有所成的話,你甚至可以讓你的下一個程序既快又小。首先我們 快速瀏覽一下編譯器優化理論,然後討論GCC的代碼優化命令行選項,從一般的、體系結 構無關的優化,到體系結構相關的優化方法。 雖然本章的示例代碼都是C語言的,但是優化選項是通用的、語言無關的。能把一些優化 選項適用到所有語言的編譯器上,是一個編譯器家族最大的優勢,比如GCC編譯器家族就 是這樣。 OPTIMIZATION AND DEBUGGING 沒有代碼優化的時候,GCC的一個重要目標是盡量縮短編譯時間,並保證產生的代碼在調 試環境下的行為正確。比如,在優化過的代碼裡,一個變量如果在循環裡多次計算,但是 值其實沒有變化,那麼編譯器可以把它移到循環的外面,只計算一次。雖然這是可行了( 當然,只要不改變程序的運行結果),但是這使你無法按照源代碼進行調試,因為計算該 變量的代碼被優化掉了。如果沒有優化,你就可以正確的進行調試,檢查變量的值。這就 是所謂的「代碼在調試環境下的行為正確」。 優化能改變代碼的執行流程,但不改變執行結果。所以,優化一般是編碼並調試完成之後 才進行的。其實優化過的代碼也是可以進行調試的,只是需要一些技巧。 編譯器優化理論概覽 代碼優化是指分析一段編譯後的代碼,然後決定如何改變它,使它運行的更快,消耗的資 源更少。擁有此功能的編譯器叫做優化編譯器(optimizing compilers),最後的輸出代 碼叫做優化代碼(optimized code)。 優化編譯器使用幾種辦法來決定哪些代碼可被優化。一種是控制流分析(control flow analysis),即檢查循環和其他控制語句,比如if-then和case,找出程序可能的執行路 徑,然後決定哪些執行路徑可以被改進。另一個典型的優化技術是檢查數據是怎樣使用的 ,即數據流分析(data flow analysis)。此法檢查變量在哪裡是怎樣使用的(或沒被使 用),然後應用一系列的方程式到這些使用模式上面,從而找到優化的途徑。 除了本章所述的一些計算機所作的改進外,優化還包括了對程序所使用的算法的改進。典 型的比如冒泡排序算法改進成快速排序或希爾排序。這類改進能使程序的性能有本質的提 高,比計算機能做的優化強得多,所以優化既是CPU的事情,也是人的事情。 本節定義一個基本塊(basic block)指,只有一個入口和出口,其他地方不包括終止、 分支語句的連續代碼段。在一個基本塊內進行的轉化稱為局部轉化(local transformations),同樣的不是在一個基本塊內的轉化稱為全局轉化(global transformations)。通常編譯器會進行許多全局或局部的轉化,不過局部轉化總是先做 。 雖然本節的例子使用C語言,其實所有GCC編譯器都使用一種中間語言進行這種轉化,這種 中間語言比各種編程語言更適合計算機處理。GCC在產生最終的2進制代碼前,會使用一系 列不同的中間語言翻譯你的源程序。不過對人類來說,C和其他的高級語言比這些中間語 言更好理解。 GCC編譯器還做了很多其他的優化,有些非常細緻,甚至需要專業的編譯器理論知識。這 裡列出的優化方法只是基礎,並且能用命令行來進行選擇。 注意 我所知的最經典的編譯器著作,當屬「龍書」《Compilers: Principles, Techniques, and Tools》,因其封面有一個恐龍而得名(Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman,Addison Wesley Longman, 1986. ISBN: 0-201-10088-6)。書裡的優化理論 介紹比本文詳細的多,並且曾是我的啟蒙書籍。 Code Motion Code motion是一種優化技巧,是指在Common subexpression elimination(後有詳述) 時去掉多餘的代碼。Code motion並不是去掉所有的subexpression,而是在中間語言形式 下改變它們的位置,以便能減少它們出現的次數。比如,在嵌套的循環或其他控制結構裡 ,中間變量的計算次數可能不是最優的,要優化這些程序,編譯器把這些計算語句移到循 環更少的地方,並且保證計算結果是一樣的。把計算移出循環的方法我們稱為 loop-invariant code motion。Code motion還用在另一種Common subexpression elimination裡,叫做partial redundancy elimination。 Common Subexpression Elimination 去除多餘的計算是一種標準的優化手段,因為它能減少程序的指令數,並得到相同的結果 。比如,如果一個表達式的參數(所引用的變量)值不變,那麼就可以只計算一次結果, 在以後引用該表達式的地方用結果值替代就可以了。這些後來引用該表達式的地方就叫做 common subexpressions。比如下例: Listing 5-1. An Example of a Common Subexpression #define STEP 3 #define SIZE 100 int i, j, k; int p[SIZE]; for (i = 0; i < SIZE; ++i) { j = 2 * STEP; k = p[i] * j; } for循環內的表達式j = 2 * STEP就是一個common subexpression,因為它的值可以在進 入循環之前計算,而且它的參數變量STEP(實際上是一個宏定義)從不改變。Common subexpression elimination (CSE)把for循環內的重複計算去掉,成為如下形式: j = 2 * STEP; for (i = 0; i < 100; ++i) { k = p[i] * j; } 雖然這個例子簡單了點,不過能很好的說明問題,CSE去掉了100次對j的計算。CSE能去掉 不必要的計算,改善程序性能,並減少了最終文件的大小。 Constant Folding Constant folding是指去掉在編譯時就能確定的數值計算表達式。這些表達式必須只包含 常數值,或者值為常數的變量。比如,下面的計算表達式都可以用一個賦值語句來替換: n = 10 * 20 * 400; i = 10; j = 20; ij = i * j; 後面的例子裡,如果i和j在後續的程序裡沒有用到的話,完全可以去除它們的定義。 Copy Propagation Transformations 這是另一種減少或去除多餘計算的方法,即去除那些只是為了傳遞數值的變量複製操作。 看下面的代碼: i = 10; x[j] = i; y[MIN] = b; b = i; Copy propagation transformation可能會優化成下面的代碼: i = 10; x[j] = 10; y[MIN] = b; b = 10; 在本例裡,copy propagation允許把右值變成常數,這樣比搜索和複製變量的值要快得多 ,並且也能去掉變量給自己賦值的情況。有些情況下,copy propagation並不直接產生優 化,但是能簡化代碼,方便其他優化,比如code motion和code elimination。 Constant propagation是一種把變量替換成常量的copy propagation transformation優 化方法。Copy propagation主要指去掉不必要的變量間的相互複製,而Constant propagation則指去掉不必要的預定義值到變量的複製。 Dead Code Elimination DCE是指把那些實際上無用的或多餘的代碼去掉。你可能想問「為什麼我會寫這樣的代碼 呢?」,其實在一個很大的、延續時間很長的項目裡,這是很容易發生的。許多DCE都是 在中間語言表示的形式下進行的,因為它是源代碼更標準的翻譯,更容易發現不必要的中 間計算。 Unreachable code elimination是指去除編譯器確定不可能到達的代碼。比如下面的代碼 塊: if ( i == 10 ) { . . . } else { . . . if ( i == 10) { . . . } } 第2次對i是否等於10的測試及處理代碼可以去掉,因為它是不可能到達的。UCE也是在中 間代碼形式裡進行的。 If-Conversion 即分支重構,比如把大的if-then-elseif-else結構,重構成多個if語句,這樣能簡化代 碼,為以後的優化提供方便,並去除某些無用的跳轉和分支語句。 Inlining 即把複雜結構或函數調用替換成內聯的代碼以改善性能。Code inlining或loop unrolling都是指把全部或部分循環展開成一系列的直接指令。Function inlining是指用 函數執行的指令替代對函數的調用。一般情況下,inlining能減少代碼複雜度,提高性能 ,因為不需要多餘的分支跳轉。它還能給common subexpression elimination和code motion提供優化機會。這方面最經典的例子是Duff』s Device,詳見 http://en.wikipedia.org/wiki/Duff's_device。 GCC Optimization Basics GCC處理源代碼時,會把它轉化成一種中間形式。這樣做有幾大好處:、 l 把源代碼變得簡單、低級,使優化點暴露出來; l 使可能很複雜的結構更容易生成簡單易讀的語法分析樹; l 使用統一的中間形式使GCC編譯器之間能通用優化策略。 傳統上GCC使用的內部中間形式叫做Register Transfer Language (RTL),這是一種很低 級的語言,GCC把任何代碼(無論什麼級別)轉化成目標代碼之前都先翻譯成這種代碼。 對於像GCC的RTL這種非常低級的語言進行的優化也是很「低級」的,比如寄存器分配、堆 棧和數據優化等。因為它很低級,所以不會像你想像的那樣能進行數據類型、數組和變量 引用、控制流改變等「高級」的優化。 GCC 4.0的作者們發明了一種新的中間形式static single assignment (SSA),通過對GCC 編譯器產生的語法分析樹進行操作而得到,因此得名Tree SSA。4.0及更高的GCC編譯器在 生成Tree SSA之前還有2種中間形式,叫做GENERIC和GIMPLE。GENERIC是通過去除源代碼 中語言相關的結構得到的中間形式,GIMPLE則是把GENERIC只讀地址引用進行簡化得到。 也許你也看出來了,在到達RTL等級之前,有許多的優化已經在這些相對高級點的層面上 先做了。 關於Tree SSA的詳細信息和優化處理的步驟有許多資料可參考,其中一個是2003年的GCC 開發者總結,網址為 http://www.linux.org.uk/~ajh/gcc/gccsummit-2003-proceedings.pdf。 What』s New in GCC 4.x Optimization GCC 4.x家族最重要的變化是引入了中間形式Tree SSA,它提供了更多的優化空間,和更 多的參數選項,包括-ftree-ccp, -ftree-ch, -ftree-copyrename, -ftree-dce, -ftree-dominator-opts, -ftree-dse, -ftree-fre, -ftree-loop-im, -ftree-loop-ivcanon, -ftree-loop-linear, -ftree-loop-optimize, -ftree-lrs, -ftree-pre, -ftree-sra, -ftree-ter, and -ftree-vectorize,本章稍候會敘述。由於 有了這些重大的改變,原來的通用優化等級-O1、-O2、-O3和-Os都有了變化。除此以外, 任何語言的GCC編譯器的優化都更普遍了。 同時由於有IBM的大力支持,GCC 4改進了向量化。向量化發現同一操作應用到多個數據的 代碼,並改善其性能。GCC 4可以把16個標量操作合成為一個向量操作。這個優化方法可 以在遊戲、視頻和多媒體應用裡大展身手,因為這些程序的指令都是對數組向量的重複操 作。 GCC 4還改進了數組邊界檢查和棧的內容結構檢查,保護程序免遭流行的緩衝區和棧溢出 攻擊。 Architecture-Independent Optimizations GCC的優化分為2大類:體系結構無關和體系結構相關。本節介紹體系結構無關的優化,包 括計算機體系無關,比如x86;處理器類型無關,比如IA-32處理器;和處理器家族無關, 例如Pentium IV (Xeon)。 GCC的優化選項有-O;-On,參數n是介於0到3之間的整數;或者-Os。-O0關閉優化。-O和 -O1(又叫作第1級優化)等價,允許編譯器在不大量增加編譯時間的前提下減少代碼量和 執行時間。-O2和-O3比-O1的優化等級更高,-Os會最小化代碼量。 本節所有的表格顯示了GCC提供的各種優化選項,如果要關閉相應優化,只需在-f和優化 選項名字之間加上no-就行了。比如,要禁止deferred stack pops優化,命令行可以這樣 寫: $ gcc myprog.c -o myprog -O1 -fno-defer-pop 注意 -f表示一個機器無關的操作標誌,即應用一個(大多數情況下)體系結構無關的優化操作 。這些標誌選項更改了GCC的默認行為,但是不需要硬件的特殊支持。通常你可以指定多 個標誌。 Level 1 GCC Optimizations 下表列出了-O或-O1時進行的默認優化選項: Optimization Description -fcprop-registers 試圖減少寄存器複製操作的次數 -fdefer-pop Accumulates function arguments on the stack. -fdelayed-branch Utilizes instruction slots available after delayed branch instructions. -fguess-branch-probability 利用隨機預測器猜測分支的可達性 -fif-conversion 把有條件跳轉變成非分支語句 -fif-conversion2 利用條件執行(要求CPU支持)進行if-conversion優化 -floop-optimize 應用幾個針對循環的優化 -fmerge-constants 合併多個模塊中相等的常量 -fomit-frame-pointer 省略函數楨指針的存儲。只能在不影響調試的系統裡激活 -ftree-ccp 在SSA Trees上進行較少的conditional constant propagation(CCP)優化(只限GCC 4.x) -ftree-ch 在SSA Trees上執行loop header copying,即去掉一個跳轉指令,並提供code motion優 化的機會(只限GCC 4.x) -ftree-copyrename 在SSA Trees上執行copy renaming,即在複製位置把內部變量的名字改得更接近原始變 量的名字(只限GCC 4.x) -ftree-dce 在SSA Trees上執行dead code elimination (DCE)優化(只限GCC 4.x) -ftree-dominator-opts 利用支配樹(dominator tree)遍歷來進行一系列優化。A dominator tree is a tree where each node』s children are the nodes that it immediately dominates。這些 優化包括constant/copy propagation,redundancy elimination,range propagation, expression simplification和jump threading(減少跳轉語句)(只限GCC 4.x) -ftree-dse 在SSA Trees上執行dead store elimination (DSE) (只限GCC 4.x) -ftree-fre 在SSA Trees上執行full redundancy elimination (FRE),即認為全路徑計算的表達式 會導致冗余編譯。這和partial redundancy elimination(PRE)相似,不過比它快,找到 的冗余也比較少。(只限GCC 4.x) -ftree-lrs 在SSA Trees轉化成RTL前,轉化成一般形式,並執行live range splitting。這種方法 明確了變量的生存期,為後續的優化提供幫助(只限GCC 4.x) -ftree-sra 把聚合體替換成標量,即把對結構體的引用替換成標量數值,避免在不必要的時候把結 構體提交到內存裡(只限GCC 4.x) -ftree-ter 在SSA Trees轉化成RTL前,轉化成一般形式,並執行temporary expression replacement (TER)。把只使用一次的臨時表達式替換成原始定義的表達式,這樣更容易 產生RTL代碼,並使產生的RTL代碼有更多的優化機會。(只限GCC 4.x) 第1級優化揉合了代碼大小和速度改進2種優化措施。比如,-tree-dce去掉了無用代碼, 於是減少了代碼量;跳轉指令減少使整個程序的棧使用量減少;而-fcprop-registers是 性能優化,減少在寄存器間複製數據的次數。 -fdelayed-branch和-fguess-branch-probability是指令調度改進。如果底層CPU支持指 令調度,這些優化標誌就試圖使CPU等待下一條指令的等待時間最小化。 -floop-optimize開啟了對循環的優化,包括把常數表達式移出循環和簡化推出循環的條 件測試。在更高的第2級優化裡,該標誌還執行strength reduction和循環展開。 -fomit-frame-pointer是非常有用,原因有2個:省下了設置、保存和恢復楨指針的代碼 ;有時候省下了一個CPU寄存器,可有它用。而負面影響是:沒有了楨指針,調試(比如 棧跟蹤,尤其是嵌套很深的函數)變得很難甚至不可能。 -O2優化(第2級優化)包括了第1級的所有優化加上下表列出的另一些優化。應用這些優 化將延長編譯時間,不過你的程序性能將得到顯著的提高。 Level 2 GCC Optimizations 當使用-O2優化選項時,下表的優化將默認進行: Optimization Description -falign-functions 把函數對齊到2的指數字節邊界 -falign-jumps 把跳轉指令對齊到2的指數字節邊界 -falign-labels 把標籤對齊到2的指數字節邊界 -falign-loops 把循環對齊到2的指數字節邊界 -fcaller-saves 保存並恢復被函數調用改寫的寄存器 -fcrossjumping 分解等價代碼來減少代碼量 -fcse-follow-jumps CSE過程中跳過不會到達的目標 -fcse-skip-blocks CSE過程中可以跳過條件塊 -fdelete-null-pointer-checks 去掉不必要的null指針檢查 -fexpensive-optimizations 執行一些「較昂貴」的優化 -fforce-mem 在寄存器裡保存內存操作數(只限GCC 4.1) -fgcse 執行一遍全局CSE(Common Subexpression Elimination) -fgcse-lm 在全局CSE時把裝載指令移到循環外面 -fgcse-sm 在全局CSE時把保存治療移到循環外面 -foptimize-sibling-calls 優化有副作用的或尾遞歸的函數調用 -fpeephole2 執行機器相關的深度優化 -fregmove Reassigns register numbers for maximum register tying -freorder-blocks 重新安排函數的基本塊,以便減少分支和提高代碼局部性 -freorder-functions 對於經常調用或極少調用的函數,使用特殊的text段重新安排函數的基本塊,以提高代 碼局部性 -frerun-cse-after-loop 在循環優化之後執行一遍CSE -frerun-loop-opt 執行2此循環優化 -fsched-interblock 在基本塊間調度指令 -fsched-spec Schedules speculative execution of nonload instructions -fschedule-insns 重新安排指令以最小化執行延遲 -fschedule-insns2 執行第2次schedule-insns -fstrength-reduce 用「廉價」的指令代替「昂貴」的指令 -fstrict-aliasing 通知編譯器使用最嚴格的別名規則(aliasing rules) -fthread-jumps 試圖重新安排跳轉指令的順序,成為執行的順序 -ftree-pre 在SSA Trees上執行partial redundancy elimination (PRE) -funit-at-a-time 在開始代碼生成之前對整個文件進行語法分析,以便進行額外的優化,比如重新安排代 碼和申明,去掉從不引用的靜態變量和函數等。 -fweb 把每個web(代碼的存活範圍)賦給它自己的偽寄存器,方便後續的優化,例如CSE, dead code elimination和循環優化。 4個-falign-選項強制函數、跳轉指令、標籤和循環對齊到2的指數邊界,原理是內存對齊 的數據和結構對計算機有更高的訪問速度。前提是對齊後的代碼被頻繁調用,能彌補因對 齊造成的no-op指令的延遲。 -fcse-follow-jumps和-fcse-skip-blocks正如其名,是在前面介紹的CSE過程中執行的優 化。使用-fcse-follow-jumps,CSE會跳過不可到達的目標代碼。比如,下面的條件代碼 : if (i < 10) { foo(); } else { bar(); } 通常,即使(i < 10)測試為false,CSE仍要按照全路徑對foo()進行優化。如果你指定了 -fcse-follow-jumps,CSE就直接跳到else塊進行優化(bar())。 -fcse-skip-blocks使CSE可以跳過條件塊。比如你寫了如下的if語句: if (i >= 0) { j = foo(i); } bar(j); 如果你指定了-fcse-skip-blocks而且i是負值,那麼CSE將直接跳到bar(),越過了原來的 if語句。而通常情況下,無論i是什麼值,CSE都需要對if語句進行處理。 -fpeephole2執行CPU相關的深度優化,把較長的指令集替換成較短的、簡練的指令。比如 下面的代碼: a = 2; for (i = 1; i < 10; ++i) a += 2; GCC可能把整個循環替換成賦值語句a=20。使用了-fpeephole2,GCC就在標準的深度優化 (比如C語言裡用位操作代替算術操作)之外還進行CPU相關的優化。 -fforce-mem是指在對指針進行運算前,把內存操作數和常量複製到寄存器裡,目的是生 成內存引用的common subexpressions,然後用CSE進行優化。前面已經講過,CSE能去除 多餘的寄存器裝載指令。 -foptimize-sibling-calls試圖優化掉尾遞歸的或同屬調用(sibling call)的函數。尾 遞歸調用是指函數的遞歸調用出現在最後面。比如下面的代碼: int inc(int i) { printf("%d\n" i); if(i < 10) inc(i + 1); } 上面定義的inc()函數,在函數體最後遞歸調用了以i+1為參數的自身,直到i大於等於10 。既然尾遞歸調用的深度是已知的,那麼就可以用一個迭代來消除尾遞歸。 -foptimize-sibling-calls就試圖進行這種優化。同屬調用(sibling call)也是指函數 調用出現在尾上下文(tail context,比如return語句)中。 GCC Optimizations for Code Size 選項-Os變得越來越流行,因為它包含了第2級優化裡除增加代碼量以外的所有優化。-Os 還應用了一些減少代碼量的額外優化。代碼量在這裡不是指程序文件在磁盤裡佔用的存儲 空間,而是指程序運行時佔用的內存空間。注意,-Os會自動屏蔽下面的優化選項: ‧ -falign-functions ‧ -falign-jumps ‧ -falign-labels ‧ -falign-loops ‧ -fprefetch-loop-arrays ‧ -freorder-blocks ‧ -freorder-blocks-and-partition ‧ -ftree-ch 分別使用-O2和-Os編譯程序,然後對比它們的性能和內存用量是很有意義的。比如,我發 現最新的Linux內核下使用-O2和-Os編譯的程序擁有幾乎相同的運行時性能,但是後者的 運行時內存用量卻少了15%。當然,你的環境下可能有不同的發現。 Level 3 GCC Optimizations 指定-O3優化選項除了包括第1級,第2級的所有優化外,還包括: l -fgcse-after-reload:在重新裝載時執行一遍額外的load elimination; l -finline-functions:把所有的「簡單」函數內聯到調用者中; l -funswitch-loops:Moves branches with loop invariant conditions out of loops 注意 如果使用了多個-O選項,最後的那個決定一切。所以,命令gcc -O3 foo.c bar.c -O0 -o baz將不執行任何優化,因為-O0出現在最後。 Manual GCC Optimization Flags 除了前面講的幾個-O選項能進行的優化外,GCC還有幾個只能用-f指定的優化選項,如下 表所示: Flag Description -fbounds-check 對訪問數組的索引進行檢查 -fdefault-inline 把C++成員函數默認為內聯 -ffast-math 設置: -fno-math-errno -funsafe-math-optimizations -fno-trapping-math 選項 -ffinite-math-only 禁止檢查NaN和無窮大的參數或結果 -ffloat-store 禁止在寄存器裡存儲浮點數值 -fforce-addr 在寄存器裡存儲內存常量 -ffunction-cse 在寄存器裡存儲函數地址 -finline 把用inline關鍵字指定的函數內聯展開 -finline-functions 在調用者裡把簡單的函數內聯 -finline-limit=n 指定內聯函數的偽指令數不超過n -fkeep-inline-functions 保持內聯函數仍為可調用的函數 -fkeep-static-consts 保留用static const申明但從未引用過的變量 -fmath-errno 設置數學函數的errno執行時成為單條指令 -fmerge-all-constants 把模塊間相同值的變量合併 -ftrapping-math Emits code that generates user-visible traps for FP operations -ftrapv 產生代碼捕捉有符號值的運算溢出 -funsafe-math-optimizations 禁止對浮點操作進行錯誤檢查和一致性測試 上面列出的選項很多都有關浮點操作。在進行這些效果不確定的優化的同時,優化器會背 離嚴格的ISO和/或IEEE標準,尤其是對數學函數和浮點運算。在浮點運算量巨大的應用裡 ,這樣做可能有顯著的性能提升,但是代價就是放棄了對標準的遵守。在某些情況下,這 種放棄是可以接受的,當然最終決定權在你的手裡。 注意 不是所有的GCC優化選項都可以用這些標誌來控制。有些優化選項是完全自動進行的,而 且只對代碼進行小的修改。只要你使用了-O,就不能禁止這些優化。 Processor-Specific Optimizations 傳統上,為目標機器定制的優化並不被提倡,因為它們依賴許多目標系統的信息。GCC利 用這些信息產生特殊的代碼,使用處理器特有的屬性或避免已知的缺陷。 在寫這本書以前,我通常只用-O2來優化我的程序,把剩下的事都交給編譯器。寫完這本 書以後,我感覺自己的能力更強了,並給程序增加了一些被證明很有用的編譯選項,即在 特定情況下的特定優化選項。下面的文字都是指導性的,因為畢竟你比我更懂自己的代碼 。 Automating Optimization with Acovea 即使你把本文的內容忘的差不多了,你肯定還是知道GCC的選項簡直有無數個。要想給某 一個特定的程序和特定的體系結構選擇最好的GCC選項集,根本就是不可能的。所以很多 人都做法是,使用標準的優化選項,然後在自己知道的其他選項裡試驗出幾個有用的。這 樣做可能是為了節省開發時間,但是它卻是「可恥」的。 Scott Ladd的Acovea程序(http://www.coyotegulch.com/products/acovea/index.html )提供了一個有趣而且有用的方法,獲得最好的優化選擇,原理是利用進化算法( evolutionary algorithm)模擬自然的選擇。聽起來似乎很神奇,讓我們看看它是怎麼工 作的。Acovea應用那些可能改善各種代碼的優化算法,檢查結果,然後保留那些能使代碼 性能提高的優化。這和自然選擇過程的第一步在概念上非常相似。Acovea然後自動把滿意 的優化算法傳給後續的選擇過程,這樣一步步應用更多的優化算法。 GCC專家們在網上各處發表了很多進化出「最佳」優化的建議。不過,這些建議可能是相 互矛盾的,甚至在你的應用裡不產生效果。Acovea試圖通過反覆的用優化選項編譯代碼, 並自動詳細的分析性能,來得到最佳的結果。這種詳盡的分析經歷可以使你學習GCC優化 選項中最難懂的部分——選項之間的相互影響。Acovea使你可以自動測試所有的GCC選項 組合,幫助你大大加快開發過程,得到最少或編譯最快的程序版本。 Building Acovea 你可以從http://www.coyotegulch.com/products/acovea/index.html上得到Acovea的最 新版本。安裝Acovea前需要2個額外的庫: l coyotl:包含了Acovea用到的許多函數,包括一個定制的隨機數生成器,底層的浮點 應用,一個通用的命令行分析器,和改良的排序和驗證工具; l evocosm:提供了開發進化算法的一個框架。 然後使用簡單的解壓、配置、安裝流程就行了。最後的可執行程序runacovea位於 /usr/local/bin(默認)下。 注意 Acovea只支持類Unix和Linux的系統,對Cygwin的支持可能還需要點工作。 Configuring and Running Acovea Acovea為你的程序進行的測試選項定義在XML格式的配置文件裡,配置文件的模板可以在 http://www.coyotegulch.com/products/acovea/acovea-config.html得到。GCC的版本對 這些配置文件非常重要,所以首先得到和你GCC版本相符的配置文件;其次是處理器的類 型。下面是一個Acovea配置文件的範例: <?xml version="1.0"?> <acovea_config> <acovea version="5.2.0" /> <description value="gcc 4.1 Opteron (AMD64/x86_64)" /> <quoted_options value="false" /> <prime command="gcc" flags="-lrt -lm -std=gnu99 -O1 -march=opteron ACOVEA_OPTIONS -o ACOVEA_OUTPUT ACOVEA_INPUT" /> <baseline description="-O1" command="gcc" flags="-lrt -lm -std=gnu99 -O1 -march=opteron -o ACOVEA_OUTPUT ACOVEA_INPUT" /> <baseline description="-O2" command="gcc" flags="-lrt -lm -std=gnu99 -O2 -march=opteron -o ACOVEA_OUTPUT ACOVEA_INPUT" /> <baseline description="-O3" command="gcc" flags="-lrt -lm -std=gnu99 -O3 -march=opteron -o ACOVEA_OUTPUT ACOVEA_INPUT" /> <baseline description="-O3 -ffast-math" command="gcc" flags="-lrt -lm -std=gnu99 -O3 -march=opteron -ffast-math -o ACOVEA_OUTPUT ACOVEA_INPUT" /> <baseline description="-Os" command="gcc" flags="-lrt -lm -std=gnu99 -Os -march=opteron -o ACOVEA_OUTPUT ACOVEA_INPUT" /> <!-- A list of flags that will be "evolved" by ACOVEA (85 for GCC 4.1!) --> <flags> <!-- O1 options (these turn off options implied by -O1) --> <flag type="simple" value="-fno-merge-constants" /> <flag type="simple" value="-fno-defer-pop" /> <flag type="simple" value="-fno-thread-jumps" /> <flag type="enum" value="-fno-omit-frame-pointer|-momit-leaf-frame-pointer" /> <flag type="simple" value="-fno-guess-branch-probability" /> <flag type="simple" value="-fno-cprop-registers" /> <flag type="simple" value="-fno-if-conversion" /> . . .. <!-- O2 options --> <flag type="simple" value="-fcrossjumping" /> <flag type="simple" value="-foptimize-sibling-calls" /> <flag type="simple" value="-fcse-follow-jumps" /> <flag type="simple" value="-fcse-skip-blocks" /> <flag type="simple" value="-fgcse" /> <flag type="simple" value="-fexpensive-optimizations" /> <flag type="simple" value="-fstrength-reduce" /> <flag type="simple" value="-frerun-cse-after-loop" /> <flag type="simple" value="-frerun-loop-opt" /> … <!-- O3 options --> <flag type="simple" value="-fgcse-after-reload" /> <flag type="simple" value="-finline-functions" /> <flag type="simple" value="-funswitch-loops" /> … <!-- Additional options --> <flag type="simple" value="-ffloat-store" /> <flag type="simple" value="-fprefetch-loop-arrays" /> <flag type="simple" value="-fno-inline" /> <flag type="simple" value="-fpeel-loops" /> … <!-- Tuning options that have a numeric value --> <flag type="tuning" value="-finline-limit" default="600" min="100" max="10000" step="100" separator="=" /> </flags> </acovea_config> 注意 Acovea可以用於任何GCC編譯,只要給baseline屬性的command元素賦相應的值就行了。 當你準備好了配置文件後,就可以使用runacovea程序進行測試: runacovea –config config-file-name –input source-file-name 注意 默認情況下,Acovea把速度和性能放在首位,不過也可以指定-size選項使runacovea首先 優化代碼量。 執行runacovea能得到很多的輸出,因為它使用許多優化的排列組合進行測試,最終的輸 出可能是如下樣子: Acovea completed its analysis at 2005 Nov 24 08:45:34 Optimistic options: -fno-defer-pop (2.551) -fmerge-constants (1.774) -fcse-follow-jumps (1.725) -fthread-jumps (1.822) Pessimistic options: -fcaller-saves (-1.824) -funswitch-loops (-1.581) -funroll-loops (-2.262) -fbranch-target-load-optimize2 (-2.31) -fgcse-sm (-1.533) -ftree-loop-ivcanon (-1.824) -mfpmath=387 (-2.31) -mfpmath=sse (-1.581) Acovea's Best-of-the-Best: gcc -lrt -lm -std=gnu99 -O1 -march=opteron -fno-merge-constants -fno-defer-pop -momit-leaf-frame-pointer -fno-if-conversion -fno-loop-optimize -ftree-ccp -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-copyrename -ftree-fre -ftree-ch -fmerge-constants -fcrossjumping -fcse-follow-jumps -fpeephole2 -fschedule-insns2 -fstrict-aliasing -fthread-jumps -fgcse-lm -fsched-interblock -fsched-spec -freorder-functions -funit-at-a-time -falign-functions -falign-jumps -falign-loops -falign-labels -ftree-pre -finline-functions -fgcse-after-reload -fno-inline -fpeel-loops -funswitch-loops -funroll-all-loops -fno-function-cse -fgcse-las -ftree-vectorize -mno-push-args -mno-align-stringops -minline-all-stringops -mfpmath=sse,387 -funsafe-math-optimizations -finline-limit=600 -o /tmp/ACOVEAA7069796 fibonacci_all.c Acovea's Common Options: gcc -lrt -lm -std=gnu99 -O1 -march=opteron -fno-merge-constants -fno-defer-pop -momit-leaf-frame-pointer -fcse-follow-jumps -fthread-jumps -ftree-pre -o /tmp/ACOVEAAA635117 fibonacci_all.c -O1: gcc -lrt -lm -std=gnu99 -O1 -march=opteron -o /tmp/ACOVEA58D74660 fibonacci_all.c -O2: gcc -lrt -lm -std=gnu99 -O2 -march=opteron -o /tmp/ACOVEA065F6A10 fibonacci_all.c -O3: gcc -lrt -lm -std=gnu99 -O3 -march=opteron -o /tmp/ACOVEA934D7357 fibonacci_all.c -O3 -ffast-math: gcc -lrt -lm -std=gnu99 -O3 -march=opteron -ffast-math -o /tmp/ACOVEA408E67B6 fibonacci_all.c -Os: gcc -lrt -lm -std=gnu99 -Os -march=opteron -o /tmp/ACOVEAAB2E22A4 fibonacci_all.c 正如你所看到的,Acovea生成了一些最佳的優化選項組合列表,還有一些建議的GCC優化 選項信息。 前面也說過,靠手動詳盡的測試所有GCC優化選項並找出它們之間的相互影響,需要相當 長的時間。Acovea的最大作用就是自動幫你來做這件事情。要更多的瞭解Acovea,請參考 http://www.coyotegulch.com/products/acovea。 -- .. S -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.5.65
moonshade:這個有一大本文件的,我看了幾頁就沒力了... 04/03 19:00