# 2017q1 Homework1 (raytracing) contributed by <`ryanwang522`> ### Reviewed by `illusion030` * 沒有實作 force inline * 平行化可以尋找熱點來決定平行化的策略,以達到最佳平行化的效果,可以參考 [ggary9424的共筆](https://embedded2016.hackpad.com/2016q1-Homework-2A-bLGQtRraTES#:h=程式運作分析) * 可以跟編譯器最佳化的結果比較看看 --- ## 開發環境 * OS: ubuntu 16.04 LTS * L1d cache: 32K * L1i cache: 32K * L2 cache: 256K * L3 cache: 3072K * Architecture:x86_64 * CPU op-mode(s): 32-bit, 64-bit * Byte Order: Little Endian * CPU(s): 4 * Model name: Intel(R) Core(TM) i5-4200H CPU @ 2.80GHz ## 原始版本 * 進行編譯並執行之後,利用 gprof 觀察程式內每個函式的呼叫情形 * `$ gprof ./raytracing | less` * 發現 `dot_product()` 耗費的時間相當可觀,和 `math-toolfit.c` 裡頭的 function 同為程式熱點,可以從這裡去改善。 ``` Execution time of raytracing() : 5.454266 sec ``` ``` Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls s/call s/call name 24.45 0.54 0.54 69646433 0.00 0.00 dot_product 17.21 0.92 0.38 56956357 0.00 0.00 subtract_vector 9.96 1.14 0.22 31410180 0.00 0.00 multiply_vector 8.60 1.33 0.19 10598450 0.00 0.00 normalize 6.79 1.48 0.15 13861875 0.00 0.00 rayRectangularIntersection 6.79 1.63 0.15 4620625 0.00 0.00 ray_hit_object 6.34 1.77 0.14 13861875 0.00 0.00 raySphereIntersection 5.89 1.90 0.13 17836094 0.00 0.00 add_vector 4.98 2.01 0.11 17821809 0.00 0.00 cross_product 2.26 2.06 0.05 1048576 0.00 0.00 ray_color 1.36 2.09 0.03 1048576 0.00 0.00 rayConstruction 0.91 2.11 0.02 2110576 0.00 0.00 compute_specular_diffuse 0.91 2.13 0.02 2110576 0.00 0.00 localColor 0.91 2.15 0.02 1241598 0.00 0.00 refraction 0.91 2.17 0.02 1 0.02 2.20 raytracing 0.45 2.18 0.01 4221152 0.00 0.00 multiply_vectors 0.45 2.19 0.01 113297 0.00 0.00 fresnel 0.45 2.20 0.01 1 0.01 0.01 delete_sphere_list 0.23 2.21 0.01 2558386 0.00 0.00 idx_stack_empty 0.23 2.21 0.01 1204003 0.00 0.00 idx_stack_push ``` * 參考了 [hugikun999的共筆](https://hackmd.io/s/HyHhgcv6),透過 graphviz 產生出視覺化的函式運作圖表如下 ![](http://imgur.com/L7e4itH.png) ## 實際嘗試 ### Loop unrolling * 先試著將負擔較重的 `subtract_vector` 以及 `multiply_vector` 進行 loop unrolling * `$ ./raytracing` `Execution time of raytracing() : 4.693472 sec` * 發現雖然只是減少少數的迴圈次數,但由於呼叫次數非常多,所以微小的改變也可以對效能有著不可忽略的影響。 * 將`math-toolkit.h`內的函式進行改寫。 * 與原版比較 ![](http://imgur.com/lkDtLJ5.png) ### Multi-thread * OpenMP `$ sudo apt-get install gcc-multilib` * 修改`Makefile` * 編譯參數 `CFLAGS` 加上 `-fopenmp` * `LDFLAGS` 加上 `-lgomp` * 針對`raytracing()`裡面的外層 for-loop 進行平行化,加上 ``` #pragma omp parallel for num_threads(NUM_THREADS) private(stk, d) ``` * 接著執行看看,發現輸出圖形長這樣 ![](http://imgur.com/oYTLX3W.png) * 因為先前修過平行程式設計的課程,看到這種輸出已經是家常便飯了QQ,也大概知道原因是有些 private 變數沒有設定好,造成每個 thread 都改到應該要在每個執行序裡面獨立的變數,再回去把平行的函式內容看懂。 * 觀察 `ray_color()` * 發現 `object_color` 在裡面有大量的修改 -> 設為 private * 大致上瀏覽了一下有沒有需要使用 critical section 保護的變數,決定就先試試吧! ``` #pragma omp parallel for num_threads(NUM_THREADS) private(stk, d, object_color) ``` * 進行輸出 ![](http://imgur.com/pDaqRVe.png) * 觀察效能 `./raytracing` ``` Execution time of raytracing() : 1.064679 sec ``` ![](http://imgur.com/P15kIjx.png) * 執行 100 次並平均後的結果如圖所示,平行化使得執行時間縮減了 83%! * 最後想來測試看看 openmp 的不同 schedule 模式有什麼差別 * 整理`schedule(type [,chunk_size])`,type 大致上分為四種 * static * 每個 thread 在開始進行迴圈進行之前就已經被分配好了該執行的 iteration。 * 其中 chunk_size 代表每個 thread 每次要執行的迴圈次數。 * 舉例: 3 個 threads 執行 12 個 iterations。 `schedule(static, 1)` 也可以說是 cyclic partition ![](https://i.imgur.com/uhuWsyv.png) `schedule(static, 2)` ![](https://i.imgur.com/3SfHTSO.png) `schedule(static, 4)` 同 block partition ![](https://i.imgur.com/Ecjn9G3.png) * dynamic / guided * 每個 thread 也都執行一個 chunk_size 大小的區塊,當執行完再去 request 剩下的區塊 * guided 不同的地方是會先指行比較大的 chunk_size 區塊,隨後遞減到 指定的的 chunk_size。 * chunk_size 預設為 1。 * auto * 交給 compiler 決定。 * runtime * 透過設定 `OMP_schedule` 環境變數,在執行時間決定排程。 > 有錯還煩請指正。[name=Ryan Wang] * 針對不同模式進行比較 * Cyclic partion `#pragma omp parallel for num_threads(NUM_THREADS) private(stk, d, object_color) schedule(static, 1)` * 執行結果 `Execution time of raytracing() : 0.765990 sec` * Block partition `#pragma omp parallel for num_threads(NUM_THREADS) private(stk, d, object_color) schedule(static, 128)` * 執行結果 `Execution time of raytracing() : 0.769061 sec` * Dynamic with size = 1 `#pragma omp parallel for num_threads(NUM_THREADS) private(stk, d, object_color) schedule(dynamic)` * 執行結果 `Execution time of raytracing() : 0.766699 sec` * gnuplot 比較 ![](http://imgur.com/9sRPFtd.png) ## Reference [hugikun999共筆](https://hackmd.io/s/HyHhgcv6) [gnuplot 語法解說和示範](https://hackmd.io/s/Skwp-alOg) [Openmp loop schedule](https://software.intel.com/en-us/articles/openmp-loop-scheduling) [多處理機與平行程式設計課程投影片]()