10.21 實際測試內存在順序IO和隨機IO時的訪問延時差異

在一文中,我們理解了內存IO的內部實現過程,知道了內存的隨機IO比順序IO要慢,並對延遲時間進行了大概的估算。那麼我們今天來用代碼的方式來時間一下,看看在我們的項目工程中,內存訪問的在不同的訪問場景下延時究竟是個什麼表現。

實際測試內存在順序IO和隨機IO時的訪問延時差異

先測順序情況

測試原理就是定義一個指定大小的double(8字節)數組,然後以指定的步長去循環。這裡面的變量有兩個。核心代碼如下:

void init_data(double *data, int n){ 
int i;
for (i = 0; i < n; i++) {
data[i] = i;
}
}

void seque_access(int elems, int stride) {
int i;
double result = 0.0;
volatile double sink;

for (i = 0; i < elems; i += stride) {
result += data[i];
}
sink = result;
}

在這個核心代碼的基礎上,我們有兩個可調節變量:

  • 一是數組大小,數組越小,高速緩存命中率越高,平均延時就會越低。
  • 二是循環步長,步長越小,順序性越好,同樣也會增加緩存命中率,平均延時也低。 我們再測試的過程中採取的辦法是,固定其中一個變量,然後動態調節另外一個變量來查看效果。

另外說明一下,這個代碼測試中考慮的幾個額外的開銷的處理情況。

1.加法開銷:由於加法指令簡單,一個CPU週期就可完成,CPU週期比內存週期要快,所以暫且忽略它。

2.耗時統計:這涉及到高開銷的系統調用,本實驗通過跑1000次取一次耗時的方式來降低影響。

場景一: 固定數組大小2K,調節步長

實際測試內存在順序IO和隨機IO時的訪問延時差異

圖1 固定數組2k,動態調節步長

數組足夠小的時候,L1 cache全部都能裝的下。內存IO發生較少,大部分都是高效的緩存IO,所以我這裡看到的內存延時只有1ns左右,這其實只是虛擬地址轉換+L1訪問的延時。

場景二: 固定步長為8,數組從32K到64M

實際測試內存在順序IO和隨機IO時的訪問延時差異

圖2 固定步長,動態調節數組從32K到64M

當數組越來越大,Cache裝不下,導致穿透高速緩存,到內存實際IO的次數就會變多,平均耗時就增加

場景三: 步長為32,數組從32K到64M

實際測試內存在順序IO和隨機IO時的訪問延時差異

圖3 固定步長為32,動態調節數組從32K到64M

和場景二相比,步長變大以後,局部性變差,穿透的內存IO進一步增加。雖然數據量一樣大,但是平均耗時就會繼續有所上漲。不過雖然穿透增加,但由於訪問地址仍然相對比較連續,所以即使發生內存IO也絕大部分都是行地址不變的順序IO情況。所以耗時在9ns左右,和之前估算大致相符!

另外注意一個細節,就是隨著數組從64M到32M變化的過程中。耗時有幾個明顯的下降點,分別是8M,256K和32K。這是因為本機的CPU的L1大小是32K,L2是256K,L3是12M。在數據集32K的時候,L1全能裝的下,所有基本都是高速緩存IO。256K的時候、8M的時候,雖然L1命中率下降,但是L2、L3訪問速度仍然比真正的內存IO快。但是超過12M以後越多,真正的內存IO就越來越多了。

再測隨機IO情況

在順序的實驗場景裡,數組的下標訪問都是比較有規律地遞增。在隨機IO的測試中,我們要徹底打亂這個規律,提前隨機好一個下標數組,實驗時不停地訪問數組的各個隨機位置。

void init_data(double *data, int n){ 
int i;
for (i = 0; i < n; i++) {
data[i] = i;
}
}

void random_access(int* random_index_arr, int count) {
int i;
double result = 0.0;
volatile double sink;

for (i = 0; i < count; i++) {
result += data[*(random_index_arr+i)];
}
sink = result;
}

這實際比上面的實驗多了一次內存IO,但由於對random_index_arr的訪問時順序的,而且該數組也比較小。我們假設它全部能命中高速緩存,所以暫且忽略它的影響。

隨機實驗場景: 數組從32K到64M

實際測試內存在順序IO和隨機IO時的訪問延時差異

圖4 隨機訪問

這次的數組訪問就沒有步長的概念了,全部打亂,隨機訪問。當數據集比較小的時候、L1、L2、L3還能抗一抗。但當增加到16M、64M以後,穿透到內存的IO情況會變多,穿透過去以後極大可能行地址也會變。在64M的數據集中,內存的延時竟然下降到了38.4ns,和我們估算的也基本一致。

結論

有了實驗數據的佐證,進一步證實了的結論。內存也存在隨機訪問比順序訪問慢的多的情況,大概是4:1的關係。所以不要覺得內存很快,就用起來太隨性了!


分享到:


相關文章: