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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

【C/C++】代码优化技巧

發(fā)布時(shí)間:2025/3/21 c/c++ 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C/C++】代码优化技巧 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)自:http://www.cnblogs.com/lizhenghn/p/3969531.html

1. 牢記Ahmdal定律

                  

  • funccost表示是函數(shù)func的運(yùn)行時(shí)間百分比,funcspeedup是你優(yōu)化后函數(shù)的運(yùn)行系數(shù);
  • 所以,如果函數(shù)TriangleIntersect()占用40%的運(yùn)行時(shí)間,而在你優(yōu)化后使它運(yùn)行快了兩倍,那么你的程序運(yùn)行能夠快了25%;
  • 這意味著不經(jīng)常使用的代碼不需要做過(guò)多優(yōu)化(或者完全不優(yōu)化),比如場(chǎng)景加載過(guò)程;
  • 也就是:讓頻繁調(diào)用的代碼運(yùn)行得更加高效,而讓較少調(diào)用的代碼保持運(yùn)行正確;

2. 先有正確的代碼,然后再做優(yōu)化

  • 這并不是說(shuō)先花8個(gè)周時(shí)間寫一個(gè)全功能的光線追蹤器,然后再花8個(gè)周去優(yōu)化;
  • 而是在你的管線追蹤程序中的多個(gè)階段都進(jìn)行優(yōu)化;
  • 如果代碼是正確的,而你又知道哪些函數(shù)會(huì)被頻繁的調(diào)用,優(yōu)化是很明顯的;
  • 然后找到瓶頸所在,并去除瓶頸(通過(guò)優(yōu)化或者算法改進(jìn))。通常來(lái)說(shuō)改進(jìn)算法可以很顯著地優(yōu)化瓶頸——甚至可能采用了一個(gè)你沒(méi)想到的算法。優(yōu)化那些你所知道的將被頻繁調(diào)用的函數(shù)是一個(gè)很好的做法;

3. 那些我認(rèn)識(shí)的能夠?qū)懗龇浅8咝У拇a的人說(shuō),他們花費(fèi)在優(yōu)化代碼上的時(shí)間是他們寫代碼時(shí)間的至少兩倍以上?

4. 跳轉(zhuǎn)/分支語(yǔ)句是昂貴的,不管何時(shí)盡可能的減少使用

  • 函數(shù)調(diào)用除了棧存儲(chǔ)操作外,還需要兩次跳轉(zhuǎn);
  • 優(yōu)先選擇迭代,而不是遞歸;
  • 如果是短函數(shù),使用內(nèi)聯(lián)來(lái)消除函數(shù)開銷;
  • 將循環(huán)放在函數(shù)內(nèi)(例如將for(i=0;i<100;i++) DoSomething();改為在DoSomething()內(nèi)做DoSomething());
  • 長(zhǎng)長(zhǎng)的if...else if...else if...else if...語(yǔ)句鏈需要大量的跳轉(zhuǎn)才能結(jié)束(除了在測(cè)試每個(gè)條件時(shí))。如果可能,改為switch語(yǔ)句,有時(shí)編譯器可以有優(yōu)化為在一個(gè)表中查找和單級(jí)跳轉(zhuǎn)。如果switch語(yǔ)句是不可能的,那把最經(jīng)常走到的if語(yǔ)句放在語(yǔ)句鏈開頭;

5. 考慮數(shù)組索引的順序

  • 兩維或更多維的數(shù)組在內(nèi)存中仍是按一維存儲(chǔ)的。這意思是array[i][j]和 array[i][j+1]是相鄰的(C/C++代碼),然而array[i][j]和array[i+1][j]卻可以相離的任意遠(yuǎn);
  • 訪問(wèn)物理內(nèi)存中的連續(xù)數(shù)據(jù),可以顯著加快你的代碼(有時(shí)是一個(gè)數(shù)量級(jí),甚至更多);
  • 現(xiàn)在CPU從主內(nèi)存中加載數(shù)據(jù)到高速緩存時(shí),它不僅僅是只加載單一數(shù)據(jù),而是加載一塊數(shù)據(jù),既包含了要請(qǐng)求的數(shù)據(jù),也包含部分相鄰數(shù)據(jù)(一個(gè)cache行)。這意思是說(shuō)如果array[i][j]在CPU緩存中,那么array[i][j+1]就很有可能也在緩存中了,然而array[i+1][j]可能仍在內(nèi)存中;

6.?考慮指令級(jí)并行性(IPL)

  • 盡管很多程序仍是單線程執(zhí)行,但現(xiàn)代的CPU已經(jīng)能夠在單核上有顯著的并行性。這意味著單CPU也可能同時(shí)執(zhí)行4個(gè)浮點(diǎn)數(shù)乘法、等待4個(gè)內(nèi)存請(qǐng)求,并執(zhí)行即將到來(lái)的分支比較操作
  • 為了充分利用這種并行性,代碼塊(比如在跳轉(zhuǎn)語(yǔ)句中)需要足夠的獨(dú)立指令來(lái)使CPU得到充分使用;
  • 可以考慮通過(guò)展開循環(huán)來(lái)改進(jìn);
  • 這也是使用內(nèi)聯(lián)函數(shù)的一個(gè)很好的原因;

7. 避免或減少局部變量的使用

  • 局部變量通常是存儲(chǔ)在棧上。如果很少,可以存儲(chǔ)在寄存器中。在這種情況下,函數(shù)不僅得到了對(duì)存儲(chǔ)在寄存器上的數(shù)據(jù)的更快內(nèi)存訪問(wèn)的好處,也可以避免建立一個(gè)棧幀的開銷;
  • 但是,也不要把所有對(duì)象都全盤聲明為全局變量;

8. 減少函數(shù)參數(shù)的個(gè)數(shù)

  • 和減少局部變量的原因一樣——他們也是在棧上存儲(chǔ)的;

9. 結(jié)構(gòu)體(包括類)傳參時(shí)使用傳引用而不是傳值

  • 在光線追蹤程序中,哪怕是簡(jiǎn)單如vector、points、colors等結(jié)構(gòu),我也沒(méi)有見(jiàn)過(guò)使用值傳遞的代碼

10. 如果你不需要一個(gè)函數(shù)的返回值,那就不要返回

11. 盡可能避免使用轉(zhuǎn)型操作

  • 整數(shù)和浮點(diǎn)數(shù)的指令集通常在不同的寄存器上運(yùn)算,因此轉(zhuǎn)型操作需要拷貝操作;
  • 短整形(char和short)仍然需要一個(gè)全尺寸的寄存器,而且在存儲(chǔ)回內(nèi)存之前,它們需要對(duì)齊到32位或64位上,然后才轉(zhuǎn)換成更小尺寸類型;

12. 當(dāng)定義C++對(duì)象時(shí)一定要小心

  • 使用初始化(Color c(black))而不是賦值(Color c, c = black),而前者更快;

13. 使類的默認(rèn)構(gòu)造函數(shù)盡可能的輕量

  • 特別是那簡(jiǎn)單的、經(jīng)常使用的類(例如,顏色,矢量,點(diǎn)等);
  • 這些默認(rèn)構(gòu)造函數(shù)通常是在你不注意時(shí)就調(diào)用,甚至那時(shí)你并不希望這樣;
  • 使用構(gòu)造初始化列表(使用Color::Color() : r(0), g(0), b(0) {}而不是Color::Color() { r?= g = b = 0; } );

14. 盡可能使用移位操作符>>和<<,而不是整數(shù)乘法和除法

15. 小心使用查表功能

  • 很多人鼓勵(lì)對(duì)于復(fù)雜的功能(例如,三角函數(shù))使用預(yù)先計(jì)算過(guò)值的查表法。對(duì)于光線跟蹤程序來(lái)說(shuō),這往往是不必要的。內(nèi)存查找是非常(日益)昂貴的,而且重新計(jì)算三角函數(shù)往往和從內(nèi)存中查找值一樣快(尤其是當(dāng)你考慮到內(nèi)存查找會(huì)影響CPU緩存命中率時(shí));
  • 在其它情況下,查表可能是非常有用的。比如在GPU編程中,查表法通常是復(fù)雜功能的優(yōu)先選擇;

16. 對(duì)于大多數(shù)的類類型,使用運(yùn)算符 +=,-=,*=和/=,而少用+,-,*,/

  • 這類簡(jiǎn)單操作其實(shí)需要?jiǎng)?chuàng)建一個(gè)匿名名的、臨時(shí)的中間對(duì)象;
  • 例如Vector v = Vector(1,0,0) + Vector(0,1,0) + Vector(0,0,1) 語(yǔ)句創(chuàng)建了5個(gè)未命名、臨時(shí)的Vector:Vector(1,0,0), Vector(0,1,0),Vector(0,0,1),Vector(1,0,0) + Vector(0,1,0),以及Vector(1,0,0) + Vector(0,1,0) + Vector(0,0,1);
  • 稍微更好點(diǎn)的做法:Vector v(1,0,0); v+= Vector(0,1,0); v+= Vector(0,0,1); 這樣僅僅創(chuàng)建了2個(gè)臨時(shí)Vector:Vector v(1,0,0) 和Vector(0,0,1),而節(jié)省了6個(gè)函數(shù)調(diào)用(3個(gè)構(gòu)造和3個(gè)析構(gòu));

17. 對(duì)于基本數(shù)據(jù)類型,使用運(yùn)算符+,-,*,/,而少用+=,-=,*=和/=

18. 延遲局部變量的定義時(shí)間

  • 定義一個(gè)對(duì)象總會(huì)有一個(gè)函數(shù)調(diào)用開銷(就是構(gòu)造函數(shù))
  • 如果一個(gè)對(duì)象只是有時(shí)候才被使用(比如在一個(gè)if語(yǔ)句內(nèi)部),那么就只在必要時(shí)才定義,因?yàn)檫@樣就只當(dāng)這個(gè)變量使用時(shí)才會(huì)調(diào)用它的構(gòu)造函數(shù)

19. 對(duì)于對(duì)象來(lái)說(shuō),使用前綴操作符(++obj),而不是后綴操作符(obj++)

  • 在你的光線追蹤程序中,這可能并不是個(gè)問(wèn)題
  • 對(duì)象的拷貝操作必須使用后綴操作符(這需要額外調(diào)用一個(gè)構(gòu)造和一個(gè)析構(gòu)函數(shù)),而前綴操作符并不產(chǎn)生臨時(shí)對(duì)象

20. 慎用模板

  • 各種具現(xiàn)化實(shí)例的優(yōu)化方式可能是不同的;
  • 標(biāo)準(zhǔn)模板庫(kù)(STL)做了很好的優(yōu)化,但如果你打算實(shí)現(xiàn)交互式光線跟蹤器,最好是仍避免使用;
  • 通過(guò)自己實(shí)現(xiàn),你能清楚地明白要它使用的算法,你就會(huì)知道最有效的使用方式;
  • 更重要的是,我的經(jīng)驗(yàn)表明調(diào)試、編譯STL會(huì)很慢。通常這也是沒(méi)問(wèn)題的,除非你使用Debug版本進(jìn)行性能分析。你會(huì)發(fā)現(xiàn)STL的構(gòu)造、迭代器等操作會(huì)占用運(yùn)行時(shí)間的15%以上,它會(huì)使輸出的分析結(jié)果更為混亂

21. 在計(jì)算過(guò)程中避免動(dòng)態(tài)內(nèi)存分配

  • 動(dòng)態(tài)內(nèi)存主要優(yōu)勢(shì)在于存儲(chǔ)場(chǎng)景數(shù)據(jù)和其他數(shù)據(jù),而不是在計(jì)算過(guò)程中進(jìn)行修改
  • 然而,在許多(大多數(shù))時(shí)候系統(tǒng)動(dòng)態(tài)存儲(chǔ)分配要求使用鎖來(lái)控制訪問(wèn)分配器。對(duì)于使用動(dòng)態(tài)內(nèi)存的多線程應(yīng)用程序來(lái)說(shuō),由于需要等待分配和釋放內(nèi)存,通過(guò)這些額外的處理,你可能實(shí)際上得到的是一個(gè)更慢的程序
  • 即使在單線程程序中,在堆上分配內(nèi)存也比在棧上分配更昂貴。操作系統(tǒng)需要進(jìn)行一些計(jì)算來(lái)確定所需大小的內(nèi)存塊。

22. 發(fā)現(xiàn)和充分利用有關(guān)你的系統(tǒng)內(nèi)存Cache的有用信息

  • ü? 如果一個(gè)數(shù)據(jù)結(jié)構(gòu)大小恰好填滿一個(gè)Cache行,處理整個(gè)類只需要從內(nèi)存中讀取一次;
  • ü? 確保所有的數(shù)據(jù)結(jié)構(gòu)都能對(duì)齊到Cache邊界(如果你的數(shù)據(jù)大小和Cache都是128字節(jié),那么當(dāng)1個(gè)字節(jié)在一個(gè)Cache行而另外127字節(jié)在第二個(gè)Cache行時(shí),那么性能仍然不好)

23. 避免不必要的數(shù)據(jù)初始化

  • 如果你要初始化一大塊內(nèi)存,考慮用memset()函數(shù)

24. 盡量提早結(jié)束循環(huán)判斷和函數(shù)返回

  • 考慮射線和三角形相交。常見(jiàn)情況是射線和三角形不相交,因此這里可以優(yōu)化;
  • 如果你要判斷射線和三角形相交的情況,一旦t值射線平面為負(fù),你可以立即返回。這樣可以使你跳過(guò)大約一半的光線三角形交叉點(diǎn)的重心坐標(biāo)計(jì)算。一個(gè)巨大的勝利!一旦你確定沒(méi)有相交發(fā)生,求交函數(shù)就應(yīng)該退出
  • 同樣的,一些循環(huán)也可以被提早結(jié)束。例如,在光線陰影設(shè)置中,最近的相交是不必要的。只要發(fā)現(xiàn)了任何交叉閉環(huán),求交函數(shù)就可以返回

25. 先在紙上簡(jiǎn)化你使用的公式

  • 在很多公式中,總是可以或者一些特殊情況下,可以取消計(jì)算
  • 編譯器找不到這些簡(jiǎn)化,但是你可以。消除一些內(nèi)在循環(huán)中的昂貴操作可以比你在其他地方的優(yōu)化更能加速你的程序

26. 對(duì)于整數(shù)型、定點(diǎn)數(shù)、32位浮點(diǎn)數(shù)、64位浮點(diǎn)數(shù)來(lái)說(shuō),他們之間的差別并沒(méi)有你想象中的那么大

  • 現(xiàn)代CPU進(jìn)行浮點(diǎn)運(yùn)算和整數(shù)運(yùn)算其實(shí)有相同的運(yùn)算吞吐量,像光線追蹤這種計(jì)算密集型的程序,這意思是整數(shù)和浮點(diǎn)運(yùn)算成本之間的差異可以忽略不計(jì),這意味著你不需要做一些優(yōu)化來(lái)使用整數(shù)運(yùn)算;
  • 雙精度浮點(diǎn)運(yùn)算并不一定比單精度浮點(diǎn)計(jì)算更慢,尤其是在64位機(jī)器上。我曾經(jīng)在同一臺(tái)機(jī)器上測(cè)試光線追蹤算法,結(jié)果是有時(shí)全部使用double比全部使用float會(huì)運(yùn)行得更快,

27. 考慮通過(guò)重寫你的數(shù)學(xué)公式來(lái)消除昂貴的操作

  • sqrt()函數(shù)通常是可以避免的,尤其是在比較數(shù)值的平方是否相等時(shí);
  • 如果你需要反復(fù)除以x,考慮計(jì)算1/x,然后相乘。在向量的歸一化操作中(3次除法),這曾經(jīng)是一個(gè)很大的優(yōu)化,但最近我發(fā)現(xiàn)這很難說(shuō)。然而如果你整除更多次數(shù),這樣做仍是有益的;
  • 如果你執(zhí)行一個(gè)循環(huán)操作,將那些在循環(huán)中固定不變的計(jì)算移出到循環(huán)外。

總結(jié)

以上是生活随笔為你收集整理的【C/C++】代码优化技巧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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