# 2017q1 Homework1 (raytracing) contributed by < `0xff07` > 原始碼在[這裡](https://github.com/0xff07/raytracing) 作業要求在[這裡](https://hackmd.io/s/HyuBWDwYl) # 開發環境 ``` Ubuntu 16.04.2 Linux version 4.8.0-36-generic gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K ``` > * 中英文字間請以空白隔開 > * 請列出開發環境相關資訊 > [color=red][name=課程助教] # 工具 ## gprof 簡單說明用法如下 : 1. 編譯時, 加入 `-pg` 選項 2. 執行編譯好的程式。 執行完成後會多出一個 `gmon.out`檔案 3. 執行 : ``` $ gprof <程式名稱> gmon.out ``` 就會出現各種統計資訊。 也可以使用 `gprof <程式名稱> gmon.out | less`, 或導向到另一個檔案以方便閱讀。 # 執行 gprof評估 執行 ``` $ make PROFILE=1 $ ./raytracing $ gprof raytracing gmon.out ``` ``` Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 23.22 0.55 0.55 69646433 0.00 0.00 dot_product 20.69 1.04 0.49 56956357 0.00 0.00 subtract_vector 9.08 1.26 0.22 31410180 0.00 0.00 multiply_vector 7.60 1.44 0.18 10598450 0.00 0.00 normalize 6.76 1.60 0.16 17836094 0.00 0.00 add_vector 6.33 1.75 0.15 13861875 0.00 0.00 rayRectangularIntersection 5.07 1.87 0.12 4620625 0.00 0.00 ray_hit_object 4.22 1.97 0.10 13861875 0.00 0.00 raySphereIntersection 4.22 2.07 0.10 1048576 0.00 0.00 ray_color 3.80 2.16 0.09 17821809 0.00 0.00 cross_product 1.69 2.20 0.04 2110576 0.00 0.00 compute_specular_diffuse 1.69 2.24 0.04 1 0.04 2.36 raytracing 1.48 2.27 0.04 4221152 0.00 0.00 multiply_vectors 0.84 2.29 0.02 1241598 0.00 0.00 protect_color_overflow 0.84 2.31 0.02 1241598 0.00 0.00 refraction 0.42 2.32 0.01 3838091 0.00 0.00 length 0.42 2.33 0.01 2520791 0.00 0.00 idx_stack_top 0.42 2.34 0.01 2110576 0.00 0.00 localColor 0.42 2.35 0.01 1241598 0.00 0.00 reflection 0.21 2.36 0.01 2558386 0.00 0.00 idx_stack_empty 0.21 2.36 0.01 1204003 0.00 0.00 idx_stack_push ``` 註 : 僅列出前幾個執行時間佔較大的函數 發現 dot_product​ 這個函數佔了23.22%的執行時間, 另外subreact_vector花了20.69%的執行時間, 兩個加起來高達43.91% ! ## 視覺化 Call Graph 如果覺得 call graph 文字看起來很亂, 可以參考[這篇](http://stackoverflow.com/questions/517589/tools-to-get-a-pictorial-function-call-graph-of-code)。以以裡面 [KCachegrind](https://kcachegrind.github.io/html/Home.html)。 為例, 首先安裝: ```BASH $ sudo apt-get install -y kcachegrind valgrind ``` 接著編譯 raytracing 程式: ```BASH $ make ``` 然後: ``` $ valgrind --tool=callgrind ./raytracing ``` 會出現以下文字: ``` ==27518== Callgrind, a call-graph generating cache profiler ==27518== Copyright (C) 2002-2015, and GNU GPL'd, by Josef Weidendorfer et al. ==27518== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==27518== Command: ./raytracing ==27518== ==27518== For interactive control, run 'callgrind_control -h'. # Rendering scene ``` 然後等待... 完成之後出現 ``` Execution time of raytracing() : 96.692830 sec ==27518== ==27518== Events : Ir ==27518== Collected : 19459508299 ==27518== ==27518== I refs: 19,459,508,299 ``` 這時後會出現一個如 `callgrind.out.<pid>`的檔案。 接著執行 ``` $ kcachegrind callgrind.out.<pid> ``` 就會跑出如下視窗了 : ![](https://i.imgur.com/RtOoZwB.png) 因為有點好奇如果加了 `-pg` 編譯的話, 呼叫關係會長怎樣。 # 實驗 1. Loop unrolling 將本來的`subtract_vector`由 ```C 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]; } ``` 改成 : ```C static inline void subtract_vector(const double *a, const double *b, double *out) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; } ``` 以及將`dot_product`由 ``` C static inline double dot_product(const double *v1, const double *v2) { double dp = 0.0; for (int i = 0; i < 3; i++) dp += v1[i] * v2[i]; return dp; } ``` ``` % cumulative self self total time seconds seconds calls s/call s/call name 17.08 0.28 0.28 69646433 0.00 0.00 dot_product 14.34 0.52 0.24 31410180 0.00 0.00 multiply_vector 12.20 0.72 0.20 10598450 0.00 0.00 normalize 11.59 0.91 0.19 17836094 0.00 0.00 add_vector 8.54 1.05 0.14 56956357 0.00 0.00 subtract_vector 8.24 1.18 0.14 17821809 0.00 0.00 cross_product 6.71 1.29 0.11 13861875 0.00 0.00 rayRectangularIntersection 4.88 1.37 0.08 13861875 0.00 0.00 raySphereIntersection 3.66 1.43 0.06 4620625 0.00 0.00 ray_hit_object 3.66 1.49 0.06 2110576 0.00 0.00 localColor 2.44 1.53 0.04 4221152 0.00 0.00 multiply_vectors 1.83 1.56 0.03 2110576 0.00 0.00 compute_specular_diffuse 1.83 1.59 0.03 1048576 0.00 0.00 rayConstruction 1.22 1.61 0.02 3838091 0.00 0.00 length 0.61 1.62 0.01 1241598 0.00 0.00 protect_color_overflow 0.61 1.63 0.01 1241598 0.00 0.00 reflection 0.61 1.64 0.01 1048576 0.00 0.00 ray_color ``` (圖待補) 初步發現 `dot_product` 時間由0.55秒減少至0.28秒, `subtract_vector` 由0.49減少到0.14(!)。 不過這只做了一次而已, 所以需要多做幾次才可以有意義。 # 實驗2. openMP 把最 raytracing() 函式最內部的迴圈平行化。 但是我 code 寫錯了, 以致於 `make check` 會顯示錯誤。 可是產出的結果意外的跟本來的接近, 如下 : ![](https://i.imgur.com/4FhiaIO.png) 左方為這份程式的輸出, 右方是原始檔案。 這份程式執行時間如下 : ``` # Rendering scene Done! Execution time of raytracing() : 1.692834 sec ``` 而原始執行時間為 : ``` # Rendering scene Done! Execution time of raytracing() : 2.973419 sec ``` 花了原來 57 %的時間 。 真是太莫名奇妙了。 如果再加上 loop unrolling , 跑起來的時間是 : ``` # Rendering scene Done! Execution time of raytracing() : 1.264572 sec ``` 約變成原來的 42% >> 因為第二次 commit 就莫名其妙變快了將近一半, 在開心過頭的狀況下就很無聊的把 branch 叫作 superfast 了... :::danger 這才剛開始,好嗎? --jserv ::: >> Forgive my stupidity...