日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Optimizing Code with GCC

發(fā)布時(shí)間:2023/12/18 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Optimizing Code with GCC 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

現(xiàn)在的編譯器越來越聰明,功能越來越強(qiáng),從簡(jiǎn)單的函數(shù)內(nèi)聯(lián),到復(fù)雜的寄存器分析,一系列代碼革命使程序運(yùn)行得越來越快。大多數(shù)時(shí)候,更快比更小重要,因?yàn)榇疟P空間和內(nèi)存都變得便宜了。但是在嵌入式系統(tǒng)里,更小和更快是一樣重要的,所以把代碼進(jìn)行優(yōu)化是非常有意義的工作。

如果你已經(jīng)知道了怎樣用gcc編譯你的代碼,現(xiàn)在是時(shí)候讓你的代碼更快或者更小了,這也是本章的內(nèi)容。如果學(xué)有所成的話,你甚至可以讓你的下一個(gè)程序既快又小。首先我們快速瀏覽一下編譯器優(yōu)化理論,然后討論GCC的代碼優(yōu)化命令行選項(xiàng),從一般的、體系結(jié)構(gòu)無關(guān)的優(yōu)化,到體系結(jié)構(gòu)相關(guān)的優(yōu)化方法。

雖然本章的示例代碼都是C語言的,但是優(yōu)化選項(xiàng)是通用的、語言無關(guān)的。能把一些優(yōu)化選項(xiàng)適用到所有語言的編譯器上,是一個(gè)編譯器家族最大的優(yōu)勢(shì),比如GCC編譯器家族就是這樣。

OPTIMIZATION AND DEBUGGING

沒有代碼優(yōu)化的時(shí)候,GCC的一個(gè)重要目標(biāo)是盡量縮短編譯時(shí)間,并保證產(chǎn)生的代碼在調(diào)試環(huán)境下的行為正確。比如,在優(yōu)化過的代碼里,一個(gè)變量如果在循環(huán)里多次計(jì)算,但是值其實(shí)沒有變化,那么編譯器可以把它移到循環(huán)的外面,只計(jì)算一次。雖然這是可行了(當(dāng)然,只要不改變程序的運(yùn)行結(jié)果),但是這使你無法按照源代碼進(jìn)行調(diào)試,因?yàn)橛?jì)算該變量的代碼被優(yōu)化掉了。如果沒有優(yōu)化,你就可以正確的進(jìn)行調(diào)試,檢查變量的值。這就是所謂的“代碼在調(diào)試環(huán)境下的行為正確”。

優(yōu)化能改變代碼的執(zhí)行流程,但不改變執(zhí)行結(jié)果。所以,優(yōu)化一般是編碼并調(diào)試完成之后才進(jìn)行的。其實(shí)優(yōu)化過的代碼也是可以進(jìn)行調(diào)試的,只是需要一些技巧。

編譯器優(yōu)化理論概覽

代碼優(yōu)化是指分析一段編譯后的代碼,然后決定如何改變它,使它運(yùn)行的更快,消耗的資源更少。擁有此功能的編譯器叫做優(yōu)化編譯器(optimizing compilers),最后的輸出代碼叫做優(yōu)化代碼(optimized code)。

優(yōu)化編譯器使用幾種辦法來決定哪些代碼可被優(yōu)化。一種是控制流分析(control flow analysis),即檢查循環(huán)和其他控制語句,比如if-thencase,找出程序可能的執(zhí)行路徑,然后決定哪些執(zhí)行路徑可以被改進(jìn)。另一個(gè)典型的優(yōu)化技術(shù)是檢查數(shù)據(jù)是怎樣使用的,即數(shù)據(jù)流分析(data flow analysis)。此法檢查變量在哪里是怎樣使用的(或沒被使用),然后應(yīng)用一系列的方程式到這些使用模式上面,從而找到優(yōu)化的途徑。

除了本章所述的一些計(jì)算機(jī)所作的改進(jìn)外,優(yōu)化還包括了對(duì)程序所使用的算法的改進(jìn)。典型的比如冒泡排序算法改進(jìn)成快速排序或希爾排序。這類改進(jìn)能使程序的性能有本質(zhì)的提高,比計(jì)算機(jī)能做的優(yōu)化強(qiáng)得多,所以優(yōu)化既是CPU的事情,也是人的事情。

本節(jié)定義一個(gè)基本塊(basic block)指,只有一個(gè)入口和出口,其他地方不包括終止、分支語句的連續(xù)代碼段。在一個(gè)基本塊內(nèi)進(jìn)行的轉(zhuǎn)化稱為局部轉(zhuǎn)化(local transformations),同樣的不是在一個(gè)基本塊內(nèi)的轉(zhuǎn)化稱為全局轉(zhuǎn)化(global transformations)。通常編譯器會(huì)進(jìn)行許多全局或局部的轉(zhuǎn)化,不過局部轉(zhuǎn)化總是先做。

雖然本節(jié)的例子使用C語言,其實(shí)所有GCC編譯器都使用一種中間語言進(jìn)行這種轉(zhuǎn)化,這種中間語言比各種編程語言更適合計(jì)算機(jī)處理。GCC在產(chǎn)生最終的2進(jìn)制代碼前,會(huì)使用一系列不同的中間語言翻譯你的源程序。不過對(duì)人類來說,C和其他的高級(jí)語言比這些中間語言更好理解。

GCC編譯器還做了很多其他的優(yōu)化,有些非常細(xì)致,甚至需要專業(yè)的編譯器理論知識(shí)。這里列出的優(yōu)化方法只是基礎(chǔ),并且能用命令行來進(jìn)行選擇。

注意

我所知的最經(jīng)典的編譯器著作,當(dāng)屬“龍書”《Compilers: Principles, Techniques, and Tools》,因其封面有一個(gè)恐龍而得名(Alfred V. Aho, Ravi Sethi, and Jeffrey D. UllmanAddison Wesley Longman, 1986. ISBN: 0-201-10088-6)。書里的優(yōu)化理論介紹比本文詳細(xì)的多,并且曾是我的啟蒙書籍。

Code Motion

Code motion是一種優(yōu)化技巧,是指在Common subexpression elimination(后有詳述)時(shí)去掉多余的代碼。Code motion并不是去掉所有的subexpression,而是在中間語言形式下改變它們的位置,以便能減少它們出現(xiàn)的次數(shù)。比如,在嵌套的循環(huán)或其他控制結(jié)構(gòu)里,中間變量的計(jì)算次數(shù)可能不是最優(yōu)的,要優(yōu)化這些程序,編譯器把這些計(jì)算語句移到循環(huán)更少的地方,并且保證計(jì)算結(jié)果是一樣的。把計(jì)算移出循環(huán)的方法我們稱為loop-invariant code motionCode motion還用在另一種Common subexpression elimination里,叫做partial redundancy elimination

Common Subexpression Elimination

去除多余的計(jì)算是一種標(biāo)準(zhǔn)的優(yōu)化手段,因?yàn)樗軠p少程序的指令數(shù),并得到相同的結(jié)果。比如,如果一個(gè)表達(dá)式的參數(shù)(所引用的變量)值不變,那么就可以只計(jì)算一次結(jié)果,在以后引用該表達(dá)式的地方用結(jié)果值替代就可以了。這些后來引用該表達(dá)式的地方就叫做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循環(huán)內(nèi)的表達(dá)式j = 2 * STEP就是一個(gè)common subexpression,因?yàn)樗闹悼梢栽谶M(jìn)入循環(huán)之前計(jì)算,而且它的參數(shù)變量STEP(實(shí)際上是一個(gè)宏定義)從不改變。Common subexpression elimination (CSE)for循環(huán)內(nèi)的重復(fù)計(jì)算去掉,成為如下形式:

j = 2 * STEP;

for (i = 0; i < 100; ++i) {

??? k = p[i] * j;

}

雖然這個(gè)例子簡(jiǎn)單了點(diǎn),不過能很好的說明問題,CSE去掉了100次對(duì)j的計(jì)算。CSE能去掉不必要的計(jì)算,改善程序性能,并減少了最終文件的大小。

Constant Folding

Constant folding是指去掉在編譯時(shí)就能確定的數(shù)值計(jì)算表達(dá)式。這些表達(dá)式必須只包含常數(shù)值,或者值為常數(shù)的變量。比如,下面的計(jì)算表達(dá)式都可以用一個(gè)賦值語句來替換:

n = 10 * 20 * 400;

?

i = 10;

j = 20;

ij = i * j;

后面的例子里,如果ij在后續(xù)的程序里沒有用到的話,完全可以去除它們的定義。

Copy Propagation Transformations

這是另一種減少或去除多余計(jì)算的方法,即去除那些只是為了傳遞數(shù)值的變量復(fù)制操作。看下面的代碼:

i = 10;

x[j] = i;

y[MIN] = b;

b = i;

Copy propagation transformation可能會(huì)優(yōu)化成下面的代碼:

i = 10;

x[j] = 10;

y[MIN] = b;

b = 10;

在本例里,copy propagation允許把右值變成常數(shù),這樣比搜索和復(fù)制變量的值要快得多,并且也能去掉變量給自己賦值的情況。有些情況下,copy propagation并不直接產(chǎn)生優(yōu)化,但是能簡(jiǎn)化代碼,方便其他優(yōu)化,比如code motioncode elimination

Constant propagation是一種把變量替換成常量的copy propagation transformation優(yōu)化方法。Copy propagation主要指去掉不必要的變量間的相互復(fù)制,而Constant propagation則指去掉不必要的預(yù)定義值到變量的復(fù)制。

Dead Code Elimination

DCE是指把那些實(shí)際上無用的或多余的代碼去掉。你可能想問“為什么我會(huì)寫這樣的代碼呢?”,其實(shí)在一個(gè)很大的、延續(xù)時(shí)間很長(zhǎng)的項(xiàng)目里,這是很容易發(fā)生的。許多DCE都是在中間語言表示的形式下進(jìn)行的,因?yàn)樗窃创a更標(biāo)準(zhǔn)的翻譯,更容易發(fā)現(xiàn)不必要的中間計(jì)算。

Unreachable code elimination是指去除編譯器確定不可能到達(dá)的代碼。比如下面的代碼塊:

if ( i == 10 ) {

??? . . .

} else {

??? . . .

??? if ( i == 10) {

??????? . . .

??? }

}

2次對(duì)i是否等于10的測(cè)試及處理代碼可以去掉,因?yàn)樗遣豢赡艿竭_(dá)的。UCE也是在中間代碼形式里進(jìn)行的。

If-Conversion

即分支重構(gòu),比如把大的if-then-elseif-else結(jié)構(gòu),重構(gòu)成多個(gè)if語句,這樣能簡(jiǎn)化代碼,為以后的優(yōu)化提供方便,并去除某些無用的跳轉(zhuǎn)和分支語句。

Inlining

即把復(fù)雜結(jié)構(gòu)或函數(shù)調(diào)用替換成內(nèi)聯(lián)的代碼以改善性能。Code inliningloop unrolling都是指把全部或部分循環(huán)展開成一系列的直接指令。Function inlining是指用函數(shù)執(zhí)行的指令替代對(duì)函數(shù)的調(diào)用。一般情況下,inlining能減少代碼復(fù)雜度,提高性能,因?yàn)椴恍枰嘤嗟姆种D(zhuǎn)。它還能給common subexpression eliminationcode motion提供優(yōu)化機(jī)會(huì)。這方面最經(jīng)典的例子是Duff’s Device,詳見http://en.wikipedia.org/wiki/Duff's_device

GCC Optimization Basics

GCC處理源代碼時(shí),會(huì)把它轉(zhuǎn)化成一種中間形式。這樣做有幾大好處:、

l? 把源代碼變得簡(jiǎn)單、低級(jí),使優(yōu)化點(diǎn)暴露出來;

l? 使可能很復(fù)雜的結(jié)構(gòu)更容易生成簡(jiǎn)單易讀的語法分析樹;

l? 使用統(tǒng)一的中間形式使GCC編譯器之間能通用優(yōu)化策略。

傳統(tǒng)上GCC使用的內(nèi)部中間形式叫做Register Transfer Language (RTL),這是一種很低級(jí)的語言,GCC把任何代碼(無論什么級(jí)別)轉(zhuǎn)化成目標(biāo)代碼之前都先翻譯成這種代碼。對(duì)于像GCCRTL這種非常低級(jí)的語言進(jìn)行的優(yōu)化也是很“低級(jí)”的,比如寄存器分配、堆棧和數(shù)據(jù)優(yōu)化等。因?yàn)樗艿图?jí),所以不會(huì)像你想象的那樣能進(jìn)行數(shù)據(jù)類型、數(shù)組和變量引用、控制流改變等“高級(jí)”的優(yōu)化。

GCC 4.0的作者們發(fā)明了一種新的中間形式static single assignment (SSA),通過對(duì)GCC編譯器產(chǎn)生的語法分析樹進(jìn)行操作而得到,因此得名Tree SSA4.0及更高的GCC編譯器在生成Tree SSA之前還有2種中間形式,叫做GENERICGIMPLEGENERIC是通過去除源代碼中語言相關(guān)的結(jié)構(gòu)得到的中間形式,GIMPLE則是把GENERIC只讀地址引用進(jìn)行簡(jiǎn)化得到。也許你也看出來了,在到達(dá)RTL等級(jí)之前,有許多的優(yōu)化已經(jīng)在這些相對(duì)高級(jí)點(diǎn)的層面上先做了。

關(guān)于Tree SSA的詳細(xì)信息和優(yōu)化處理的步驟有許多資料可參考,其中一個(gè)是2003年的GCC開發(fā)者總結(jié),網(wǎng)址為http://www.linux.org.uk/~ajh/gcc/gccsummit-2003-proceedings.pdf

What’s New in GCC 4.x Optimization

GCC 4.x家族最重要的變化是引入了中間形式Tree SSA,它提供了更多的優(yōu)化空間,和更多的參數(shù)選項(xiàng),包括-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,本章稍候會(huì)敘述。由于有了這些重大的改變,原來的通用優(yōu)化等級(jí)-O1-O2-O3-Os都有了變化。除此以外,任何語言的GCC編譯器的優(yōu)化都更普遍了。

同時(shí)由于有IBM的大力支持,GCC 4改進(jìn)了向量化。向量化發(fā)現(xiàn)同一操作應(yīng)用到多個(gè)數(shù)據(jù)的代碼,并改善其性能。GCC 4可以把16個(gè)標(biāo)量操作合成為一個(gè)向量操作。這個(gè)優(yōu)化方法可以在游戲、視頻和多媒體應(yīng)用里大展身手,因?yàn)檫@些程序的指令都是對(duì)數(shù)組向量的重復(fù)操作。

GCC 4還改進(jìn)了數(shù)組邊界檢查和棧的內(nèi)容結(jié)構(gòu)檢查,保護(hù)程序免遭流行的緩沖區(qū)和棧溢出攻擊。

Architecture-Independent Optimizations

GCC的優(yōu)化分為2大類:體系結(jié)構(gòu)無關(guān)和體系結(jié)構(gòu)相關(guān)。本節(jié)介紹體系結(jié)構(gòu)無關(guān)的優(yōu)化,包括計(jì)算機(jī)體系無關(guān),比如x86;處理器類型無關(guān),比如IA-32處理器;和處理器家族無關(guān),例如Pentium IV (Xeon)

GCC的優(yōu)化選項(xiàng)有-O-On,參數(shù)n是介于03之間的整數(shù);或者-Os-O0關(guān)閉優(yōu)化。-O-O1(又叫作第1級(jí)優(yōu)化)等價(jià),允許編譯器在不大量增加編譯時(shí)間的前提下減少代碼量和執(zhí)行時(shí)間。-O2-O3-O1的優(yōu)化等級(jí)更高,-Os會(huì)最小化代碼量。

本節(jié)所有的表格顯示了GCC提供的各種優(yōu)化選項(xiàng),如果要關(guān)閉相應(yīng)優(yōu)化,只需在-f和優(yōu)化選項(xiàng)名字之間加上no-就行了。比如,要禁止deferred stack pops優(yōu)化,命令行可以這樣寫:

$ gcc myprog.c -o myprog -O1 -fno-defer-pop

注意

-f表示一個(gè)機(jī)器無關(guān)的操作標(biāo)志,即應(yīng)用一個(gè)(大多數(shù)情況下)體系結(jié)構(gòu)無關(guān)的優(yōu)化操作。這些標(biāo)志選項(xiàng)更改了GCC的默認(rèn)行為,但是不需要硬件的特殊支持。通常你可以指定多個(gè)標(biāo)志。

Level 1 GCC Optimizations

下表列出了-O-O1時(shí)進(jìn)行的默認(rèn)優(yōu)化選項(xiàng):

?

Optimization

Description

-fcprop-registers

試圖減少寄存器復(fù)制操作的次數(shù)

-fdefer-pop

Accumulates function arguments on the stack.

-fdelayed-branch

Utilizes instruction slots available after delayed branch instructions.

-fguess-branch-probability

利用隨機(jī)預(yù)測(cè)器猜測(cè)分支的可達(dá)性

-fif-conversion

把有條件跳轉(zhuǎn)變成非分支語句

-fif-conversion2

利用條件執(zhí)行(要求CPU支持)進(jìn)行if-conversion優(yōu)化

-floop-optimize

應(yīng)用幾個(gè)針對(duì)循環(huán)的優(yōu)化

-fmerge-constants

合并多個(gè)模塊中相等的常量

-fomit-frame-pointer

省略函數(shù)楨指針的存儲(chǔ)。只能在不影響調(diào)試的系統(tǒng)里激活

-ftree-ccp

SSA Trees上進(jìn)行較少的conditional constant propagationCCP)優(yōu)化(只限GCC 4.x

-ftree-ch

SSA Trees上執(zhí)行loop header copying,即去掉一個(gè)跳轉(zhuǎn)指令,并提供code motion優(yōu)化的機(jī)會(huì)(只限GCC 4.x

-ftree-copyrename

SSA Trees上執(zhí)行copy renaming,即在復(fù)制位置把內(nèi)部變量的名字改得更接近原始變量的名字(只限GCC 4.x

-ftree-dce

SSA Trees上執(zhí)行dead code elimination (DCE)優(yōu)化(只限GCC 4.x

-ftree-dominator-opts

利用支配樹(dominator tree)遍歷來進(jìn)行一系列優(yōu)化。A dominator tree is a tree where each node’s children are the nodes that it immediately dominates。這些優(yōu)化包括constant/copy propagationredundancy eliminationrange propagationexpression simplificationjump threading(減少跳轉(zhuǎn)語句)(只限GCC 4.x

-ftree-dse

SSA Trees上執(zhí)行dead store elimination (DSE)(只限GCC 4.x

-ftree-fre

SSA Trees上執(zhí)行full redundancy elimination (FRE),即認(rèn)為全路徑計(jì)算的表達(dá)式會(huì)導(dǎo)致冗余編譯。這和partial redundancy elimination(PRE)相似,不過比它快,找到的冗余也比較少。(只限GCC 4.x

-ftree-lrs

SSA Trees轉(zhuǎn)化成RTL前,轉(zhuǎn)化成一般形式,并執(zhí)行live range splitting。這種方法明確了變量的生存期,為后續(xù)的優(yōu)化提供幫助(只限GCC 4.x

-ftree-sra

把聚合體替換成標(biāo)量,即把對(duì)結(jié)構(gòu)體的引用替換成標(biāo)量數(shù)值,避免在不必要的時(shí)候把結(jié)構(gòu)體提交到內(nèi)存里(只限GCC 4.x

-ftree-ter

SSA Trees轉(zhuǎn)化成RTL前,轉(zhuǎn)化成一般形式,并執(zhí)行temporary expression replacement (TER)。把只使用一次的臨時(shí)表達(dá)式替換成原始定義的表達(dá)式,這樣更容易產(chǎn)生RTL代碼,并使產(chǎn)生的RTL代碼有更多的優(yōu)化機(jī)會(huì)。(只限GCC 4.x

1級(jí)優(yōu)化揉合了代碼大小和速度改進(jìn)2種優(yōu)化措施。比如,-tree-dce去掉了無用代碼,于是減少了代碼量;跳轉(zhuǎn)指令減少使整個(gè)程序的棧使用量減少;而-fcprop-registers是性能優(yōu)化,減少在寄存器間復(fù)制數(shù)據(jù)的次數(shù)。

-fdelayed-branch-fguess-branch-probability是指令調(diào)度改進(jìn)。如果底層CPU支持指令調(diào)度,這些優(yōu)化標(biāo)志就試圖使CPU等待下一條指令的等待時(shí)間最小化。

-floop-optimize開啟了對(duì)循環(huán)的優(yōu)化,包括把常數(shù)表達(dá)式移出循環(huán)和簡(jiǎn)化推出循環(huán)的條件測(cè)試。在更高的第2級(jí)優(yōu)化里,該標(biāo)志還執(zhí)行strength reduction和循環(huán)展開。

-fomit-frame-pointer是非常有用,原因有2個(gè):省下了設(shè)置、保存和恢復(fù)楨指針的代碼;有時(shí)候省下了一個(gè)CPU寄存器,可有它用。而負(fù)面影響是:沒有了楨指針,調(diào)試(比如棧跟蹤,尤其是嵌套很深的函數(shù))變得很難甚至不可能。

-O2優(yōu)化(第2級(jí)優(yōu)化)包括了第1級(jí)的所有優(yōu)化加上下表列出的另一些優(yōu)化。應(yīng)用這些優(yōu)化將延長(zhǎng)編譯時(shí)間,不過你的程序性能將得到顯著的提高。

Level 2 GCC Optimizations

當(dāng)使用-O2優(yōu)化選項(xiàng)時(shí),下表的優(yōu)化將默認(rèn)進(jìn)行:

Optimization

Description

-falign-functions

把函數(shù)對(duì)齊到2的指數(shù)字節(jié)邊界

-falign-jumps

把跳轉(zhuǎn)指令對(duì)齊到2的指數(shù)字節(jié)邊界

-falign-labels

把標(biāo)簽對(duì)齊到2的指數(shù)字節(jié)邊界

-falign-loops

把循環(huán)對(duì)齊到2的指數(shù)字節(jié)邊界

-fcaller-saves

保存并恢復(fù)被函數(shù)調(diào)用改寫的寄存器

-fcrossjumping

分解等價(jià)代碼來減少代碼量

-fcse-follow-jumps

CSE過程中跳過不會(huì)到達(dá)的目標(biāo)

-fcse-skip-blocks

CSE過程中可以跳過條件塊

-fdelete-null-pointer-checks

去掉不必要的null指針檢查

-fexpensive-optimizations

執(zhí)行一些“較昂貴”的優(yōu)化

-fforce-mem

在寄存器里保存內(nèi)存操作數(shù)(只限GCC 4.1

-fgcse

執(zhí)行一遍全局CSECommon Subexpression Elimination

-fgcse-lm

在全局CSE時(shí)把裝載指令移到循環(huán)外面

-fgcse-sm

在全局CSE時(shí)把保存治療移到循環(huán)外面

-foptimize-sibling-calls

優(yōu)化有副作用的或尾遞歸的函數(shù)調(diào)用

-fpeephole2

執(zhí)行機(jī)器相關(guān)的深度優(yōu)化

-fregmove

Reassigns register numbers for maximum register tying

-freorder-blocks

重新安排函數(shù)的基本塊,以便減少分支和提高代碼局部性

-freorder-functions

對(duì)于經(jīng)常調(diào)用或極少調(diào)用的函數(shù),使用特殊的text段重新安排函數(shù)的基本塊,以提高代碼局部性

-frerun-cse-after-loop

在循環(huán)優(yōu)化之后執(zhí)行一遍CSE

-frerun-loop-opt

執(zhí)行2此循環(huán)優(yōu)化

-fsched-interblock

在基本塊間調(diào)度指令

-fsched-spec

Schedules speculative execution of nonload instructions

-fschedule-insns

重新安排指令以最小化執(zhí)行延遲

-fschedule-insns2

執(zhí)行第2schedule-insns

-fstrength-reduce

用“廉價(jià)”的指令代替“昂貴”的指令

-fstrict-aliasing

通知編譯器使用最嚴(yán)格的別名規(guī)則(aliasing rules

-fthread-jumps

試圖重新安排跳轉(zhuǎn)指令的順序,成為執(zhí)行的順序

-ftree-pre

SSA Trees上執(zhí)行partial redundancy elimination (PRE)

-funit-at-a-time

在開始代碼生成之前對(duì)整個(gè)文件進(jìn)行語法分析,以便進(jìn)行額外的優(yōu)化,比如重新安排代碼和申明,去掉從不引用的靜態(tài)變量和函數(shù)等。

-fweb

把每個(gè)web(代碼的存活范圍)賦給它自己的偽寄存器,方便后續(xù)的優(yōu)化,例如CSEdead code elimination和循環(huán)優(yōu)化。

4個(gè)-falign-選項(xiàng)強(qiáng)制函數(shù)、跳轉(zhuǎn)指令、標(biāo)簽和循環(huán)對(duì)齊到2的指數(shù)邊界,原理是內(nèi)存對(duì)齊的數(shù)據(jù)和結(jié)構(gòu)對(duì)計(jì)算機(jī)有更高的訪問速度。前提是對(duì)齊后的代碼被頻繁調(diào)用,能彌補(bǔ)因?qū)R造成的no-op指令的延遲。

-fcse-follow-jumps-fcse-skip-blocks正如其名,是在前面介紹的CSE過程中執(zhí)行的優(yōu)化。使用-fcse-follow-jumpsCSE會(huì)跳過不可到達(dá)的目標(biāo)代碼。比如,下面的條件代碼:

if (i < 10) {

??? foo();

} else {

??? bar();

}

通常,即使(i < 10)測(cè)試為falseCSE仍要按照全路徑對(duì)foo()進(jìn)行優(yōu)化。如果你指定了-fcse-follow-jumpsCSE就直接跳到else塊進(jìn)行優(yōu)化(bar())。

-fcse-skip-blocks使CSE可以跳過條件塊。比如你寫了如下的if語句:

if (i >= 0) {

??? j = foo(i);

}

bar(j);

如果你指定了-fcse-skip-blocks而且i是負(fù)值,那么CSE將直接跳到bar(),越過了原來的if語句。而通常情況下,無論i是什么值,CSE都需要對(duì)if語句進(jìn)行處理。

-fpeephole2執(zhí)行CPU相關(guān)的深度優(yōu)化,把較長(zhǎng)的指令集替換成較短的、簡(jiǎn)練的指令。比如下面的代碼:

a = 2;

for (i = 1; i < 10; ++i)

a += 2;

GCC可能把整個(gè)循環(huán)替換成賦值語句a=20。使用了-fpeephole2GCC就在標(biāo)準(zhǔn)的深度優(yōu)化(比如C語言里用位操作代替算術(shù)操作)之外還進(jìn)行CPU相關(guān)的優(yōu)化。

-fforce-mem是指在對(duì)指針進(jìn)行運(yùn)算前,把內(nèi)存操作數(shù)和常量復(fù)制到寄存器里,目的是生成內(nèi)存引用的common subexpressions,然后用CSE進(jìn)行優(yōu)化。前面已經(jīng)講過,CSE能去除多余的寄存器裝載指令。

-foptimize-sibling-calls試圖優(yōu)化掉尾遞歸的或同屬調(diào)用(sibling call)的函數(shù)。尾遞歸調(diào)用是指函數(shù)的遞歸調(diào)用出現(xiàn)在最后面。比如下面的代碼:

int inc(int i)

{

??? printf("%d/n" i);

??? if(i < 10)

??????? inc(i + 1);

}

上面定義的inc()函數(shù),在函數(shù)體最后遞歸調(diào)用了以i+1為參數(shù)的自身,直到i大于等于10。既然尾遞歸調(diào)用的深度是已知的,那么就可以用一個(gè)迭代來消除尾遞歸。-foptimize-sibling-calls就試圖進(jìn)行這種優(yōu)化。同屬調(diào)用(sibling call)也是指函數(shù)調(diào)用出現(xiàn)在尾上下文(tail context,比如return語句)中。

GCC Optimizations for Code Size

選項(xiàng)-Os變得越來越流行,因?yàn)樗说?span style="font-family:Calibri">2級(jí)優(yōu)化里除增加代碼量以外的所有優(yōu)化。-Os還應(yīng)用了一些減少代碼量的額外優(yōu)化。代碼量在這里不是指程序文件在磁盤里占用的存儲(chǔ)空間,而是指程序運(yùn)行時(shí)占用的內(nèi)存空間。注意,-Os會(huì)自動(dòng)屏蔽下面的優(yōu)化選項(xiàng):

? -falign-functions

? -falign-jumps

? -falign-labels

? -falign-loops

? -fprefetch-loop-arrays

? -freorder-blocks

? -freorder-blocks-and-partition

? -ftree-ch

分別使用-O2-Os編譯程序,然后對(duì)比它們的性能和內(nèi)存用量是很有意義的。比如,我發(fā)現(xiàn)最新的Linux內(nèi)核下使用-O2-Os編譯的程序擁有幾乎相同的運(yùn)行時(shí)性能,但是后者的運(yùn)行時(shí)內(nèi)存用量卻少了15%。當(dāng)然,你的環(huán)境下可能有不同的發(fā)現(xiàn)。

Level 3 GCC Optimizations

指定-O3優(yōu)化選項(xiàng)除了包括第1級(jí),第2級(jí)的所有優(yōu)化外,還包括:

l? -fgcse-after-reload:在重新裝載時(shí)執(zhí)行一遍額外的load elimination

l? -finline-functions:把所有的“簡(jiǎn)單”函數(shù)內(nèi)聯(lián)到調(diào)用者中;

l? -funswitch-loopsMoves branches with loop invariant conditions out of loops

注意

如果使用了多個(gè)-O選項(xiàng),最后的那個(gè)決定一切。所以,命令gcc -O3 foo.c bar.c -O0 -o baz將不執(zhí)行任何優(yōu)化,因?yàn)?span style="font-family:Calibri">-O0出現(xiàn)在最后。

Manual GCC Optimization Flags

除了前面講的幾個(gè)-O選項(xiàng)能進(jìn)行的優(yōu)化外,GCC還有幾個(gè)只能用-f指定的優(yōu)化選項(xiàng),如下表所示:

Flag

Description

-fbounds-check

對(duì)訪問數(shù)組的索引進(jìn)行檢查

-fdefault-inline

C++成員函數(shù)默認(rèn)為內(nèi)聯(lián)

-ffast-math

設(shè)置:

-fno-math-errno

-funsafe-math-optimizations

-fno-trapping-math

選項(xiàng)

-ffinite-math-only

禁止檢查NaN和無窮大的參數(shù)或結(jié)果

-ffloat-store

禁止在寄存器里存儲(chǔ)浮點(diǎn)數(shù)值

-fforce-addr

在寄存器里存儲(chǔ)內(nèi)存常量

-ffunction-cse

在寄存器里存儲(chǔ)函數(shù)地址

-finline

把用inline關(guān)鍵字指定的函數(shù)內(nèi)聯(lián)展開

-finline-functions

在調(diào)用者里把簡(jiǎn)單的函數(shù)內(nèi)聯(lián)

-finline-limit=n

指定內(nèi)聯(lián)函數(shù)的偽指令數(shù)不超過n

-fkeep-inline-functions

保持內(nèi)聯(lián)函數(shù)仍為可調(diào)用的函數(shù)

-fkeep-static-consts

保留用static const申明但從未引用過的變量

-fmath-errno

設(shè)置數(shù)學(xué)函數(shù)的errno執(zhí)行時(shí)成為單條指令

-fmerge-all-constants

把模塊間相同值的變量合并

-ftrapping-math

Emits code that generates user-visible traps for FP operations

-ftrapv

產(chǎn)生代碼捕捉有符號(hào)值的運(yùn)算溢出

-funsafe-math-optimizations

禁止對(duì)浮點(diǎn)操作進(jìn)行錯(cuò)誤檢查和一致性測(cè)試

上面列出的選項(xiàng)很多都有關(guān)浮點(diǎn)操作。在進(jìn)行這些效果不確定的優(yōu)化的同時(shí),優(yōu)化器會(huì)背離嚴(yán)格的ISO/IEEE標(biāo)準(zhǔn),尤其是對(duì)數(shù)學(xué)函數(shù)和浮點(diǎn)運(yùn)算。在浮點(diǎn)運(yùn)算量巨大的應(yīng)用里,這樣做可能有顯著的性能提升,但是代價(jià)就是放棄了對(duì)標(biāo)準(zhǔn)的遵守。在某些情況下,這種放棄是可以接受的,當(dāng)然最終決定權(quán)在你的手里。

注意

不是所有的GCC優(yōu)化選項(xiàng)都可以用這些標(biāo)志來控制。有些優(yōu)化選項(xiàng)是完全自動(dòng)進(jìn)行的,而且只對(duì)代碼進(jìn)行小的修改。只要你使用了-O,就不能禁止這些優(yōu)化。

Processor-Specific Optimizations

傳統(tǒng)上,為目標(biāo)機(jī)器定制的優(yōu)化并不被提倡,因?yàn)樗鼈円蕾囋S多目標(biāo)系統(tǒng)的信息。GCC利用這些信息產(chǎn)生特殊的代碼,使用處理器特有的屬性或避免已知的缺陷。

在寫這本書以前,我通常只用-O2來優(yōu)化我的程序,把剩下的事都交給編譯器。寫完這本書以后,我感覺自己的能力更強(qiáng)了,并給程序增加了一些被證明很有用的編譯選項(xiàng),即在特定情況下的特定優(yōu)化選項(xiàng)。下面的文字都是指導(dǎo)性的,因?yàn)楫吘鼓惚任腋约旱拇a。

Automating Optimization with Acovea

即使你把本文的內(nèi)容忘的差不多了,你肯定還是知道GCC的選項(xiàng)簡(jiǎn)直有無數(shù)個(gè)。要想給某一個(gè)特定的程序和特定的體系結(jié)構(gòu)選擇最好的GCC選項(xiàng)集,根本就是不可能的。所以很多人都做法是,使用標(biāo)準(zhǔn)的優(yōu)化選項(xiàng),然后在自己知道的其他選項(xiàng)里試驗(yàn)出幾個(gè)有用的。這樣做可能是為了節(jié)省開發(fā)時(shí)間,但是它卻是“可恥”的。

Scott LaddAcovea程序(http://www.coyotegulch.com/products/acovea/index.html)提供了一個(gè)有趣而且有用的方法,獲得最好的優(yōu)化選擇,原理是利用進(jìn)化算法(evolutionary algorithm)模擬自然的選擇。聽起來似乎很神奇,讓我們看看它是怎么工作的。Acovea應(yīng)用那些可能改善各種代碼的優(yōu)化算法,檢查結(jié)果,然后保留那些能使代碼性能提高的優(yōu)化。這和自然選擇過程的第一步在概念上非常相似。Acovea然后自動(dòng)把滿意的優(yōu)化算法傳給后續(xù)的選擇過程,這樣一步步應(yīng)用更多的優(yōu)化算法。

GCC專家們?cè)诰W(wǎng)上各處發(fā)表了很多進(jìn)化出“最佳”優(yōu)化的建議。不過,這些建議可能是相互矛盾的,甚至在你的應(yīng)用里不產(chǎn)生效果。Acovea試圖通過反復(fù)的用優(yōu)化選項(xiàng)編譯代碼,并自動(dòng)詳細(xì)的分析性能,來得到最佳的結(jié)果。這種詳盡的分析經(jīng)歷可以使你學(xué)習(xí)GCC優(yōu)化選項(xiàng)中最難懂的部分——選項(xiàng)之間的相互影響。Acovea使你可以自動(dòng)測(cè)試所有的GCC選項(xiàng)組合,幫助你大大加快開發(fā)過程,得到最少或編譯最快的程序版本。

Building Acovea

你可以從http://www.coyotegulch.com/products/acovea/index.html上得到Acovea的最新版本。安裝Acovea前需要2個(gè)額外的庫(kù):

l? coyotl:包含了Acovea用到的許多函數(shù),包括一個(gè)定制的隨機(jī)數(shù)生成器,底層的浮點(diǎn)應(yīng)用,一個(gè)通用的命令行分析器,和改良的排序和驗(yàn)證工具;

l? evocosm:提供了開發(fā)進(jìn)化算法的一個(gè)框架。

然后使用簡(jiǎn)單的解壓、配置、安裝流程就行了。最后的可執(zhí)行程序runacovea位于/usr/local/bin(默認(rèn))下。

注意

Acovea只支持類UnixLinux的系統(tǒng),對(duì)Cygwin的支持可能還需要點(diǎn)工作。

Configuring and Running Acovea

Acovea為你的程序進(jìn)行的測(cè)試選項(xiàng)定義在XML格式的配置文件里,配置文件的模板可以在http://www.coyotegulch.com/products/acovea/acovea-config.html得到。GCC的版本對(duì)這些配置文件非常重要,所以首先得到和你GCC版本相符的配置文件;其次是處理器的類型。下面是一個(gè)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元素賦相應(yīng)的值就行了。

當(dāng)你準(zhǔn)備好了配置文件后,就可以使用runacovea程序進(jìn)行測(cè)試:

runacovea –config config-file-name –input source-file-name

注意

默認(rèn)情況下,Acovea把速度和性能放在首位,不過也可以指定-size選項(xiàng)使runacovea首先優(yōu)化代碼量。

執(zhí)行runacovea能得到很多的輸出,因?yàn)樗褂迷S多優(yōu)化的排列組合進(jìn)行測(cè)試,最終的輸出可能是如下樣子:

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生成了一些最佳的優(yōu)化選項(xiàng)組合列表,還有一些建議的GCC優(yōu)化選項(xiàng)信息。

前面也說過,靠手動(dòng)詳盡的測(cè)試所有GCC優(yōu)化選項(xiàng)并找出它們之間的相互影響,需要相當(dāng)長(zhǎng)的時(shí)間。Acovea的最大作用就是自動(dòng)幫你來做這件事情。要更多的了解Acovea,請(qǐng)參考http://www.coyotegulch.com/products/acovea。

總結(jié)

以上是生活随笔為你收集整理的Optimizing Code with GCC的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。