HuangWenChen
```
方便閱讀linux分析工具-gprof 使用Gnu gprof進行程式分析 gprof - linux program profiling tool
gprof是一種GNU profiler**工具,**可以顯示程式進行的;"flatprofile",包含程式執行時各個function的執行時間以及call次數,可以幫助分析程式找到效能瓶頸,加以優化
原理:
使用步驟:
gprof execution_file
分析這些數據,做成使用者好讀取的型式執行:
老師幫我們在Makefile中包好了,如果再編譯時定義PROFILE為1,就會加入 -pg參數
執行時間為7.74秒,有生成gmon.out,接著用gprof去分析,-b這個參數代表不顯示統計圖的文字敘述,再把結果導入 result.txt
(gprof 指令也可以加入data檔: gprof raytracing gmon.out ,這裡沒加是因為直接使用預設名稱)
執行結果:
<undefined style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; letter-spacing: normal;">* flat profile</undefined>
呈現每個function所耗的時間以及call的次數,由此可知前幾個函式單一執行時間雖然小於0.01秒,卻都執行了1千萬次以上,所以如果要對單一函數做優化,當然是以他們優先,就算只有些微優化,再總時間上就會很明顯的影響:
每次結果,執行時間排序都有些微不同,但是dot_product都是最久的,其餘大於1千萬calls且時間較長的為
dot_product , subtract_vector , multiply_vector rayRectangularIntersection normalize add_vector raySphereIntersection
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
28.47 1.34 1.34 69646433 0.00 0.00 dot_product
14.23 2.01 0.67 56956357 0.00 0.00 subtract_vector
11.26 2.54 0.53 31410180 0.00 0.00 multiply_vector
8.92 2.96 0.42 13861875 0.00 0.00 rayRectangularIntersection
7.65 3.32 0.36 10598450 0.00 0.00 normalize
6.16 3.61 0.29 17836094 0.00 0.00 add_vector
5.10 3.85 0.24 13861875 0.00 0.00 raySphereIntersection
3.61 4.02 0.17 4620625 0.00 0.00 ray_hit_object
3.40 4.18 0.16 17821809 0.00 0.00 cross_product
1.91 4.27 0.09 4221152 0.00 0.00 multiply_vectors
1.91 4.36 0.09 1 0.09 4.71 raytracing
1.70 4.44 0.08 1048576 0.00 0.00 ray_color
…
疑點:我看manual裡,有說到mcount跟profil這兩個內部function,但是這裡卻沒有顯示出來
<undefined style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; letter-spacing: normal;">* call graph</undefined>
*函式叫用表,左邊為function編號,右邊為有叫用此function的parent,編號可以在最後的索引中找到相對函式
Call graph
granularity: each sample hit covers 2 byte(s) for 0.22% of 4.49 seconds
index % time self children called name
[1] 100.0 0.00 4.49 main [1]
0.05 4.43 1/1 raytracing [2]
0.01 0.00 1/1 delete_sphere_list [21]
0.00 0.00 3/3 append_sphere [30]
0.00 0.00 3/3 append_rectangular [29]
0.00 0.00 2/2 append_light [31]
0.00 0.00 1/1 write_to_ppm [35]
0.00 0.00 1/1 delete_rectangular_list [33]
0.00 0.00 1/1 delete_light_list [32]
0.00 0.00 1/1 diff_in_second [34]
0.05 4.43 1/1 main [1]
[2] 99.8 0.05 4.43 1 raytracing [2]
0.10 4.17 1048576/1048576 ray_color [3]
0.02 0.14 1048576/1048576 rayConstruction [15]
0.00 0.00 1/1 calculateBasisVecto
…
用虛線切開的算一個entry,一個entry代表所有跟某個function有關的呼叫,我們用第二個來看,以[X]開頭的這行是primary line,在name欄位的就是這個entry的主角,以上為callers line,以下為subroutine line(children)
用第二個entry舉例:
primary line:
index % time self children called name
[2] 99.8 0.05 4.43 1 raytracing [2]
%time:佔所有時間比例(含call function)
self:執行時間(不含call) =flat profile的時間
children:function call時間
called被呼叫幾次(如果有兩個數字"A+B" A是被外部呼叫,B是遞迴呼叫(自己呼叫)
callers line:
index % time self children called name
0.05 4.43 1/1 main [1]
self&childen:同primay
called:呼叫raytracing的函式,佔的比例(不包含遞迴)
name:如果caller無法被定義,就印<spontaneous>,ex.main
subroutine line
index % time self children called name
0.10 4.17 1048576/1048576 ray_color [3]
0.02 0.14 1048576/1048576 rayConstruction [15]
0.00 0.00 1/1 calculateBasisVecto
self:由父函式呼叫的子函式總執行時間
children:由父函式呼叫時,子函式呼叫別人所花的總時間
called:子函式被父函式呼叫的比例
要閱讀這個graph其實不容易,如果想要簡易明瞭的_gprof2dot_
https://github.com/jrfonseca/gprof2dot
<undefined style="color: rgb(0, 0, 0); font-family: Helvetica; font-size: medium; letter-spacing: normal;">* Index by function name</undefined>
函式編號
Index by function name
[11] add_vector (math-toolkit.h) [22] fresnel (raytracing.c) [15] rayConstruction (raytracing.c)
[31] append_light [24] idx_stack_empty (idx_stack.h) [5] rayRectangularIntersection (raytracing.c)
[29] append_rectangular [27] idx_stack_init (idx_stack.h) [7] raySphereInterse
…
以下為沒有加入-pg編譯的執行時間
目標就是把時間降低吧~
math-toolkit.h:
由gprof可知raytracing的熱點為: dot_product , subtract_vector , multiply_vector rayRectangularIntersection normalize add_vector raySphereIntersection
math-toolkit.h:
dot_product , subtract_vector , multiply_vector normalize add_vector
raytracing.c:
rayRectangularIntersection raySphereIntersection ->無規則非迴圈
原版
執行時間:
gprof:
Loop Unrolling
為了減少loop的預測錯誤,展開loop內容,減少loop次數,會因此而縮短執行時間,卻也會讓程式體積變大,是一種以空間換取時間的方式
舉例:dot_product
把for loop拆開:
能實現優化的函式:add_vector, subtract_vector, multiply_vectors, multiply_vector , dot_product
執行時間:
gprof分析
在計組有學過prediction,記得loop的prediction會預測forward,所以可以減少猜錯,或是用1-2 bits,去記憶forward或backward,但是因為這裡只有loop3次,沒辦法很確定看出在-O0時式採用哪一種,所以要用一個大一點的for loop來驗證
如果要將大forloop做loop unrolling,可以使用Duff’s device,會是一個較簡潔的辦法
** inline 函數**
在 math-toolkit.h 中,每個函數前面都有 static inline
Static:
因為這個.h檔可能被多個檔案引用,若不設為static,會造成在linking成執行檔時發生找到多個重複function的錯誤,所以宣告為靜態數static,只讓function在header裡能被找到,減少錯誤,
Inline:
inline 是一個keyword,提醒compiler可以將function本體直接填入呼叫該function的位置
優點為:
減少呼叫function的overhead,如參數傳遞、回傳值處理等。
切換到function會jump,也就是會有branch的行為。因為CPU的pipeline和cache的特性, 會影響效能。
經由代換程式碼,也許可以增加最佳化的可能性。
知道了 inline 是建議編輯器在函數很小的時候可以直接內嵌進程式碼,藉此減少呼叫函數的 overhead 。
因為是「建議」,所以編輯器可以忽略你的建議,此外,這個case老師規定編器不能開最佳化,要用-O0,這時即使被宣告inline,任何函數都不會被展開,所以要強制編譯器 inline
在程式碼中加入 __attribute__ ((always_inline))
來達到強制 inline
能實現優化的函式:all
執行時間:
gprof分析
跟原版 相比math-toolkit.h裡的funtion都不見了,其餘的時間都增加了,明顯就是展開後,就不算是function call了,時間都分攤在caller上,所以減少的時間就是function call造成的overhead
**POSIX Thread **
使用SIMD指令
OPENMP
以 make PROFILE=1 重新編譯程式碼,並且學習 gprof
以 gprof 指出效能瓶頸,並且著手改寫檔案 math-toolkit.h 在內的函式實做,充分紀錄效能差異在共筆
注意: 請勿更動編譯器最佳化選項 -O0 (關閉最佳化)
檔案 math-toolkit.h 定義的若干數學操作函式很重要,可參考 SIMD optimized dot and cross product functions 和 2015q3 Homework #1 Ext
可善用 POSIX Thread, OpenMP, software pipelining, 以及 loop unrolling 一類的技巧來加速程式運作
將你的觀察、分析,以及各式效能改善過程,並善用 gnuplot 製圖,紀錄於「作業區」,記得標注「開發紀錄(A)」