# 2016q3 Homework1 (raytracing) contributed by <`petermouse`> ### Reviewed by <`vic85821`> * 建議以圖表呈現結果較明顯 * 可以比較看看inline function跟Macro的差異 * commit [5705101](https://github.com/petermouse/raytracing/commit/5705101b8945eec441b06fe6f6f87e01c6f5908e) 可以再明確的說明更改內容及目的 * 使用astyle幫助檢查coding style ## 開發環境 * OS:Lubuntu 16.04 LTS * Memory: 8 GB * CPU: * Name: * Intel(R) Core(TM) i5-4200H CPU @ 2.80GHz * Cache: * L1d Cache: 32K * L1i Cache: 32K * L2 Cache: 256K * L3 Cache: 3072K ## 使用GNU Gprof GNU Gprof:可以幫分析程式在執行期間各個部份(函式)所消耗的時間。 為了使raytracing使用Gprof,make時附加參數`PROFILE=1`,`Makefile`的`ifeq`成立,`CFLAGS`(編譯參數)與`LDFLAGS`(連結參數)將append`-pg`,。 `-pg`:建立call-graph data。 執行raytracing ``` # Rendering scene Done! Execution time of raytracing() : 5.190075 sec ``` gprof分析 ``` gprof -b raytracing gmon.out ``` `-b`:簡化說明 [其他參數](https://sourceware.org/binutils/docs/gprof/Output-Options.html#Output-Options) 部份結果 ``` Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 24.75 0.47 0.47 69646433 0.00 0.00 dot_product 24.75 0.94 0.47 56956357 0.00 0.00 subtract_vector 7.64 1.09 0.15 4620625 0.00 0.00 ray_hit_object 6.85 1.22 0.13 13861875 0.00 0.00 rayRectangularIntersection 6.58 1.34 0.13 31410180 0.00 0.00 multiply_vector 6.32 1.46 0.12 17836094 0.00 0.00 add_vector 6.32 1.58 0.12 10598450 0.00 0.00 normalize 4.74 1.67 0.09 17821809 0.00 0.00 cross_product 3.16 1.73 0.06 13861875 0.00 0.00 raySphereIntersection 2.63 1.78 0.05 2110576 0.00 0.00 compute_specular_diffuse 2.11 1.82 0.04 1048576 0.00 0.00 ray_color 1.58 1.85 0.03 2110576 0.00 0.00 localColor 1.32 1.88 0.03 4221152 0.00 0.00 multiply_vectors 0.53 1.89 0.01 3838091 0.00 0.00 length ``` 關於self seconds與cumulative seconds與欄位的解讀: cumulative seconds:呼叫該函式(包含子函式)佔的總時間 self seconds:呼叫該函式(不包含子函式)佔的總時間,反映在時間上的百分比。 從這個例子可以發現,dot_product與subtract_vector等都呼叫了幾千萬次,也佔了不少時間,從這些呼叫多次的函式改善,對整體的改善幫助最多。 ## 改進 ### (1)loop unrolling 在`math-toolkit.h`中,有向量的基本運算,長得如下程式碼: ```clike= static inline void subtract_vector(const double *a, const double *b, double *out) { for (int i = 0; i < 3; i++) out[i] = a[i] - b[i]; } ``` 都是對index 0~2作個別運算,等於for迴圈根本沒有必要,造成多餘的branh prediction 利用perf分析 ``` perf stat -e cache-references,cache-misses,branches -r 10 ./raytracing ``` 結果 ``` Performance counter stats for './raytracing' (10 runs): 442,951 cache-references ( +- 18.95% ) 96,074 cache-misses # 21.690 % of all cache refs ( +- 28.09% ) 1,870,505,339 branches ( +- 0.01% ) 2.500741620 seconds time elapsed ( +- 0.35% ) ``` loop unrolling後 ``` Performance counter stats for './raytracing' (10 runs): 246,565 cache-references ( +- 9.09% ) 45,257 cache-misses # 18.355 % of all cache refs ( +- 7.32% ) 969,883,821 branches ( +- 0.00% ) 1.713952851 seconds time elapsed ( +- 0.12% ) ``` 竟然差了0.8秒,而branch數差了將近一半! Gprof分析 ``` Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 18.36 0.20 0.20 69646433 0.00 0.00 dot_product 12.85 0.34 0.14 10598450 0.00 0.00 normalize 11.02 0.46 0.12 17821809 0.00 0.00 cross_product 10.10 0.57 0.11 56956357 0.00 0.00 subtract_vector 10.10 0.68 0.11 13861875 0.00 0.00 rayRectangularIntersection 8.26 0.77 0.09 4620625 0.00 0.00 ray_hit_object 6.43 0.84 0.07 13861875 0.00 0.00 raySphereIntersection 5.05 0.90 0.06 17836094 0.00 0.00 add_vector 2.75 0.93 0.03 31410180 0.00 0.00 multiply_vector 2.75 0.96 0.03 1241598 0.00 0.00 refraction 2.75 0.99 0.03 1048576 0.00 0.00 ray_color 2.75 1.02 0.03 1 0.03 1.09 raytracing 2.29 1.04 0.03 2110576 0.00 0.00 compute_specular_diffuse 1.84 1.06 0.02 2520791 0.00 0.00 idx_stack_top 1.38 1.08 0.02 2110576 0.00 0.00 localColor 0.92 1.09 0.01 3838091 0.00 0.00 length ``` ### (2)inline function inline function:不呼叫function,而是直接將function主體嵌入程式碼中,如此有助於減少function call的次數。 在函數前使用`__attribute__((always_inline))`來強制gcc inline 還有很多選項來設定function的attribute,可以在[gcc manual](https://gcc.gnu.org/onlinedocs/gcc-6.2.0/gcc/Function-Attributes.html#Function-Attributes)找到 perf分析 ``` Performance counter stats for './raytracing' (10 runs): 131,085 cache-references ( +- 8.77% ) 31,756 cache-misses # 24.226 % of all cache refs ( +- 2.33% ) 545,079,262 branches ( +- 0.00% ) 1.533319775 seconds time elapsed ( +- 0.31% ) ``` 比起先前版本在快了約0.2秒 gprof分析 ``` Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 39.32 0.57 0.57 13861875 0.00 0.00 rayRectangularIntersection 19.32 0.85 0.28 13861875 0.00 0.00 raySphereIntersection 15.18 1.07 0.22 2110576 0.00 0.00 compute_specular_diffuse 8.97 1.20 0.13 1048576 0.00 0.00 ray_color 5.52 1.28 0.08 2110576 0.00 0.00 localColor 5.52 1.36 0.08 1048576 0.00 0.00 rayConstruction 3.45 1.41 0.05 4620625 0.00 0.00 ray_hit_object 0.69 1.42 0.01 1241598 0.00 0.00 reflection 0.69 1.43 0.01 1241598 0.00 0.00 refraction 0.69 1.44 0.01 113297 0.00 0.00 fresnel 0.69 1.45 0.01 1 0.01 1.45 raytracing 0.00 1.45 0.00 2558386 0.00 0.00 idx_stack_empty 0.00 1.45 0.00 2520791 0.00 0.00 idx_stack_top ``` 已經看不到`math-toolkit.h`的函式了,因為已經inline它們了 ## SIMD:使用AVX2.0 先到[intel官網](http://ark.intel.com/products/75027/Intel-Core-i5-4200H-Processor-3M-Cache-up-to-3_40-GHz)查詢我的CPU是否支援extension,發現可以支援SSE4.1/4.2, AVX 2.0 。gcc也在4.7版以後支援AVX2[(來源)](https://gcc.gnu.org/gcc-4.7/changes.html) 使用AVX2,必須include`immintrin.h`標頭檔,並且編譯時加上 `-mavx`。 ```clike= inline __attribute__((always_inline)) void add_vector(const double *a, const double *b, double *out) { __m256d ma = _mm256_set_pd(0,a[2],a[1],a[0]); __m256d mb = _mm256_set_pd(0,b[2],b[1],b[0]); ma = _mm256_add_pd(ma,mb); double* d=(double *)&ma; out[0]=d[0]; out[1]=d[1]; out[2]=d[2]; } ``` perf結果 ``` Performance counter stats for './raytracing' (10 runs): 231,011 cache-references ( +- 7.25% ) 36,516 cache-misses # 15.807 % of all cache refs ( +- 4.92% ) 546,709,778 branches ( +- 0.01% ) 3.619358562 seconds time elapsed ( +- 0.10% ) ``` 實際上反而更慢了,我在想是不是因為比之前多了很多新的變數宣告以及函數呼叫,增加了許多成本呢? >> 提出猜想後,要設計實驗證明,或者找到相關的技術手冊,不要只是「憑感覺」。SIMD register load/store 有成本。 [name=jserv] 實驗證明:(待續) ### OpenMP 待續 ## 參考資料 [How to read gprof output](http://badgertronics.com/writings/gprof.html) [perf tutorial](https://perf.wiki.kernel.org/index.php/Tutorial) [Crunching Numbers with AVX and AVX2](http://www.codeproject.com/Articles/874396/Crunching-Numbers-with-AVX-and-AVX) [作業video](https://www.youtube.com/watch?v=m1RmfOfSwno) [2016/5/3 Embedded System HW5 Team 4共筆](https://embedded2016.hackpad.com/ep/pad/static/f5CCUGMQ4Kp)