contributed by <TempoJiJi
>
ierosodin
應該一併列出 processor model,這對解讀數據來說很重要 jserv
觀察程式的行爲後,在Makefile裏看見了這個編譯選項:
-DBASELINE
才學到原來能在編譯動態使用#define
$ make check
time ./time_test_baseline
N = 400000000 , pi = 3.141593
4.41user 0.00system 0:04.43elapsed 99%CPU (0avgtext+0avgdata 1792maxresident)k
0inputs+0outputs (0major+87minor)pagefaults 0swaps
time ./time_test_openmp_2
N = 400000000 , pi = 3.141593
4.89user 0.00system 0:02.45elapsed 199%CPU (0avgtext+0avgdata 1784maxresident)k
0inputs+0outputs (0major+88minor)pagefaults 0swaps
time ./time_test_openmp_4
N = 400000000 , pi = 3.141593
9.49user 0.00system 0:02.46elapsed 385%CPU (0avgtext+0avgdata 1736maxresident)k
0inputs+0outputs (0major+90minor)pagefaults 0swaps
time ./time_test_avx
N = 400000000 , pi = 3.141593
1.28user 0.00system 0:01.28elapsed 99%CPU (0avgtext+0avgdata 1704maxresident)k
0inputs+0outputs (0major+83minor)pagefaults 0swaps
time ./time_test_avxunroll
N = 400000000 , pi = 3.141593
1.23user 0.00system 0:01.23elapsed 99%CPU (0avgtext+0avgdata 1716maxresident)k
0inputs+0outputs (0major+84minor)pagefaults 0swaps
在這裏就可以很清楚的看到每種不同的版本的執行時間(N=400000000),這裏有3種不同的時間:
CPU bound: 指的是程序需要大量的 CPU computation (對 CPU time 需求量大),相比之下僅需少量的 I/O operation,效能取決於 CPU 速度。
I/O bound: 需要大量 I/O operation (I/O request 的頻率很高或一次 I/O request 成本很高),但僅需少量的 CPU computation,效能取決於 I/O device 速度。
real < user
: 表示程式是CPU bound,所以使用平行處理對效能提升有幫助real ≒ user
: 表示程式是CPU bound,但即使使用平行處理,效能也不會有明顯的提升,有可能是計算兩沒有很大,又或是程式的相依性太高real > user
: 表示程式是I/O bound,成本大部分爲I/O操作,所以使用平行處理並不會帶來顯著的效能提昇 (或沒有提昇)參考資料:
接下來看看make gencsv:
$ make gencsv
for i in `seq 100 5000 25000`; do \
printf "%d," $i;\
./benchmark_clock_gettime $i; \
done > result_clock_gettime.csv
這裏的for相等於:
for(i = 100; i <= 25000; i+=5000)
大意就是將N初始化爲100進行測試,每次加5000直到25000。然後將結果輸出到result_clock_gettime.csv裏
執行make plot
輸出結果,在Makefile裏加入:
plot: gencsv
gnuplot runtime.gp
clean:
rm -f $(EXECUTABLE) *.o *.s result_clock_gettime.csv runtime.png
執行
$ make plot
baseline的計算結果誤差圖:
上面這種方法就是目前baseline使用的
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;
}
N 爲積分區間[0,1]的分段大小,理論上N分的越細(N值很大)精準度就越高,相對的計算量也很大
參考資料:Wikipedia
Code:
double compute_pi_leibniz(size_t N)
{
double pi = 0.0;
for(int i=0;i<N;i++)
pi += pow(-1,i) / (2*i+1);
return pi * 4.0;
}
leibniz_avx :
double leibniz_avx(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);
ymm1 = _mm256_set1_pd(1.0);
ymm2 = _mm256_set1_pd(2.0);
ymm4 = _mm256_setzero_pd();
for(int i=0 ; i <= N-4 ; i+=4){
ymm3 = _mm256_set_pd(i, i+1.0, i+2.0, i+3.0); // i to i+4
ymm3 = _mm256_mul_pd(ymm3 , ymm2); // 2*i (i to i+4)
ymm3 = _mm256_add_pd(ymm3 , ymm1); // 2*i+1 (i to i+4)
ymm3 = _mm256_div_pd(ymm0 , ymm3); // pow(-1,i) / ymm3
ymm4 = _mm256_add_pd(ymm4 , ymm3); // pi += ymm3
}
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;
}
查看效能:
$ make check
N = 400000000 , pi = 3.141593
2.20user 0.00system 0:02.20elapsed 99%CPU (0avgtext+0avgdata 1708maxresident)k
0inputs+0outputs (0major+83minor)pagefaults 0swaps
time ./time_test_leibniz_openmp_2
N = 400000000 , pi = 3.141593
2.45user 0.00system 0:01.23elapsed 199%CPU (0avgtext+0avgdata 1768maxresident)k
0inputs+0outputs (0major+86minor)pagefaults 0swaps
time ./time_test_leibniz_openmp_4
N = 400000000 , pi = 3.141593
4.78user 0.00system 0:01.23elapsed 386%CPU (0avgtext+0avgdata 1788maxresident)k
0inputs+0outputs (0major+93minor)pagefaults 0swaps
time ./time_test_leibniz_avx
N = 400000000 , pi = 3.141593
1.35user 0.00system 0:01.36elapsed 99%CPU (0avgtext+0avgdata 1788maxresident)k
0inputs+0outputs (0major+85minor)pagefaults 0swaps
time ./time_test_leibniz_avxunroll
N = 400000000 , pi = 3.141593
1.71user 0.00system 0:01.71elapsed 99%CPU (0avgtext+0avgdata 1932maxresident)k
0inputs+0outputs (0major+89minor)pagefaults 0swaps
在這裏觀察openmp:
以下爲leibniz各個不同實做的效能對比圖:
leibniz的計算結果誤差圖:
clock_t clock(void);
clock()測試:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main( void )
{
long i=1000L;
clock_t start, finish;
double duration;
printf( "Time to do %ld empty loops is ", i );
start = clock();
while (--i){
system("cd");
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
return 0;
}
執行結果:
$ time ./clock_test
Time to do 1000 empty loops is 0.049815 seconds
real 0m0.482s
user 0m0.000s
sys 0m0.052s
在這裏可以看到程式使用system("cd")這個system call,結果clock()所計算出來的時間跟real time卻差很多。
int clock_gettime(clockid_t clk_id, struct timespec *tp);
第一個參數clockid_t 確定要用哪個是種類型:
CLOCK_REALTIME
: 標準POSIX即時時鐘,利用變數xtime記錄RTC的時間。會隨着系統即時時間而改變,即從 1970/1/1 00:00:00
開始計時,中間如果系統時間被改變,則對應的時間也會被改變CLOCK_MONOTONIC
: 從系統啓動時開始計算,利用jiffies來記錄,不會受到系統時間被改變的影響,但會因爲NTP的改變而受到影響CLOCK_MONOTONIC_RAW
: 跟CLOCK_MONOTONIC一樣,但不會受到NTP的影響CLOCK_PROCESS_CPUTIME_ID
: 記錄一個process裏所話的總CPU時間(User mode & Kernel mode),不包含等I/O和其他資源的時間。CLOCK_THREAD_CPUTIME_ID
: 記錄一個thread所花的總CPU時間有個時間結構體 timespec
,其計時的單位是奈秒(nanosecond)
strace timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
}
time_t time(time_t *t);
Description
time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
int gettimeofday ( struct timeval * tv , struct timezone * tz )
跟time()一樣,但會回傳多一個timezone,看下面這個timeval的結構:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
Notes:
The time returned by gettimeofday() is affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the system time). 如果系統時間被改變,那麼gettimeofday()所回傳的值,會被不連續的跳動時間所影響
為什麼 time() 和 gettimeofday() 不適合拿來作 benchmark ?
在上面可以看到 time() 跟 gettimeofday() 都是直接回傳系統時間,所以如果系統時間在程式執行期間被改變,那麼回傳出來的值就會不準確。而且爲了要跟NTP同步,在同步的期間可能會有幾秒的誤差。
爲什麼clock_gettime()結果飄忽不定?
clock_gettime() 會因爲不同的進程在CPU之間切換而受到影響。解決方法就是利用clock_gettime()找出95%信賴區間,這樣得到的數據就更加精準。
參考資料:
TempoJiJi
ComputePi
sysprog21