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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

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

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

轉自:http://www.cnblogs.com/lizhenghn/p/3969531.html

1. 牢記Ahmdal定律

                  

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

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

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

3. 那些我認識的能夠寫出非常高效的代碼的人說,他們花費在優(yōu)化代碼上的時間是他們寫代碼時間的至少兩倍以上?

4. 跳轉/分支語句是昂貴的,不管何時盡可能的減少使用

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

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

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

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

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

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

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

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

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

9. 結構體(包括類)傳參時使用傳引用而不是傳值

  • 在光線追蹤程序中,哪怕是簡單如vector、points、colors等結構,我也沒有見過使用值傳遞的代碼

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

11. 盡可能避免使用轉型操作

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

12. 當定義C++對象時一定要小心

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

13. 使類的默認構造函數(shù)盡可能的輕量

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

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

15. 小心使用查表功能

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

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

  • 這類簡單操作其實需要創(chuàng)建一個匿名名的、臨時的中間對象;
  • 例如Vector v = Vector(1,0,0) + Vector(0,1,0) + Vector(0,0,1) 語句創(chuàng)建了5個未命名、臨時的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);
  • 稍微更好點的做法:Vector v(1,0,0); v+= Vector(0,1,0); v+= Vector(0,0,1); 這樣僅僅創(chuàng)建了2個臨時Vector:Vector v(1,0,0) 和Vector(0,0,1),而節(jié)省了6個函數(shù)調(diào)用(3個構造和3個析構);

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

18. 延遲局部變量的定義時間

  • 定義一個對象總會有一個函數(shù)調(diào)用開銷(就是構造函數(shù))
  • 如果一個對象只是有時候才被使用(比如在一個if語句內(nèi)部),那么就只在必要時才定義,因為這樣就只當這個變量使用時才會調(diào)用它的構造函數(shù)

19. 對于對象來說,使用前綴操作符(++obj),而不是后綴操作符(obj++)

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

20. 慎用模板

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

21. 在計算過程中避免動態(tài)內(nèi)存分配

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

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

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

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

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

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

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

25. 先在紙上簡化你使用的公式

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

26. 對于整數(shù)型、定點數(shù)、32位浮點數(shù)、64位浮點數(shù)來說,他們之間的差別并沒有你想象中的那么大

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

27. 考慮通過重寫你的數(shù)學公式來消除昂貴的操作

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

總結

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

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