# 2016q3 Homework1 (raytracing)
contributed by <`HaoTse`>
---
### Reviewed by `shelly4132`
- 可以利用 **gnuplot** 繪製出效能比較圖表
- 可將POSIX Thread的方法實作出來
---
## 開發工具
- graphviz: 畫示意圖
- imagemagick: 格式轉換
```shell
$ sudo apt-get update
$ sudo apt-get install graphviz
$ sudo apt-get install imagemagick
```
----
### gprof
- GNU的工具
- 使用方式
- 編譯時加上`-pg`的參數,編譯器會在各函數中加入mcount函數
- 執行產生gmon.out
- `$ gprof -b raytracing gmon.out | less`
> 由於輸出訊息多,`$ less` 使我們可以使用上下鍵看訊息。[name=鄭皓澤]
- gprof v.s perf
**perf top** 的原理是每隔一段時間採樣一次,最後根據這些資料輸出,因此採樣的頻率可能會造成結果的不同。
---
## 效能分析
首先不開啟gprof,得到結果如下
```
# Rendering scene
Done!
Execution time of raytracing() : 3.338670 sec
```
接著使用`$ make PROFILE=1`重新編譯
> 注意得先做`$ make clean`
```
# Rendering scene
Done!
Execution time of raytracing() : 6.912961 sec
```
兩者對照可以看出gprof對速度的影響。
----
### gmon.out
`$ gprof -b raytracing gmon.out | less`
```shell=
% cumulative self self total
time seconds seconds calls s/call s/call name
21.79 0.54 0.54 69646433 0.00 0.00 dot_product
20.58 1.05 0.51 56956357 0.00 0.00 subtract_vector
12.91 1.37 0.32 31410180 0.00 0.00 multiply_vector
9.68 1.61 0.24 13861875 0.00 0.00 raySphereIntersection
8.47 1.82 0.21 13861875 0.00 0.00 rayRectangularIntersection
6.86 1.99 0.17 10598450 0.00 0.00 normalize
4.44 2.10 0.11 17836094 0.00 0.00 add_vector
4.03 2.20 0.10 17821809 0.00 0.00 cross_product
2.42 2.26 0.06 4620625 0.00 0.00 ray_hit_object
2.02 2.31 0.05 1048576 0.00 0.00 ray_color
1.61 2.35 0.04 4221152 0.00 0.00 multiply_vectors
1.21 2.38 0.03 2520791 0.00 0.00 idx_stack_top
1.01 2.41 0.03 1048576 0.00 0.00 rayConstruction
0.81 2.43 0.02 2110576 0.00 0.00 localColor
0.81 2.45 0.02 1241598 0.00 0.00 refraction
0.40 2.46 0.01 2110576 0.00 0.00 compute_specular_diffuse
0.40 2.47 0.01 1241598 0.00 0.00 reflection
0.40 2.48 0.01 1 0.01 0.01 delete_sphere_list
0.20 2.48 0.01 1 0.01 0.01 calculateBasisVectors
0.00 2.48 0.00 3838091 0.00 0.00 length
0.00 2.48 0.00 2558386 0.00 0.00 idx_stack_empty
0.00 2.48 0.00 1241598 0.00 0.00 protect_color_overflow
0.00 2.48 0.00 1204003 0.00 0.00 idx_stack_push
0.00 2.48 0.00 1048576 0.00 0.00 idx_stack_init
0.00 2.48 0.00 113297 0.00 0.00 fresnel
0.00 2.48 0.00 37595 0.00 0.00 idx_stack_pop
0.00 2.48 0.00 3 0.00 0.00 append_rectangular
0.00 2.48 0.00 3 0.00 0.00 append_sphere
0.00 2.48 0.00 2 0.00 0.00 append_light
0.00 2.48 0.00 1 0.00 0.00 delete_light_list
0.00 2.48 0.00 1 0.00 0.00 delete_rectangular_list
0.00 2.48 0.00 1 0.00 0.00 diff_in_second
0.00 2.48 0.00 1 0.00 2.47 raytracing
0.00 2.48 0.00 1 0.00 0.00 write_to_ppm
```
前三名dot_product、subtract_vector、multiply_vector都在math-toolkit.h這個檔案中,因此我們首先著手對它做優化。
---
## 優化方法1: force inline function
開啟math-toolkit.h後,最明顯的部份就是**static inline**,可是這次我們要求不使用編譯器最佳化,因此使用`__attribute__((always_inline))`強制開啟inline。
先將前面提到的3個最花時間的function加上force inline。
----
- 執行結果
```
# Rendering scene
Done!
Execution time of raytracing() : 4.771009 sec
```
執行時間從 6.912961sec下降至 4.771009sec。
----
- gprof輸出
```
% cumulative self self total
time seconds seconds calls s/call s/call name
32.06 1.00 1.00 13861875 0.00 0.00 rayRectangularIntersection
22.77 1.71 0.71 13861875 0.00 0.00 raySphereIntersection
8.66 1.98 0.27 10598450 0.00 0.00 normalize
7.70 2.22 0.24 2110576 0.00 0.00 compute_specular_diffuse
6.41 2.42 0.20 17836094 0.00 0.00 add_vector
5.45 2.59 0.17 17821809 0.00 0.00 cross_product
3.85 2.71 0.12 4620625 0.00 0.00 ray_hit_object
3.53 2.82 0.11 2110576 0.00 0.00 localColor
2.89 2.91 0.09 1048576 0.00 0.00 ray_color
2.24 2.98 0.07 1048576 0.00 0.00 rayConstruction
1.28 3.02 0.04 1241598 0.00 0.00 reflection
0.96 3.05 0.03 4221152 0.00 0.00 multiply_vectors
0.96 3.08 0.03 1241598 0.00 0.00 refraction
0.64 3.10 0.02 3838091 0.00 0.00 length
0.32 3.11 0.01 1 0.01 0.01 delete_sphere_list
0.32 3.12 0.01 1 0.01 3.11 raytracing
```
前面最耗時的3個function也從前3名消失了。
----
將所有的inline funciotn作force inline後,花費時間明顯下降,結果如下
```
# Rendering scene
Done!
Execution time of raytracing() : 3.916428 sec
```
---
## 優化方法2: loop unrolling
在math-toolkit.h中有許多function都有for loop,因此決定再套入loop unrolling方法降低速度。
修改後得到結果如下
```
# Rendering scene
Done!
Execution time of raytracing() : 2.934387 sec
```
執行時間從 3.916428sec降至 2.934387sec。
----
## 優化方法3: OpenMP
在raytracing.c的raytracing()中有一段程式碼如下
```clike=
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
double r = 0, g = 0, b = 0;
/* MSAA */
for (int s = 0; s < SAMPLES; s++) {
idx_stack_init(&stk);
rayConstruction(d, u, v, w,
i * factor + s / factor,
j * factor + s % factor,
view,
width * factor, height * factor);
if (ray_color(view->vrp, 0.0, d, &stk, rectangulars, spheres,
lights, object_color,
MAX_REFLECTION_BOUNCES)) {
r += object_color[0];
g += object_color[1];
b += object_color[2];
} else {
r += background_color[0];
g += background_color[1];
b += background_color[2];
}
pixels[((i + (j * width)) * 3) + 0] = r * 255 / SAMPLES;
pixels[((i + (j * width)) * 3) + 1] = g * 255 / SAMPLES;
pixels[((i + (j * width)) * 3) + 2] = b * 255 / SAMPLES;
}
}
}
```
可以看出各個pixel可以獨立運算,因此想使用多執行緒的方式來試著改善程式效能,在同學的筆記中看到OpenMP這套工具,先嘗試使用看看。
----
- 使用方式
- Makefile部份改為
```
CFLAGS = \
-std=gnu99 -Wall -O0 -g -fopenmp
LDFLAGS = \
-lm -lgomp
```
- 在上述的for loop前面增加`#pragma omp parallel for`
----
### 輸出結果
```
# Rendering scene
Done!
Execution time of raytracing() : 2.306696 sec
```
原先為 2.934387sec,發現下降並不明顯。
- `#pragma omp parallel for num_threads(64) `
將thread數量指定至64條。
不過`$ make check`後發現結果**fail**,參考同學的筆記之後發現要改為
```clike
#pragma omp parallel for num_threads(64) \
private(stk) private(d) \
private(object_color)
```
因為變數 stk、d 以及 object_color 必須要透過 private clause 宣告為執行緒私有的,否則預設之下在 #pragma omp 前的變數都是 shared,會導致計算結果錯誤。成功輸出結果如下
```
# Rendering scene
Done!
Execution time of raytracing() : 1.606055 sec
```
時間下降更多了。
---
## 目標
在`-Ofast`且不開啟`-pg`的情況下,結果如下
```
# Rendering scene
Done!
Execution time of raytracing() : 0.389374 sec
```
目標為**超越電腦**。
---
## 其他方法(待實驗)
- [ ] pthread
---
## 參考資料
- [inline介紹](http://www.cnblogs.com/cnmaizi/archive/2011/01/19/1939686.html)
- [OpenMP簡介](http://angelonotes.blogspot.tw/2012/03/openmpintroduction.html)
- [勃興筆記](https://hackmd.io/s/BygM4qP6)
- [彥寬學長筆記](https://embedded2016.hackpad.com/2016q1-Homework-2A-wOu40KzMaIP)
- [品睿學長筆記](https://embedded2016.hackpad.com/2016q1-Homework-2A-bLGQtRraTES)
###### tags: `HaoTse` `sysprog21` `raytracing` `gprof` `OpenMP`