contributed by <tina0405
>,<zhanyangch
>,<LinRiver
>
make PROFILE=1
重新編譯程式碼,並且學習 gprofmake PROFILE=1
是因為 Makefile 裡定義,而-pg
的用法在下方GNU gprof 的原理提到GNU gprof 的原理,參考 GNU gprof
分析步驟 :
-pg
的這個選項應該要跟著指令一起編譯和連結-pg
是編譯和連結的選項,如果沒使用就沒有 call-graph datagmon.out
的檔案gprof
去分析資料通常在編譯時加入 -pg
, 就代表著每一個函式會去呼叫 mcount
(或 _mcount
, 或 __mcount
, 依靠作業系統和編譯器) 做為第一個操作之一
包含 profiling library 的 mcount 程序,通常利用檢查 stack frame 去找 child 的地址和返回 original parent 的地址
frompc
和 selfpc
呼叫 __mcount_internal
, __mcount_internal
是負責維護 in-memory call graph首先必須先安裝 3 個套件 , pip 是因為安裝 gprof2dot
會用到
./raytracing
將分析資訊儲存在 gmon.out
檔案中gmon.out
就可以執行 gprof 分析 gprof -p ./raytracing
math-toolkit.h
的函式實做PROFILE=1
,跑 10 次的結果: 6.007 秒Loop Unrolling 本身是一個相當基礎性的編譯器最佳化技術, 雖然他的方法看似直接了當, 但執行上具備相當大的成效. 其基本的觀念是把 for 迴圈中的表示式線性展開, 並且在每一次迭代中存取線性數據中的一小組來取代單獨一個. 如此一來程式在執行時可讓執行迭代次數減少.
以下為一針對彩色圖片取補色之程式來進行 Loop Unrolling, 其中 BYTE * pixel 為預處理之圖片像素數據, BYTE * tempPixel 為存放已處理之圖片, int width 和 height 為預處理圖片之寬與高
接著我們嘗試將迴圈次數變為原來三分之一, 因為每一像素具有 R, G, B 三分量, 透過以下改寫
然而需要特別注意的是, 根據預處理的數組的長度來選擇相對應適當的線性展開性. 例如增加暫存器儲存迭代的暫時變數, 因而導致效能降低. 除此之外, 更應考慮此展開性是否對時間與空間之開銷比例的影響
dot_product
跑 10 次平均的結果 5.473391143 秒,比原版減少 0.5 秒subtract_vector
跑 10 次平均的結果 5.277608638 秒,再減少 0.2 秒multiply_vector
跑 10 次的平均 5.277 秒,只減少 0.09dot_product
:#define dot_product(a,b) ((a[0]*b[0])+(a[1]*b[1])+(a[2]*b[2]))
dot_product
+subtract_vector
unrolling )快了 1.1 秒subtract_vector
: 時間再加快 0.8 秒dot_product
和 subtract_vector
已經消失於圖中(函式)
改為 macro 的結果是時間減少但 cache-misses rate 增加,時間的提升原因來自於 69646433 次的dot_product
呼叫 + 56956357 次的 subtract_vector
呼叫
這個實驗中想要比較的是 macro 和 function 的差別
參考前置處理器:
#
符號
可是即使時間從原本 6.007 變成 3.302 , 加快 45% 但和最佳化後的 0.741 秒相差甚遠,因此再往 SIMD 和多執行緒的方面前進
解釋裡面常用指令,參考 Intel Intrinsics Guide 這邊所使用的是 SSE2(一次可操作兩組雙精度符點數做運算)
__m128d _mm_loadu_pd (double const* mem_addr)
__m128d _mm_mul_pd (__m128d a, __m128d b)
__m128d _mm_add_pd (__m128d a, __m128d b)
dot_product
來實測效能,所做的事其實就是 __attribute__((always_inline))
建議 CPU 執行它dot_product
會像:dot_product
程式碼部份:dot_product
部份,跑 10 次(都有 PROFILE=1)
dot_product
(unrolling + macro) : 4.176112849 secondsdot_product
(sse) : 4.317184400 secondsdot_product
來實測效能:參考 hugikun999 的共筆
以往在單核心處理器時期, 透過作業系統提供的 API 來建立執行緒. 然而在多核心的架構下, 為了讓各個處理器都能夠妥善被使用, 進而使用多執行緒程式設計來達到此目標. 透過 OpenMP 能夠取代使用作業系統之 API 來建立執行緒, 其中有以下三個特點:
CPU 核心數量的擴展性
因為多核心程式設計時是需要考慮到程式效能會隨 CPU 數目增加而有所不同. 這其中要求程式中所建立的執行緒數量需要隨著 CPU 數目改變而變化. 然而使用 OpenMP 無須擔心此 CPU 數目擴展問題
便利性
使用 OpenMP 可將同一函式中的程式分成多個執行緒進行, 亦可將一 for 迴圈分解成多個執行緒進行
可移植性
各個主流的作業系統之執行緒 API 是互不相容, 然而 OpenMP 本身即是標準規範, 對於支持他的編譯器都是執行同一標準, 具備可移植性之特性
然而有幾項是使用 OpenMP 常見的錯誤使用:
POSIX 執行緒, 亦可縮寫成 Pthreads, 為 POSIX 的執行緒標準, 定義了創建和操作執行緒的的一套 API, 一般用於 Unix-like POSIX 系統. 其 API 稱作 Pthreads, 大致共有100個函數調用且分為四類, 並以 'pthread_' 開頭:
make PROFILE=1
重新編譯程式碼,並且學習 gprof
math-toolkit.h
在內的函式實做,充分紀錄效能差異在共筆
-O0
(關閉最佳化)math-toolkit.h
定義的若干數學操作函式很重要,可參考 Investigating SSE cross product performance、MathFu 原始程式碼,以及 2015q3 Homework #1 Ext