科研和工程中的C++編程3-代碼優(yōu)化_第1頁
科研和工程中的C++編程3-代碼優(yōu)化_第2頁
科研和工程中的C++編程3-代碼優(yōu)化_第3頁
科研和工程中的C++編程3-代碼優(yōu)化_第4頁
科研和工程中的C++編程3-代碼優(yōu)化_第5頁
已閱讀5頁,還剩24頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

科研和工程中的C++編程代碼優(yōu)化浙江大學(xué)計算機(jī)學(xué)院

袁昕代碼優(yōu)化剖析(profile)

利用工具分析程序代碼,調(diào)用頻率,計算時間等。要防止:

(1)優(yōu)化了錯誤的代碼:若一個程序的主要指標(biāo)不是效率,那么一切花在使其更高效上的時間都是浪費(fèi)??恐庇X來判斷哪段代碼的主要指標(biāo)是效率是不可信的,只有直接去測量。

(2)程序員經(jīng)?!皟?yōu)化”到降低了代碼的速度。這在C++是一個典型問題,一個簡單的指令行可能會產(chǎn)生巨大數(shù)量的機(jī)器代碼,應(yīng)當(dāng)經(jīng)常檢查編譯器的輸出,并且剖析之*。代碼優(yōu)化構(gòu)造和析構(gòu):

未經(jīng)認(rèn)真設(shè)計的程序經(jīng)?;ㄙM(fèi)不少時間在調(diào)用構(gòu)造函數(shù),拷貝對象以及初始化臨時對象等等。代碼優(yōu)化即便arg為0,也付出了調(diào)用Object的構(gòu)造函數(shù)的代價。特別是如果arg經(jīng)常是0,并且Object本身還分配內(nèi)存,這種浪費(fèi)會更加嚴(yán)重。顯然的解決方案就是把obj的定義移到判斷之后。在循環(huán)中定義復(fù)雜變量要注意,若在循環(huán)中按照除非需要否則不構(gòu)造的原則構(gòu)造了復(fù)雜的對象,那么在每一次循環(huán)的時候都要付出一次構(gòu)造的代價。最好在循環(huán)外構(gòu)造之,并只構(gòu)造一次。如果一個函數(shù)在內(nèi)循環(huán)中被調(diào)用,而該函數(shù)在棧內(nèi)構(gòu)造了一個對象,那么可以在外部構(gòu)造并傳遞一個引用給它。代碼優(yōu)化盡量采用初始化列表,類中的成員類對象,如果在構(gòu)造方法里初始化,一般是使用了等號操作符,這樣就等于是調(diào)用了無參數(shù)構(gòu)造方法一次,再調(diào)用一次等號操作符。而使用初始化列表則直接調(diào)用其拷貝構(gòu)造方法*。要前自增不要后自增(即要++A不要A++),后自增會產(chǎn)生臨時對象,調(diào)用構(gòu)造方法。對于整數(shù),這沒有額外的負(fù)擔(dān),但對于用戶自定義類型,這就是浪費(fèi)。代碼優(yōu)化盡量少使用有返回值的操作符,如Toperator+(constT&)等,它返回時將構(gòu)造臨時對象,調(diào)用構(gòu)造方法。可嘗試C++0x引入的新的語法:右值引用。操作符+-*/%等都需要被設(shè)計成有返回值的方法,因此方法體內(nèi)部出錯時,只能以拋出異常的方式來通知調(diào)用者。代碼優(yōu)化構(gòu)造方法盡可能寫輕量級代碼,有些成員初始化可以使用兩步的方法,構(gòu)造方法什么都不作,另外設(shè)計一個方法來初始化成員。要注意類型轉(zhuǎn)換會產(chǎn)生隱藏的臨時對象,所以要針對各種類型重載操作符方法。代碼優(yōu)化虛函數(shù):

虛函數(shù)的機(jī)制很簡單。為了完成一個對象的虛函數(shù)調(diào)用,編譯器訪問對象的虛函數(shù)表,獲得一個成員函數(shù)的指針,設(shè)置調(diào)用環(huán)境,然后跳轉(zhuǎn)到該成員函數(shù)的地址上。一個虛函數(shù)調(diào)用的額外負(fù)擔(dān)是虛函數(shù)表的間接指向;由于事先并不知道將要跳轉(zhuǎn)的地址,所以也有可能造成處理器不能命中Cache。代碼優(yōu)化一般的C++程序都對虛函數(shù)有大量的使用,所以主要的手段是防止在那些極其重視效率的地方的虛函數(shù)調(diào)用??梢愿某蓛?nèi)聯(lián)函數(shù)以省去函數(shù)調(diào)用的開銷,也可以利用模版類的特點(diǎn)使用mix-inclass模式(混同體,設(shè)計模式書中是策略模式)。如下圖:代碼優(yōu)化代碼優(yōu)化其中的模版參數(shù)T是最終派生類。這樣在CXXX實例中調(diào)用XXX()方法時,將調(diào)用最終派生類CXXX中重載的DoXXX()方法,也做到了多態(tài),但省去了虛函數(shù)表的調(diào)用開銷。

Windows下ATL編程還可以在父類上進(jìn)一步加ATL_NO_VTABLE關(guān)鍵字,如classATL_NO_VTABLECXXXImpl,目的使得父類構(gòu)造方法不初始化虛函數(shù)表,從而優(yōu)化時把父類虛函數(shù)表及虛函數(shù)體從最后生成的模塊中刪除,從而減少了可執(zhí)行文件的大小,進(jìn)而減少內(nèi)存頁的切換。代碼優(yōu)化在很小的、頻繁使用的類上使用任何虛函數(shù)會造成額外的負(fù)擔(dān),這些都是不能接受的。由于繼承一般都要用到一個或幾個虛函數(shù)(至少有一個虛的析構(gòu)函數(shù)),所以沒必要在小而頻繁使用的對象上使用任何繼承。當(dāng)然,繼承的層次越少越好,一般3~5層足夠。代碼優(yōu)化

MFC、Qt、WxWidget、C#、Java等都使用了單根的類繼承體系,顯然它們都會有上面的問題,而且用單根繼承來描述世界有點(diǎn)怪異,使用多繼承組合、概念泛化、基于對象的設(shè)計來描述世界就比較自然。關(guān)于C++類對象的內(nèi)存布局,參考書有:

《深入探索C++物件模型》代碼優(yōu)化函數(shù)/方法參數(shù)的設(shè)計盡可能使用簡單的類型,這樣可以減少額外的類方法的調(diào)用。若參數(shù)類型為類,在隱式類型轉(zhuǎn)換中就會調(diào)用構(gòu)造方法,造成額外的調(diào)用負(fù)擔(dān)。如:

voidFoo(std::string&str){…}

Foo(“aaa”);代碼優(yōu)化這里對Foo()的調(diào)用包括了對給定constchar*參數(shù)的構(gòu)造函數(shù)的調(diào)用。在一般的實現(xiàn)中,這個構(gòu)造函數(shù)執(zhí)行了一個malloc(),一個strlen(),以及一個memcpy()。由于該例子中的string沒有被更多的應(yīng)用,接著就調(diào)用了析構(gòu)函數(shù),即free()。這里的內(nèi)存分配完全是浪費(fèi),因為字符串“aaa”早就在程序的數(shù)據(jù)段中了,因而有它在內(nèi)存中的副本。如果Foo參數(shù)定義成constchar*,那么就沒有了上面所說的那些額外的調(diào)用。對應(yīng)的較好的形式如下:

voidFoo(constchar*str){…}代碼優(yōu)化inline關(guān)鍵字的優(yōu)化功能使用內(nèi)聯(lián)函數(shù)/方法可消除函數(shù)調(diào)用的系統(tǒng)開銷,但是內(nèi)聯(lián)太多代碼可能使應(yīng)用程序很大,致使虛擬內(nèi)存頁的錯誤數(shù)增加(即內(nèi)存頁交換也要時間開銷)。所以要檢查每個函數(shù)/方法,決定是否內(nèi)聯(lián),以防止生成的代碼膨脹。最終運(yùn)行效率以測量的時間為準(zhǔn)(如用性能監(jiān)視器

perfmon.exe)*。代碼優(yōu)化減少緩存未命中和頁錯誤

緩存未命中不論出現(xiàn)在內(nèi)部緩存中,還是出現(xiàn)在外部緩存中,都會降低程序的性能;而頁錯誤由于會轉(zhuǎn)到二級存儲中獲得程序指令和數(shù)據(jù),也會降低程序的性能。

為避免此問題,使用具有良好引用地址的數(shù)據(jù)結(jié)構(gòu)很重要,這意味著應(yīng)將相關(guān)的事物合在一起。有時看上去很棒的數(shù)據(jù)結(jié)構(gòu)由于引用地址不好而變得很糟,有時正好相反。代碼優(yōu)化

(1)動態(tài)分配的鏈接表可以降低程序性能,當(dāng)搜索項或者在表中遍歷到末尾時,每個跳過的鏈接都可能未命中緩存或?qū)е马撳e誤。基于簡單數(shù)組的表實現(xiàn)由于較好的緩存和較少的頁錯誤,實際上可能快得多,即使考慮到數(shù)組更難增長的事實,它仍然可能更快。代碼優(yōu)化

(2)使用動態(tài)分配的鏈接表的哈希表可能降低性能。通過擴(kuò)展,使用動態(tài)分配的鏈接表存儲內(nèi)容的哈希表的性能可能顯著降低。事實上,在最后的分析中,通過數(shù)組的簡單線性搜索實際上可能更快(取決于具體的情況)?;跀?shù)組的哈希表(所謂的“關(guān)閉散列”)是通常具有極佳的性能但卻經(jīng)常被忽略的實現(xiàn)。代碼優(yōu)化

(3)排序和查找也要注意容器內(nèi)元素的引用地址和預(yù)期其涉及的數(shù)據(jù)。元素的引用地址連續(xù)能提高運(yùn)行效率。

(4)內(nèi)存分配:盡量分配連續(xù)的內(nèi)存來使用。如果經(jīng)常執(zhí)行小的分配,可使用自定義分配策略,先分配大的內(nèi)存塊,然后使用自定義的Helper函數(shù)從該塊分配需要的小內(nèi)存。代碼優(yōu)化閱讀ATL和STL中的集合類(array,list,map,hash,red-blackmap)的實現(xiàn)代碼,并對比它們使用內(nèi)存的方式,分析其效率。再和數(shù)據(jù)結(jié)構(gòu)書上的集合類的實現(xiàn)進(jìn)行對比,以及對比自己的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)和C++課程作業(yè),并得到結(jié)論。代碼優(yōu)化

(5)較小的工作集(由模塊文件,即程序集大小來決定)意味著更好的引用地址、更少的頁錯誤和更多的緩存命中。進(jìn)程工作集是操作系統(tǒng)為測量引用地址而直接提供了最接近的尺度。

動態(tài)庫模塊盡量減少引出函數(shù)的數(shù)目,若確實較多,windows下可以通過def文件中對每個函數(shù)加NONAME屬性去掉函數(shù)名字,只存儲序數(shù),從而減小dll文件大小。代碼優(yōu)化對于確定不會拋出異常的函數(shù)和方法,在函數(shù)/方法定義的末尾加上throw()說明,可以讓編譯器優(yōu)化調(diào)用此函數(shù)/方法的代碼。此時要確保此函數(shù)/方法不拋異常,就要自己檢查函數(shù)體內(nèi)每個調(diào)用,可能拋出異常的,要自己在該函數(shù)/方法中捕獲該異常。如有可能,使用編譯器支持的內(nèi)部函數(shù)。不能忽略任何編譯信息,包括警告信息。代碼優(yōu)化整數(shù)運(yùn)算:

對于某些整數(shù)運(yùn)算,可以用與、或、非、移位、加減等操作來替代較耗時的乘法、除法、冪指數(shù)、對數(shù)等運(yùn)算。如:代碼優(yōu)化上面的代碼求得不小于無符號整數(shù)x的2的冪。一般的做法是需要做一個循環(huán),不斷除2到0,根據(jù)循環(huán)次數(shù)計算冪。而上述代碼顯然生成的代碼更簡單,運(yùn)算更快速。參考書:《高效程序的奧秘》代碼優(yōu)化配置編譯器優(yōu)化選項:

如:確保連接器配置為去除無用的函數(shù)和類,并設(shè)置為尺寸最小化而不是速度最大化(由于Cache命中的提高,會產(chǎn)生更好的運(yùn)行效果)(注意在使用這項設(shè)置時檢查instrinsic功能是否也處于打開狀態(tài)),浮點(diǎn)優(yōu)化選項等。

使用異常會對性能造成影響,所以要綜合考慮程序魯棒性和運(yùn)行效率,針對程序不同地方的不同要求做出合適的選擇。代碼優(yōu)化編譯選項若啟用運(yùn)行時類型信息(RTTI),編譯器會為每一個類產(chǎn)生一些靜態(tài)信息。RTTI一般來說是缺省啟用的,這樣我們的代碼可以調(diào)用dynamic_cast以及檢測一個對象的類型。可以考慮完全禁止使用RTTI和dynamic_cast以節(jié)省空間(有時候dynamic_cast在某些實現(xiàn)中需要付出很高的代價)。另一方面,當(dāng)真的需要有基于類型的不同行為的時候,增加一個不同行為的虛函數(shù)是更好的面向?qū)ο笤O(shè)計(注意static_cast與此不同,它的效率和C語言的類型轉(zhuǎn)換一樣)。代碼優(yōu)化注意開發(fā)時使用的庫以及第三方庫(如boost)、共享庫:要了解其系統(tǒng)開銷,有源代碼的可以調(diào)試其源代碼,若無,可用工具測量其性能是否滿足時間要

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論