新聞中心
這篇文章講的是常量合并,這是VC++編譯器最簡(jiǎn)單的優(yōu)化之一。 這種優(yōu)化,是指編譯器在編譯時(shí)(編譯期間)直接計(jì)算出表達(dá)式的結(jié)果,在生成的代碼中直接用計(jì)算結(jié)果替換表達(dá)式。 這樣就避免了程序在運(yùn)行時(shí)執(zhí)行這些計(jì)算花費(fèi)的成本。

靖宇網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)公司自2013年創(chuàng)立以來(lái)到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。
下面是一個(gè)例子 APP.cpp文件中的main函數(shù):
- int main() { return 7 + 8; }
首先,關(guān)于這篇文章的一些須知:
- 我們將從命令行來(lái)構(gòu)建程序(而不是Visual Studio)
- 我們會(huì)使用Visual Studio 2012。 特別注意的是,這個(gè)版本的編譯器會(huì)產(chǎn)生x64位代碼(而不是已經(jīng)過(guò)時(shí)的x86架構(gòu))在64位機(jī)子上編譯。
如果你想要繼續(xù),請(qǐng)看下說(shuō)明。實(shí)際上,你只需要從Visual Studio 列表里選擇一個(gè)正確的變體。
(注意:如果你正在使用Visual Studio Express上的免費(fèi)編譯器,它僅僅只能運(yùn)行在x86上,但是也會(huì)順利生成x64的代碼。對(duì)這個(gè)實(shí)驗(yàn)同樣有用。)
我們可以通過(guò)命令 CL /FA App.cpp來(lái)構(gòu)建示例程序。用/FA開(kāi)關(guān)創(chuàng)建一個(gè)輸出文件,用來(lái)保存編譯器生成的匯編代碼,可以輸入type App.asm來(lái)顯示:
- PUBLIC main
- _TEXT SEGMENT
- main PROC
- mov eax, 15
- ret 0
- main ENDP
- _TEXT ENDS
- END
有趣的是這條指令move ax,15—-僅僅將15賦值給寄存器EAX(根據(jù)x64調(diào)用標(biāo)準(zhǔn)的定義,x64函數(shù)將會(huì)設(shè)置一個(gè)int值,作為函數(shù)的結(jié)果,并返回給調(diào)用者)。編譯器運(yùn)行期間并沒(méi)有發(fā)出 7加8的指令。就像下面這樣:
- PUBLIC main
- _TEXT SEGMENT
- main PROC
- mov eax, 7
- add eax, 8
- ret 0
- main ENDP
- _TEXT ENDS
- END
(注意看了,這兩段代碼的***一條指令,ret 0,是指將控制權(quán)返回給調(diào)用者,并從棧里彈出0個(gè)字節(jié)。不要被誤導(dǎo)認(rèn)為是返回?cái)?shù)值0給調(diào)用者!)
我猜到,你可能在想:這很好啊,但是哪個(gè)白癡會(huì)想到在代碼里寫(xiě) 7+8 這樣的運(yùn)算?的確,你是對(duì)的,但是編譯器會(huì)把這樣的結(jié)構(gòu)看成是有副作用的宏??戳讼旅娴睦?,你就會(huì)明白常量合并是一個(gè)很有用的優(yōu)化方法:
- #define SECS_PER_MINUTE 60
- #define MINUTES_PER_HOUR 60
- #define HOURS_PER_DAY 24
- enum Event { Started, Stopped, LostData, ParityError };
- struct {
- int clock_time;
- enum Event ev;
- char* reason;
- } Record;
- int main() {
- const int table_size = SECS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * sizeof Record;
- // rest of program
- }
我們要?jiǎng)?chuàng)建一個(gè)足夠大的表保存每一秒的記錄,所以table_size就是表的大小,用字節(jié)表示。很容易查看變量table_size的匯編指令:
- mov DWORD PTR table_size$[rsp], 1382400 ; 00151800H
這兒沒(méi)有乘法指令,60*60*24*16=1382400 是在編譯時(shí)計(jì)算的。
事實(shí)上,我們窺探下編譯器的內(nèi)部,會(huì)發(fā)現(xiàn)這種常量合并的運(yùn)算非常簡(jiǎn)單,它是由前端來(lái)執(zhí)行的。它并不需要后端優(yōu)化器笨重的提升能力。所以它總是存在的。不管你是開(kāi)啟優(yōu)化(使用 /O2)或者關(guān)閉優(yōu)化(/Od)都沒(méi)什么區(qū)別—–該優(yōu)化總是自動(dòng)執(zhí)行的。
不管表達(dá)式有多復(fù)雜,我們都能在編譯期間進(jìn)行常量合并嗎?—事實(shí)上,前端可以處理任意的常量算術(shù)表達(dá)式(甚至包括上面提到的sizeof,只要它們?cè)诰幾g時(shí)能被計(jì)算出來(lái))和運(yùn)算符(+ - * / % << >> ++ 和 –)。你甚至可以使用布爾值,邏輯運(yùn)算符 和條件運(yùn)算符if AND ?:。
有沒(méi)有常量合并需要后端優(yōu)化器的時(shí)候呢?當(dāng)然有,看下面的例子:
- int bump(int n) { return n + 1; }
- int main() { return 3 + bump(6); }
輸入命令cl /FA /Od App.cpp,會(huì)得到信息:不能優(yōu)化,謝謝!,輸入 App.asm,我們會(huì)得到:
- mov ecx, 6
- call ?bump@@YAHH@Z ; bump
- add eax, 3
正如我們所預(yù)料的: ECX會(huì)保存***個(gè)參數(shù)6,根據(jù)x64調(diào)用約定,然后調(diào)用bump函數(shù),結(jié)果返回給EAX,然后EAX再加3。
我們來(lái)看看如果我們使用 cl /FA /O2 App.cpp 來(lái)進(jìn)行優(yōu)化,會(huì)發(fā)生什么。
- mov eax,10
后端優(yōu)化器已經(jīng)識(shí)別到bump函數(shù)很小,可以包含到調(diào)用者里(我們?cè)诤竺娴恼鹿?jié)將會(huì)講到這種優(yōu)化方法,叫做內(nèi)聯(lián)函數(shù))。它在編譯時(shí)就能夠估算出整個(gè)表達(dá)式的值,***只剩下一條單指令。很神奇,對(duì)吧?
網(wǎng)頁(yè)名稱:優(yōu)化C++代碼(3)常量合并
路徑分享:http://fisionsoft.com.cn/article/cospphh.html


咨詢
建站咨詢
