內(nèi)存延遲實(shí)際測(cè)試

在前面的文章你能估算出你的內(nèi)存的訪問延時(shí)嗎?中,我們對(duì)內(nèi)存的訪問延遲進(jìn)行了理論上的研究。那么今天我們實(shí)際編寫代碼進(jìn)行一下測(cè)試。 看一下實(shí)踐能否和理論匹配的上。
為了衡量各種情況下的內(nèi)存帶寬情況。我們用c寫了一個(gè)double類型的數(shù)組,然后用for循環(huán)對(duì)其進(jìn)行遍歷,然后統(tǒng)計(jì)其每次訪問內(nèi)存平均耗時(shí)情況。核心代碼如下:

/*
 * 初始化一個(gè)指定大小的數(shù)組
 * 通過對(duì)內(nèi)存大小不斷變化,逐步制造出一級(jí)、二級(jí)、三級(jí)溢出、最后穿透到內(nèi)存IO的case。
 */
void init_data(double *data, int n){
    int i;
    for (i = 0; i < n; i++) {
        data[i] = i;
    }
}

/*
 * 對(duì)內(nèi)存以指定步長(zhǎng)執(zhí)行順序訪問
 * 這塊測(cè)試代碼有個(gè)加法額外開銷。算術(shù)運(yùn)算是以CPU周期來計(jì)算的,比內(nèi)存周期要快。
 * 所以本測(cè)試中忽略這個(gè)算術(shù)運(yùn)算帶來的額外開銷。
 */
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; 
}
/*
 * 對(duì)內(nèi)存執(zhí)行隨機(jī)訪問,其中random_index_arr是提前隨機(jī)好的數(shù)組下標(biāo)。  
 * 這實(shí)際比順序多了一次內(nèi)存IO,但由于對(duì)random_index_arr的訪問時(shí)順序的,而且數(shù)組要相對(duì)小很多。
 * 所以它絕大部分情況下都能命中高速緩存,可以忽略這個(gè)小內(nèi)存IO對(duì)主測(cè)試過程的影響。
 */
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; 
}

實(shí)驗(yàn)結(jié)果數(shù)據(jù)

注意,不同的機(jī)器的實(shí)驗(yàn)數(shù)據(jù)會(huì)略有差異。越新的機(jī)器數(shù)據(jù)表現(xiàn)越好,因?yàn)橛布?shù)不一樣,但是偏差不會(huì)太大。

case 1: 內(nèi)存2k,步長(zhǎng)從1到64

步長(zhǎng)(字節(jié)) 延遲(ns)
1 1.28
9 1.28
17 1.33
25 1.35
33 1.30
41 1.41
49 1.45
57 1.40
64 1.27

結(jié)論:內(nèi)存足夠小的時(shí)候,一級(jí)高速緩存完全能裝的下,因此絕大部分的請(qǐng)求都是一級(jí)緩存IO,內(nèi)存IO并沒有怎么進(jìn)行。所以無論步長(zhǎng)怎么變化,延遲基本都在1ns左右。

case2:內(nèi)存從8k到64M,步長(zhǎng)固定8

內(nèi)存大小 延遲(ns)
8k 1.32
32k 1.27
64k 1.73
256k 2.03
512k 2.62
2m 2.62
8m 2.88
16m 5.17
64m 5.84

結(jié)論:內(nèi)存越來越大,CPU的高速緩存逐漸hold不住了,越來越多的請(qǐng)求穿透到了內(nèi)存。延遲已經(jīng)逐步接近真實(shí)的內(nèi)存IO了

case3:內(nèi)存從8k到64M,步長(zhǎng)固定64

內(nèi)存大小 延遲(ns)
8k 1.25
32k 1.25
64k 1.74
256k 2.03
512k 2.47
2m 2.47
8m 3.29
16m 7.73
64m 8.89

結(jié)論:步長(zhǎng)從case2的8增加到了本case的64以后,空間局部性進(jìn)一步被打亂。內(nèi)存在2m以下的時(shí)候,延遲沒有太大變化。因?yàn)殡m然局部性亂,但CPU緩存都能hold住,穿透到內(nèi)存的情況不多。但當(dāng)內(nèi)存編程8m或者64m的情況下,高速緩存命中率就會(huì)降低,因此平均延遲也增加到了9ns左右。這個(gè)數(shù)字其實(shí)就是內(nèi)存在順序IO時(shí)的大概耗時(shí)。

case4 內(nèi)存8k到64m,徹底隨機(jī)訪問

內(nèi)存大小 延遲(ns)
8k 2.40
32k 2.40
64k 2.40
256k 2.40
512k 4.80
2m 4.80
8m 19.20
16m 24.0
64m 38.40

結(jié)論:當(dāng)徹底隨機(jī)訪問以后,不光是穿透到內(nèi)存的IO變多了,而且穿透到內(nèi)存的IO也增大了行地址變化的概率。也就是說內(nèi)存IO也不能以高效的順序IO進(jìn)行了,更多的請(qǐng)求進(jìn)行的是行列地址都變了的隨機(jī)IO。因此64m的情況下,內(nèi)存IO延遲增加到了將近40ns

最終結(jié)論

  • 內(nèi)存順序IO延遲在9ns左右
  • 內(nèi)存隨機(jī)IO延遲在40ns左右
  • 如果程序局部性寫的足夠好,操作系統(tǒng)會(huì)幫你用CPU緩存訪問減少低效的內(nèi)存IO。

個(gè)人公眾號(hào)“開發(fā)內(nèi)功管理”,打通理論與實(shí)踐的任督二脈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn) # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    呂品?閱讀 10,152評(píng)論 0 44
  • 今天看到一位朋友寫的mysql筆記總結(jié),覺得寫的很詳細(xì)很用心,這里轉(zhuǎn)載一下,供大家參考下,也希望大家能關(guān)注他原文地...
    信仰與初衷閱讀 4,839評(píng)論 0 30
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,680評(píng)論 1 32
  • 1.JVM 堆內(nèi)存和非堆內(nèi)存 堆和非堆內(nèi)存按照官方的說法:“Java 虛擬機(jī)具有一個(gè)堆(Heap),堆是運(yùn)行時(shí)數(shù)據(jù)...
    yanzhu728閱讀 1,009評(píng)論 0 0
  • 任何一個(gè)開發(fā)者都知道內(nèi)存比磁盤訪問要快,都會(huì)用內(nèi)存去cache一些東西,代替更耗時(shí)的磁盤或網(wǎng)絡(luò)IO。但是你的內(nèi)存究...
    yanfeizhang閱讀 4,925評(píng)論 0 0

友情鏈接更多精彩內(nèi)容