# 2016q3 Homework2 (raytracing) contributed by <`mingnus`> ###### tags: `raytracing` `gprof` `perf` `profiling` `sysprog` ## 環境設定 * Intel Celeron N3150 CPU, 8GB DDR3 RAM * Ubuntu 16.04.1, kernel 4.4.0-38 * gcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609 * GLIBC 2.23-0ubuntu3 ## gprof 另開專頁記錄: [gprof 說明與心得](https://hackmd.io/s/HyFU355EQ) ## Raytracing program 由gprof flat profile可看出下列向量運算是最花時間的部分: * subtract_vector() * dot_product() * multiply_vector() * add_vector() ... ## Experiments ### Loop unrolling + pthread平行運算 1. 向量運算函式以loop unrolling改寫 2. 以多執行緒同時繪製畫面不同pixels (問題: raytracing每個pixel真的都是獨立運算? pixel運算順序是否影響結果?) 採用scanline rendering: 考量畫面各區域複雜度不同[[1]]( https://embedded2016.hackpad.com/ep/pad/static/bLGQtRraTES), 為平均運算複雜度, 每個thread各自負責scanlines #(thread_id + k * nr_threads) 如果採用tiled rendering且tile面積又很大時, 程式速度將受制於負責最複雜區域的thread (e.g., cinebench) barrier實作參考concurrent-ll https://github.com/gavra0/ConcLinkedList FIXME: handle trailing rows ### Result * Loop unrolling大約可節省1/3運算時間 * 4-threads比gcc -O3快, 輸出的ppm checksum也相同 但是用gprof看, 多執行緒的vector function呼叫次數卻比單執行緒少? ![](https://i.imgur.com/rHkMkeh.png) ## 研究中項目 ### SIMD https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/x86-Options.html gcc預設就有開啟SSE (根據build machine CPU決定, 待查), 以dot_product()為例: * simd_t.c: ``` 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; } ``` * Disassemble dot_product() (注意大小寫): ``` $ gcc -c simd_t.c -g -O0 $ objdump [-Mintel] -d simd_t.o > simd_t.s 00000000000000b6 <dot_product>: b6: 55 push %rbp b7: 48 89 e5 mov %rsp,%rbp ba: 48 89 7d e8 mov %rdi,-0x18(%rbp) be: 48 89 75 e0 mov %rsi,-0x20(%rbp) c2: 66 0f ef c0 pxor %xmm0,%xmm0 c6: f2 0f 11 45 f8 movsd %xmm0,-0x8(%rbp) cb: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp) d2: eb 46 jmp 11a <dot_product+0x64> d4: 8b 45 f4 mov -0xc(%rbp),%eax d7: 48 98 cltq d9: 48 8d 14 c5 00 00 00 lea 0x0(,%rax,8),%rdx e0: 00 e1: 48 8b 45 e8 mov -0x18(%rbp),%rax e5: 48 01 d0 add %rdx,%rax e8: f2 0f 10 08 movsd (%rax),%xmm1 ec: 8b 45 f4 mov -0xc(%rbp),%eax ef: 48 98 cltq f1: 48 8d 14 c5 00 00 00 lea 0x0(,%rax,8),%rdx f8: 00 f9: 48 8b 45 e0 mov -0x20(%rbp),%rax fd: 48 01 d0 add %rdx,%rax 100: f2 0f 10 00 movsd (%rax),%xmm0 104: f2 0f 59 c1 mulsd %xmm1,%xmm0 108: f2 0f 10 4d f8 movsd -0x8(%rbp),%xmm1 10d: f2 0f 58 c1 addsd %xmm1,%xmm0 111: f2 0f 11 45 f8 movsd %xmm0,-0x8(%rbp) 116: 83 45 f4 01 addl $0x1,-0xc(%rbp) 11a: 83 7d f4 02 cmpl $0x2,-0xc(%rbp) 11e: 7e b4 jle d4 <dot_product+0x1e> 120: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0 125: 5d pop %rbp 126: c3 retq ``` 所以我們應該用資料寬度更大的ymm暫存器及AVX指令,才有機會勝過上述ASM code 如果用gcc -mavx option, 則gcc只是把SSE指令換成AVX指令, 並沒有使用ymm暫存器, 實測速度也沒有變快 * pxor => vxorpd * movsd => vmovsd * mulsd => vmulsd * addsd => vaddsd 使用-mno-sse無法通過編譯 (待查) ### Fast inverse square root https://en.wikipedia.org/wiki/Fast_inverse_square_root http://213style.blogspot.tw/2014/07/john-carmack.html *Hacker's Delight, 2/e*, Ch.11-1 and 17-4 ## 其他 * 程式分析 * 世界座標: 右手座標, Z軸在上 * 場景物件(models.inc): 白球1, 金屬球2, 綠球3; 白矩型1, 紅矩型2, 藍矩型3