在《記憶體隨機也比順序訪問慢,帶你深入理解記憶體io過程》一文中,我們理解了記憶體io的內部實現過程,知道了記憶體的隨機io比順序io要慢,並對延遲時間進行了大概的估算。那麼我們今天來用**的方式來時間一下,看看在我們的專案工程中,記憶體訪問的在不同的訪問場景下延時究竟是個什麼表現。
測試原理就是定義乙個指定大小的double(8位元組)陣列,然後以指定的步長去迴圈。這裡面的變數有兩個。核心**如下:
void init_data(double *data, int n)
}
void seque_access(int elems, int stride)
sink = result;
}
在這個核心**的基礎上,我們有兩個可調節變數:
另外說明一下,這個**測試中考慮的幾個額外的開銷的處理情況。場景一: 固定陣列大小2k,調節步長步長11.加法開銷:由於加法指令簡單,乙個cpu週期就可完成,cpu週期比記憶體週期要快,所以暫且忽略它。
2.耗時統計:這涉及到高開銷的系統呼叫,本實驗通過跑1000次取一次耗時的方式來降低影響。
9172533
4149
57延時ns
1.28
1.28
1.33
1.30
1.30
1.41
1.45
1.4陣列足夠小的時候,l1 cache全部都能裝的下。記憶體io發生較少,大部分都是高效的快取io,所以我這裡看到的記憶體延時只有1ns左右,這其實只是虛擬位址轉換+l1訪問的延時。
場景二: 固定步長為8,陣列從32k到64m
陣列大小
32k64k
256k
512k
2m8m
16m64m
延時ns
1.27
1.73
2.03
2.62
2.62
2.88
5.17
5.84
當陣列越來越大,cache裝不下,導致穿透快取記憶體,到記憶體實際io的次數就會變多,平均耗時就增加
場景三: 步長為32,陣列從32k到64m
陣列大小
32k64k
256k
512k
2m8m
16m64m
延時ns
1.25
1.74
2.03
2.47
2.47
3.29
7.73
8.89
和場景二相比,步長變大以後,區域性性變差,穿透的記憶體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的測試中,我們要徹底打亂這個規律,提前隨機好乙個下標陣列,實驗時不停地訪問陣列的各個隨機位置。
void init_data(double *data, int n)
}
void random_access(int* random_index_arr, int count)
sink = result;
}
這實際比上面的實驗多了一次記憶體io,但由於對random_index_arr的訪問時順序的,而且該陣列也比較小。我們假設它全部能命中快取記憶體,所以暫且忽略它的影響。隨機實驗場景: 陣列從32k到64m陣列大小
32k64k
256k
512k
2m8m
16m64m
延時ns
2.42.4
2.44.8
4.819.2
2438.4
這次的陣列訪問就沒有步長的概念了,全部打亂,隨機訪問。當資料集比較小的時候、l1、l2、l3還能抗一抗。但當增加到16m、64m以後,穿透到記憶體的io情況會變多,穿透過去以後極大可能行位址也會變。在64m的資料集中,記憶體的延時竟然下降到了38.4ns,和我們估算的也基本一致。
有了實驗資料的佐證,進一步證實了上一文《深入理解記憶體io的實際過程!》的結論。記憶體也存在隨機訪問比順序訪問慢的多的情況,大概是4:1的關係。所以不要覺得記憶體很快,就用起來太隨性了!
開發內功修煉之記憶體篇**:
JMeter BeanShell在實際測試中的應用
beanshell最常用的場景 beanshell除了可以import外部jar包外,還有乙個十分好用的特性,就是可以可以引用外部beanshell指令碼。aa bb cc scripta.bsh void printinfo scriptb.bsh source aa bb cc scripta....
記憶體順序(Memory Order)
這篇文章主要介紹記憶體順序 memory order 然後會結合 rocksdb leveldb 中的 skiplist 原始碼來具體分析 rocksdb skiplist 如何通過記憶體順序和原子操作做到無鎖併發 一寫多讀 memory order 記憶體順序描述了計算機 cpu 獲取記憶體的順序...
libvirt獲取實際記憶體的實現
libvirt獲取實際記憶體的實現 libvirt可以通過virdomaingetinfo 來得到虛機資訊 struct virdomaininfo libvirt獲取虛擬機器實際記憶體的實現方法 1.通過libxl domain info provides a way to get the str...