contributed by <carolc0708
>
參考陳昱齊同學的筆記,將gprof的操作作淺顯易懂的解說:
一般操作
step1: $ gcc -pg 檔名.c
執行檔檔名(ex. file)
此時會產生一個 名叫做file的執行檔
step2: $ ./file
執行file,接著會產生一個gmon.out文件,用來供gprof分析用的
step3: $ gprof -b file gmon.out | less
此時就可以看到gprof分析程式,每個函數所花的時間,可以針對時間花費最多的進行改善
先在source code底下執行$ make PROFILE=1
產生raytracing執行檔,
再執行 $ ./raytracing
,接著會產生gmon.out文件。
最後執行 $ gprof -b raytracing gmon.out | head -n 20
,列出gprof分析程式中花費時間最高的20筆,結果如下:
由結果可知,我們必須對前幾個執行次數高、執行時間長的函式,如dot_product()
、subtract_vector()
、multiply_vector()
等進行效能的改善。
關於執行時間,在沒有使用gprof來追蹤程式(直接$ make
來產生raytracting執行檔),程式執行時間為 2.433547 sec
,而使用gprof來追蹤程式後,程式執行時間為 5.019975 sec
。
先在花費總時間最高的dot_product()
做loop unrolling:
將for迴圈線性展開:
改完後,執行時間由原本的 5.019975 sec
降為 4.675378 sec
,減少了 0.34 sec
左右。
接著對 <math-toolkit.h>
中其他也有相同狀況的函式add_vector()
、subtract_vector()
、multiply_vectors()
、multiply_vector()
進行改良:
改完之後,程式執行時間降為 4.191883 sec
,比完全未改善前降低了約 0.83 sec
參考raytracing共筆中提到 Inline function的展開
Inline function背景知識:
在程式當中,直接將函式內容展開,省去執行時需額外跳至該函式記憶體位置再跳回來的時間。
即便加入inline關鍵字,在程式執行時也不一定會實作。編譯器會去選擇這樣的實作方式是否符合效益、有節省到時間。
一般來說,函數中的程式碼或處理較短,並且常呼叫使用的話,可以給他加上個inline(因為編譯器可能會應你的要求使用)
static inline
&inline
&extern inline
等讀完多份文件再回來補資料><Carol Chen
Inline function觀念
[C++]內嵌函數(inline function)筆記
Inline v.s Macro
Inline function in C
Static Inline v.s External Inline
__attribute__((always_inline))
,強行讓CPU去執行inline這個“建議”這邊是否每一個都要強制展開不是很確定?!Carol Chen
執行結果,出現了warning@@
但執行時間明顯下降,只花了2.128363 sec
,比只做loop unrolling下降了2.07 sec
。
Directive
、Clause
、Function
:
#pragma omp <directive> [clause]
parallel
、sections
、for
這三項。ordered
是用來設定平行化的執行順序。
for
: 把程式中的for迴圈分給scope上#pragma omp parallel
所生出的threads去執行section
: 把程式中沒有相關性的各個程式利用 #pragma omp section
來做區塊切割,然後由 #pragma omp parallel sections
分出的threads去做平行的處理。定義方式和for
稍不同,詳細參考這裡。OpenMP note
OpenMP wiki
OpenMP簡易的程式平行化:(三)範例 parallel、section
程式需 #include <omp.h>
Makefile中的 CFLAGS
加上 -fopenmp
,LDFLAGS
加上-lgomp
在有for迴圈的地方都做平行化處理,有兩種方式,效果相同:
#pragma omp parallel for
加在for迴圈上方#pragma omp parallel
加在要平行處理的scope{}
上方,#pragma omp for
加在裏頭的for迴圈上方。在 math-toolkit.h
中修改有for迴圈的地方,
執行結果為4.974464 sec
,比優化前版本快 0.1 sec
左右而已。判斷應該是for迴圈執行次數太少且只有一層,無法達到很大的優化效果。
raytracing.c
中修改raytracing()
函式:為什要這樣寫?Carol Chen
- private(): 讓每個執行緒中,都有一份變數的複本,以免互相干擾
- raytracing共筆中提到,使用private( )對stk、d和object_color進行處理的原因:在迴圈中的變數有非常多,但是可以從程式碼中看到以下兩行,他們的值事先就已經好,他們的值在進入迴圈之前就已進行過一次運算,並且在迴圈內並沒有任何一行指令會去改變他們的值,所以可想而知它們的值是不會改變的。所以只剩下stk、d和object_color這三個變數了。
直接執行程式對raytracing()
改善,結果為5.004982 sec
,無太大差別。但加上前面實作的loop unrolling和force inline,時間可降為2.098089 sec
。
section
的方式實作,修改dot_product()
:實驗結果為執行時間4.675069 sec
,比優化前降低 0.4 sec
,比只做for
的平行好一點點。
背景知識:(參考謝永勁的筆記和wiki)
運算方式:
一個float[4],他的位置是連續的,那麼SIMD就可以將float按照位置分別做運算,就如同下面的圖片,他能一次運算4個float,以達到加速的效果,但是需要注意的是SIMD只支援同一種運算的方式,也就是說如果你想要用不同的方式對A0,A1,A2,A3進行運算是不行的,可能就要找找看MIMD
資料儲存型態:
C的資料型態被稱作scalar types
(ex: int ,char ,float …),然而在SIMD裡面相對應的scalar是使用vector type
來做儲存的
CFLAG
加-mavx2
,並加入 #include<immintrin.h>
想參考以下看是否能夠改善Carol Chen
SIMD Ray Tracing Tips
pthread_create
int pthread_create(pthread_t *tid , const pthread_attr_t *attr , void *(*function)(void *) , void *argument)
這個Function的作用是用來產生一個Thread並執行附帶的Function,附帶有4個參數
參數1. pthread_t *tid為pthread的指標,在使用Thread之前必須要先宣告一個pthread_t的變數。
參數2. const pthread_attr_t *attr為該Thread的屬性,預設是NULL,如果沒有其他特殊的需求直接填入NULL即可。
參數3. void *(*function)(void *)為Function pointer,這邊要放入你要執行的Function。
參數4. void *argument為Function pointer所要帶的參數。
回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
example: pthread_create( &thread1, NULL , showmessage , message);
pthread_join
int pthread_join (pthread_t thread, void **value_ptr)
這個Function的作用會暫停目前執行pthread_join的Thread,等到目標Thread執行完畢之後目前執行pthread_join的Thread才會繼續執行,附帶有2個參數。
參數1: pthread_t thread為要等待的目標Thread。
參數2: void **value_ptr用來取得目標Thread的回傳值。
回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
raytracing()
中實作PThread:先看原始的raytracing()
:
可以將height或width拆成給不同Thread去執行,加快運算速度。
筆記中有提到均勻分column方式的效能比較,想多了解再來嘗試看看差異Carol Chen
在raytracing.h
中,重新設計資料結構,將Thread當中要分別下去跑的變數一起包成一個struct Thread_Content
:
Thread_Arg
定義Thread的參數,其中包含Thread_Content
指標:
接下來在raytracing.c
由raytracing()
另外分出兩個函式,分別處理Thread中的運算與Thread平行運算兩部分。
處理Thread中的運算:
處理Thread平行運算:
不同的THREAD_NUM
執行結果如下:
可以發現我的電腦開512 Threads時,效能最好。
筆記中還有很多切Thread的方式,有空時可以研究一下Carol Chen