# 2017q1 Homework1 (compute-pi)
contributed by < `etc276` >
###### tags: `嵌入式`
### Reviewed by `yangyang95`
- 不是超連結的文字 ( eg. [link text](https:// "title") ),不要使用超連結符號,可用 **粗體**
- 第一張 error rate 的圖形不清楚,且範圍需修改
- binary file 不要一起送上 github ,可在 .gitignore 中加入 *.png *.txt 來忽略特定檔案格式
- 程式碼還可以在稍微精簡一點點,並注意 coding style 小括號左方要留有空白
```C==
double compute_pi_leibniz (size_t N)
{
double pi = 0.0;
int tmp = 1;
for (size_t i = 0; i < N; i++) {
pi += tmp / (2.0 * (double)i + 1.0);
tmp *= (-1);
}
return 4 * pi;
}
```
## 開發環境
- Ubuntu 16.10 (64bit)
```
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 61
Model name: Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
Stepping: 4
CPU MHz: 2400.878
CPU max MHz: 3000.0000
CPU min MHz: 500.0000
BogoMIPS: 4788.90
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 4096K
NUMA node0 CPU(s): 0-3
```
## 作業要求
* 在 GitHub 上 fork [compute-pi](https://github.com/sysprog21/compute-pi),嘗試重現實驗,包含分析輸出
* 注意: 要增加圓周率計算過程中迭代的數量,否則時間太短就看不出差異
* 詳閱廖健富提供的[詳盡分析](https://hackpad.com/Hw1-Extcompute_pi-geXUeYjdv1I),研究 error rate 的計算,以及為何思考我們該留意後者
* 用 [phonebok](/s/S1RVdgza) 提到的 gnuplot 作為 `$ make gencsv` 以外的輸出機制,預期執行 `$ make plot` 後,可透過 gnuplot 產生前述多種實做的效能分析比較圖表
* 可善用 [rayatracing](/s/B1W5AWza) 提到的 OpenMP, software pipelining, 以及 loop unrolling 一類的技巧來加速程式運作
* 詳閱前方 "Wall-clock time vs. CPU time",思考如何精準計算時間
* 除了研究程式以外,請證明 [Leibniz formula for π](https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80),並且找出其他計算圓周率的方法
* 請詳閱[許元杰的共筆](https://embedded2015.hackpad.com/-Ext1-Week-1-5rm31U3BOmh)
* 將你的觀察、分析,以及各式效能改善過程,並善用 gnuplot 製圖,紀錄於「[作業區](https://hackmd.io/s/Hy2rieDYe)」
## 開發紀錄
### 重現實驗
* ```$ make check``` 後查看各版本時間
```
$ time ./time_test_baseline
N = 400000000 , pi = 3.141593
real 0m1.388s
user 0m1.376s
sys 0m0.004s
$ time ./time_test_openmp_2
N = 400000000 , pi = 3.141593
real 0m1.324s
user 0m2.632s
sys 0m0.000s
$ time ./time_test_openmp_4
N = 400000000 , pi = 3.141593
real 0m0.770s
user 0m2.768s
sys 0m0.000s
$ time ./time_test_avx
N = 400000000 , pi = 3.141593
real 0m0.922s
user 0m0.920s
sys 0m0.000s
$ time ./time_test_avxunroll
N = 400000000 , pi = 3.141593
real 0m0.721s
user 0m0.712s
sys 0m0.004s
```
* 分析 real, user, sys 的不同
1. real time :
也就是 Wall-clock time,當然若有其他程式同時在執行,必定會影響到。
2. user time : 表示程式在 user mode 佔用所有的 CPU time 總和。
3. sys time : 表示程式在 kernel mode 佔用所有的 CPU time 總和 ( 直接或間接的系統呼叫 )。
### 增加迭代
* 查看 Makefile 發現以下程式碼
```clike==
gencsv: default
for i in `seq 100 5000 25000`; do \
printf "%d," $$i;\
./benchmark_clock_gettime $$i; \
done > result_clock_gettime.csv
```
* 而這段程式碼可理解為
```clike==
for (i=100; i<=25000; i+=5000)
compute_pi(i);
```
* 增加迭代前的圖 (i<=25000)
![](https://i.imgur.com/V28kMyv.png)
* 增加迭代後的圖 (i<=250000)
![](https://i.imgur.com/TYjxtze.png)
* 可發現 OpenMP (4 Thread)的時間漂浮有點大,晚點再來想原因
### 研究 error rate
* 查看 Makefile 發現用來產生圖檔的資料 csv 檔是由 benchmark_clock_gettime 輸出,因此修改此 .c 檔,再輸出 error。後改為寫一個 out.pg,並在 Makefile 新增 `plot :`以產生兩種圖檔。
* 產生圖檔的過程遇到一個困難是,csv 檔的資料分隔是用逗號(,)所以一開始想用 ```"%lf,%lf,%lf,%lf,%lf,%lf"``` 的方式撰寫 out.pg,後來才發現可以使用 ```set datafile separator "," ```
* 用 gnuplot 畫出來的 error rate
![](https://i.imgur.com/3192HzF.png)
* 在 out.pg 加上 ```set logscale xy``` ,看的細一點
![](https://i.imgur.com/184FEwg.png)
### Leibniz formula for π
參考 [shelly4132的共筆](https://hackmd.io/s/HJrLU0dp)
考慮以下分解:
![image alt](https://wikimedia.org/api/rest_v1/media/math/render/svg/c6358998bd99e608b5066ceda18a5211aae91472)
對兩邊從0到1去做積分
![](https://wikimedia.org/api/rest_v1/media/math/render/svg/d84dabc013f1b57c1d047303d68e28b3054c52e1)
當![](https://wikimedia.org/api/rest_v1/media/math/render/svg/4fbcf3489c4ce0a4f161b2584ec1f31c70cbc5a6)時,除積分項以外的項收斂到萊布尼茨級數。同時,積分項收斂到0:
![](https://wikimedia.org/api/rest_v1/media/math/render/svg/39b589297ef09eb07ad5c8acf06523fc4216ff45)
所以這便證明了萊布尼茨公式。
![](https://wikimedia.org/api/rest_v1/media/math/render/svg/8283f454140077256b47afadd702fed4b3b56569)
```clike=
double compute_pi_leibniz(size_t N)
{
double pi = 0.0;
int tmp = 1;
for (size_t i =0; i < N; i++) {
pi += tmp / (2.0 * (double)i + 1.0);
tmp *= (-1);
}
pi *= 4;
return pi;
}
```
* 補圖
![](https://i.imgur.com/vHLuM4E.png)
![](https://i.imgur.com/XzBlsNd.png)
其他計算圓周率的方式如 蒙地卡羅、尤拉等等 (待補實作code)