C/C++ 不可不知道的程式設計技巧!第二部分
14. 如果可以的話,使用位移操作>>和<<來代替整數乘除法
15. 小心使用表查詢函式
許多人都鼓勵將複雜的函式(比如:三角函式)轉化為使用預編譯的查詢表。對於射線追蹤功能來說,這通常導致了不必要的記憶體查詢,這很昂貴(並不斷增長),並且這和計算一個三角函式並從記憶體中獲取值一樣快(尤其你考慮到三角查詢打亂了cpu的cache存取)。
在其他情況下,查詢表會很有用。對於GPU程式設計通常優先使用表查詢而不是複雜函式。
16. 對大多數類,優先使用+= 、 -= 、 *= 和 /=,而不是使用+ 、 – 、 * 、 和?/
這些簡單操作需要建立一個匿名臨時中間變數。
例如:Vector v = Vector(1,0,0) + Vector(0,1,0) + Vector(0,0,1);?建立了五個匿名臨時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);僅僅建立了兩個臨時Vector: Vector(0,1,0) 和 Vector(0,0,1)。這節約了6次函式呼叫(3次建構函式和3次解構函式)。
17. 對於基本資料型別,優先使用+?、?-?、?*?、?和?/,而不是+=?、?-=?、?*= 和 /=
18. 推遲定義本地變數
定義一個物件變數通常需要呼叫一次函式(建構函式)。
如果一個變數只在某些情況下需要(例如在一個if宣告語句內),僅在其需要的時候定義,這樣,建構函式僅在其被使用的時候呼叫。
19. 對於物件,使用字首操作符(++obj),而不是字尾操作符(obj++)
這在你的射線追蹤演算法中可能不是一個問題
使用字尾操作符需要執行一次物件拷貝(這也導致了額外的構造和解構函式呼叫),而字首的建構函式不需要一個臨時的拷貝。
20. 小心使用模板
對不同的是例項實現進行不同的優化。
標準模板庫已經經過良好的優化,不過我建議你在實現一個互動式射線追蹤演算法時避免使用它。
使用自己的實現,你知道它如何使用演算法,所以你知道如何最有效的實現它。
最重要的是,我的經歷告訴我:除錯STL庫非常低效。通常這也不是一個問題,除非你使用debug版本做效能分析。你會發現STL的建構函式,迭代器和其他一些操作,佔用了你15%的執行時間,這會導致你分析效能輸出更加費勁。
21. 避免在計算時進行動態記憶體分配
動態記憶體對於儲存場景和執行期間其他資料都很有用。
但是,在許多(大多數)的系統動態記憶體分配需要獲取控制訪問分配器的鎖。對於多執行緒應用程式,現實中使用動態記憶體由於額外的處理器導致了效能下降,因為需要等待分配器鎖和釋放記憶體。
即便對於單執行緒應用,在堆上分配記憶體也比在棧上分配記憶體開銷大得多。作業系統還需要執行一些操作來計算並找到適合尺寸的記憶體塊。
22. 找到你係統記憶體cache的資訊並利用它們
如果一個是資料結構正好適合一個cache行,處理整個類從記憶體中只需要做一次獲取操作。
確保所有的資料結構都是cache行大小對齊(如果你的資料結構和一個cache行大小都是128位元組,仍有可能因為你的結構體中的一個位元組在一個cache行中,而其他127位元組在另外一個cahce行中)。
23. 避免不需要的資料初始化
如果你需要初始化一大段的記憶體,考慮使用memset。
24. 儘早結束迴圈和儘早返回函式呼叫
考慮一個射線和三角形交叉,通常的情況是射線會越過三角,所以這裡可以優化。
如果你決定將射線和三角面板交叉。如果射線和麵板交叉t值是負數,你可以立即返回。這允許你跳過射線三角交叉一大半的質心座標計算。這是一個大的節約,一旦你知道這個交叉不存在,你就應該立即返回交叉計算函式。
同樣的,一些迴圈也應該儘早結束。例如,當設定陰影射線,對於近處的交叉通常都是不必須的,一旦有類似的的交叉,交叉計算就應該儘早返回。(這裡的交叉含義不太明白,可能是專業詞彙,譯者注)
25. 在稿紙上簡化你的方程式
許多方程式中,通常都可以或者在某些條件中取消計算。
編譯器不能發現這些簡化,但是你可以。取消一個內部迴圈的一些昂貴操作可以抵消你在其他地方的好幾天的優化工作。
26. 整數、定點數、32位浮點數和64位雙精度數字的數學運算差異,沒有你想象的那麼大
在現代CPU,浮點數運算和整數運算差不多擁有同樣的效率。在計算密集型應用(比如射線追蹤),這意味這可以忽略整數和浮點數計算的開銷差異。這也就是說,你不必要對算數進行整數處理優化。
雙精度浮點數運算也不比單精度浮點數運算更慢,尤其是在64位機器上。我在同一臺機器測試射線追蹤演算法全部使用double比全部使用floats執行有時候更快,反過來測試也看到了一樣的現象(這裡的原文是:I have seen ray tracers run faster using all doubles than all floats on the same machine. I have also seen the reverse)。
27. 不斷改進你的數學計算,以消除昂貴的操作
sqrt()經常可以被優化掉,尤其是在比較兩個值的平方根是否一致時。
如果你重複地需要處理 除x 操作,考慮計算1/x的值,乘以它。這在向量規範化(3次除法)運算中贏得了大的改進,不過我最近發現也有點難以確定的。不過,這仍然有所改進,如果你要進行三次或更多除法運算。
如果你在執行一個迴圈,那些在迴圈中執行不發生變化的部分,確保提取到迴圈外部。
考慮看看你的計算值是否可以在迴圈中修改得到(而不每次都重新開始迴圈計算)。
有想學習c/c++的小夥伴可以加群:941636044