解讀所附 mountain 程序,並將該 mountain 程序在本組內不同(品牌/配置)PC 的 linux 系統上運行它。嘗試繪製自己的存儲器 山三維圖(或兩張二維圖),並解讀該存儲器山,根據結果分析該系統上的高速緩存的大小。建議可在組內不同配置機器上運行,比較不 同機器上高速緩存的設置。
一. 解讀 mountain 程序:
int main()
{
int size; /* Working set size (in bytes) 工作集*/
int stride; /* Stride (in array elements) 步長*/
double Mhz; /* Clock frequency 時鐘頻率,主頻*/
init_data(data, MAXELEMS); /* Initialize each element in data 初始化數據中的每個元素*/
Mhz = mhz(0); /* Estimate the clock frequency 估計時鐘頻率*/
/* $end mountainmain */
/* Not shown in the text */
printf("Clock frequency is approx. %.1f MHz\n", Mhz);
printf("Memory mountain (MB/sec)\n");
printf("\t");
for (stride = 1; stride <= MAXSTRIDE; stride++)
printf("s%d\t", stride);
printf("\n");
/* $begin mountainmain */
for (size = MAXBYTES; size >= MINBYTES; size >>= 1) { //遍歷size工作集,cache(1 << 27) 128M--->(1<<14) 16k
/* $end mountainmain */
/* Not shown in the text */
if (size > (1 << 20))
printf("%dm\t", size / (1 << 20));
else
printf("%dk\t", size / 1024);
/* $begin mountainmain */
for (stride = 1; stride <= MAXSTRIDE; stride++) { //遍歷步長stride,1--->15
printf("%.1f\t", run(size, stride, Mhz));
}
printf("\n");
}
exit(0);
}
int test(int elems, int stride) /* The test function 測試程序*/
{
int i;
long result = 0;
volatile long sink;
for (i = 0; i < elems; i += stride) {
result += data[i];
}
sink = result; /* So compiler doesn't optimize away the loop 所以編譯器不會優化循環*/
return sink;
}
double run(int size, int stride, double Mhz)
{
double cycles;
int elems = size / sizeof(double);
test(elems, stride); /* warm up the cache */ //line:mem:warmup 預熱緩存
cycles = fcyc2(test, elems, stride, 0); /* call test(elems,stride) */ //line:mem:fcyc
return (size / stride) / (cycles / Mhz); /* convert cycles to MB/s */ //line:mem:bwcompute number/time
}
① 如果一個程序在 s 秒的時間段內讀 n 個字節,那麼這段時間內的讀吞吐量就 等於n/s,典型地是以兆字節每秒 (MB/s) 爲單位的
② run函數內部又一重循環,通過以步長 stride 掃描整數數組的頭 elems 個元素來產生讀序列,那麼測量出的讀吞吐量能讓我們看到對於這個讀序列來說的存儲系統的性能。
③ 這個run程序運行的時間通過fcyc2函數來測量,以 CPU 週期爲單位。
④ 而讀次數n=size/stride,運行時間爲s=cycle/Mhz(CPU主頻),利用公式n/s即計算出讀吞吐量
⑤ 我們已經具備了測量讀吞吐量,接下來我們就要產生足夠多的序列數據,通過雙重循環遍歷/遍歷步長stride1—>64,workSize (1 << 25) 32M—>(1<<11) 2k從而生成足夠多的數據
二. 數據分析
1.編譯運行
linux>gcc -c clock.c -o clock.o
linux> gcc -c fcyc2.c -o fcyc2.o
linux> gcc -c mountain.c -o mountain.o
linux> gcc clock.o fcyc2.o mountain.o -o run
linux>./run
- 用 lscpu指令查看cache大小:
- 數據分析:
(1)整體分析:
① size 從 2KB 變到 32MB, stride 從 1 變到 64 個元素,每個元素是一個 8 個字節的 double
整體趨勢:我們看到隨着workSetSize的增大讀吞吐率反而減小;隨着隨着stride的增大讀吞吐率也呈現減小的趨勢;因此整個Corei5的都吞吐量的趨勢是:size 的值越小,得到的工作集越小,因此時間局部性越好。 stride 的值越小,得到的空間局部性越好。
② 垂直於 size 軸的是四條山脊,分別 對應於工作集完全在 Ll 高速緩存、 L2 高速緩存、 L3 高速緩存和主存內的時間局部性區城。
③ 在 L1,L2、 L3 和主存山脊上隨着步長的增加有一個空間局部性的斜坡,空間局部性下降。
(2)取出一個片段,保持步長爲常數stride=16
工作集完全能放進統一的 L3 高速緩 存中。更大的工作集大小主要由主存來服務。
① 在吞吐量峯值 4213MB/s 處,讀都是由 L1 來服務的,大小最大爲 256KB 的工作 集完全能放進統一的 L2 高速緩存中,對於大小最大爲 8M, 工作集完全能放進統一的 L3 高速緩 存中。更大的工作集大小主要由主存來服務。
② 由此估計出本機器的數據L1-d-cache大致爲32KB,L2-cache=256kb,L3通用cache=8M
③ L3 高速緩存區域最左邊的邊緣上讀吞吐量的下降很有可能是 由其他數據和代碼塊造成的,這些數據和代碼塊使得不可能將整個數組都裝進相應的高速緩存中。
(3)保持工作集大小不變,size=256kb,空間局部性斜坡如下:
① 在256kb 山脊,L2中的讀不命中會導致一個塊從 L3 傳送到 L2。後面在 L2 中這個塊上會有一定數量的 命中,這是取決於步長的。隨着步長的增加, L2 不命中與 L2 命中的比值也增加了。所以說吞吐量呈現下降的趨勢。
機器2:
① size 從 2KB 變到 32MB, stride 從 1 變到 64 個元素,每個元素是一個 8 個字節的 double
整體趨勢:我們看到隨着workSetSize的增大讀吞吐率反而減小;隨着隨着stride的增大讀吞吐率也呈現減小的趨勢;因此整個Corei5的都吞吐量的趨勢是:size 的值越小,得到的工作集越小,因此時間局部性越好。 stride 的值越小,得到的空間局部性越好。
② 垂直於 size 軸的是四條山脊,分別 對應於工作集完全在 Ll 高速緩存、 L2 高速緩存、 L3 高速緩存和主存內的時間局部性區城。
④ 在 L1,L2、 L3 和主存山脊上隨着步長的增加有一個空間局部性的斜坡,空間局部性下降。
(2)取出一個片段,保持步長爲常數stride=16
工作集完全能放進統一的 L3 高速緩 存中。更大的工作集大小主要由主存來服務。
④ 在吞吐量峯值 3158.3MB/s 處,讀都是由 L1 來服務的,大小最大爲 256KB 的工作 集完全能放進統一的 L2 高速緩存中,對於大小最大爲 6M, 工作集完全能放進統一的 L3 高速緩 存中。更大的工作集大小主要由主存來服務。
⑤ 由此估計出本機器的數據L1-d-cache大致爲32KB,L2-cache=256kb,L3通用cache=6M左右
⑥ L3 高速緩存區域最左邊的邊緣上讀吞吐量的下降很有可能是 由其他數據和代碼塊造成的,這些數據和代碼塊使得不可能將整個數組都裝進相應的高速緩存中。
(3)保持工作集大小不變,size=256kb,空間局部性斜坡如下:
① 在256kb 山脊,L2中的讀不命中會導致一個塊從 L3 傳送到 L2。後面在 L2 中這個塊上會有一定數量的 命中,這是取決於步長的。隨着步長的增加, L2 不命中與 L2 命中的比值也增加了。所以說吞吐量呈現下降的趨勢。
三.結論
① 機器1的數據L1-d-cache大致爲32KB,L2-cache=256kb,L3通用cache=8M左右,機器2的數據L1-d-cache大致爲32KB,L2-cache=256kb,L3通用cache=6M左右。
② 我們看到隨着workSetSize的增大讀吞吐率反而減小;隨着隨着stride的增大讀吞吐率也呈現減小的趨勢;因此整個Corei5的都吞吐量的趨勢是:size 的值越小,得到的工作集越小,因此時間局部性越好。 stride 的值越小,得到的空間局部性越好。
③ 存儲器系統的性能是一座時間和空間局部性的山,這座山的上升高度差別可以超過一個數量級。在編寫我們的程序時,應該儘量使得程序運行在山峯而不是低谷。目標就是利用時間局部性,使得頻繁使用的字、字從 Ll 中取出,還要利用空間局部性,使得儘可能多的字從一個 Ll 高速緩存行中訪問到。