owned this note
owned this note
Published
Linked with GitHub
# 2016q3 Homework1 (compute-pi)
## 執行環境
Linux 4.4.0-31-generic #50-Ubuntu
x86_64 x86_64 x86_64 GNU/Linux
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 3072K
## 先編譯看看
```
$ make check
```
使用 clock_gettime() 來測量不同實做的執行時間
需要等待
```
benchmark_clock_gettime
time ./time_test_baseline
N = 400000000 , pi = 3.141593
4.37user 0.00system 0:04.37elapsed 99%CPU (0avgtext+0avgdata 1696maxresident)k
0inputs+0outputs (0major+83minor)pagefaults 0swaps
time ./time_test_openmp_2
N = 400000000 , pi = 3.141593
6.22user 0.00system 0:03.11elapsed 199%CPU (0avgtext+0avgdata 1796maxresident)k
0inputs+0outputs (0major+87minor)pagefaults 0swaps
time ./time_test_openmp_4
N = 400000000 , pi = 3.141593
8.66user 0.00system 0:02.18elapsed 396%CPU (0avgtext+0avgdata 1824maxresident)k
0inputs+0outputs (0major+91minor)pagefaults 0swaps
time ./time_test_avx
N = 400000000 , pi = 3.141593
1.90user 0.00system 0:01.90elapsed 100%CPU (0avgtext+0avgdata 1796maxresident)k
0inputs+0outputs (0major+85minor)pagefaults 0swaps
time ./time_test_avxunroll
N = 400000000 , pi = 3.141593
1.73user 0.00system 0:01.73elapsed 99%CPU (0avgtext+0avgdata 1768maxresident)k
0inputs+0outputs (0major+86minor)pagefaults 0swaps
```
接著
```
$ make gencsv
```
會將數據輸出至 result_clock_gettime.csv
接下來就是將我們在phonebook所學的繪圖用上的時間啦!!!
![](https://i.imgur.com/1xVPyKh.jpg)
____
## Pi 的計算方式
### Baseline 版本
![](https://i.imgur.com/fIiP7zp.png)
* 證明:
微積分公式
* 這段程式碼使用黎曼積分
![](https://i.imgur.com/CABZr0B.gif)(快速勾起大家高中回憶)
下面的N表示切成幾塊,如果切越多快就越接近曲線下的面積
```clike=
double compute_pi_baseline(size_t N)
{
double pi = 0.0;
double dt = 1.0 / N; // dt = (b-a)/N, b = 1, a = 0
for (size_t i = 0; i < N; i++) {
double x = (double) i / N; // x = ti = a+(b-a)*i/N = i/N
pi += dt / (1.0 + x * x); // integrate 1/(1+x^2), i = 0....N
}
return pi * 4.0; //因為算出來是pi/4 所以要*4倍
}
```
### OpenMP
* 跟baseline一樣的積分式求得pi
* threads間的資料沒有先後運算的差別
* 程式碼:
因為這裡使用的積分公式全為加法,可以分區積分後再加總
$\int_{0}^{1} \frac{1}{x^2 + 1} dt = \int_{0}^{1/2} \frac{1}{x^2 + 1} dt+\int_{1/2}^{1} \frac{1}{x^2 + 1} dt$。
```C==
double compute_pi_openmp(size_t N, int threads)
{
double pi = 0.0;
double dt = 1.0 / N;
double x;
#pragma omp parallel num_threads(threads)
{
#pragma omp for private(x) reduction(+:pi)
for (size_t i = 0; i < N; i++) {
x = (double) i / N;
pi += dt / (1.0 + x * x);
}
}
return pi * 4.0;
}
```
### AVX SIMD
AVX (Advanced Vector Extensions) 是 Intel 一套用來作 Single Instruction Multiple Data 的指令集
- 128 位 SIMD 暫存器 xmm0 - xmm15 擴展為 256 位的 ymm0 - ymm15 暫存器
- 支持 256 位的向量運算,由原來 128 位擴展為 256 位
- 指令可支持最多 4 個 operand,實現目標操作數無需損毀原來的內容
### AVX SIMD + Unroll looping
為何 AVX SIMD 的版本沒辦法達到預期 4x 效能提昇呢?
因為浮點 scalar 除法和 vector 除法的延遲,導致沒辦法有效率的執行。考慮到填充 vector instruction 的 pipeline 未能充分使用,我們可以預先展開迴圈,以便使用更多的 scalar 暫存器,從而減少資料相依性。
```
for (int i = 0; i <= dt - 4; i += 4) {
ymm3 = _mm256_set1_pd(i * delta);
ymm3 = _mm256_add_pd(ymm3, ymm2);
ymm3 = _mm256_mul_pd(ymm3, ymm3);
ymm3 = _mm256_add_pd(ymm0, ymm3);
ymm3 = _mm256_div_pd(ymm1, ymm3);
ymm4 = _mm256_add_pd(ymm4, ymm3);
}
```
可以看到 ymm3 有很強的相依性,而且看起來無法在縮得更小了。
### 效能分析圖
我們將數據的範圍調大
```
gencsv: default
for i in `seq 100 5000 800000`; do \
printf "%d," $$i;\
./benchmark_clock_gettime $$i; \
done > result_clock_gettime.csv
```
![](https://i.imgur.com/7B9vJza.jpg)
____
## Leibniz 版本
### Baseline
![](https://i.imgur.com/RsrH9Kz.png)
* 程式:
```C==
double compute_pi_Leibniz(size_t N)
{
double pi = 0.0;
double x;
for (size_t i = 0; i < N; i++) {
x = pow(-1,i) / (2*i +1);
pi += x;
}
return pi * 4.0;
}
```
### AVX SIMD
```C==
double compute_pi_leibizavx(size_t N)
{
double pi = 0.0;
register __m256d ymm0, ymm1, ymm2, ymm3, ymm4;
ymm0 = _mm256_set_pd(1.0,-1.0,1.0,-1.0); //for pow(-1,i)
ymm1 = _mm256_set1_pd(2.0);
ymm2 = _mm256_set1_pd(1.0);
ymm4 = _mm256_setzero_pd(); // sum of pi
for (int i = 0; i <= N - 4; i += 4) {
ymm3 = _mm256_set_pd(i, i+1.0, i+2.0, i+3.0); //i i+1 i+2 i+3
ymm3 = _mm256_mul_pd(ymm1, ymm3); //2*i 2*(i+1)...
ymm3 = _mm256_add_pd(ymm3,ymm2); //2*i+1 2*(i+1)+1 ...
ymm3 = _mm256_div_pd(ymm0,ymm3); //(-1)^i/(2*i+1) ...
ymm4 = _mm256_add_pd(ymm3,ymm4); //sum
}
double tmp[4] __attribute__((aligned(32)));
_mm256_store_pd(tmp, ymm4); // move packed float64 values to 256-bit aligned memory location
pi += tmp[0] + tmp[1] + tmp[2] + tmp[3];
return pi * 4.0;
}
```
### 效能分析圖
![](https://i.imgur.com/eHkdJ1n.jpg)
## 參考資料
* [Leibniz formula for π - Wikipedia](https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80)
* [廖健富學長的Hackpad](https://hackpad.com/Hw1-Extcompute_pi-geXUeYjdv1I)
* [TempoJiJi的HackMD](https://hackmd.io/s/Hy7tssw6)
* [劭芊的hackMD](https://hackmd.io/s/HJrLU0dp)
* [VVN 的hackMD](https://hackmd.io/MYZgpgTAJmCsUFpYDNiwQFnMhBOAhmAGx5gYCMY+IUuRy5QA?view)
* [Makefile](http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/)