在前面的文章你能估算出你的内存的访问延时吗?中,我们对内存的访问延迟进行了理论上的研究。那么今天我们实际编写代码进行一下测试。 看一下实践能否和理论匹配的上。
为了衡量各种情况下的内存带宽情况。我们用c写了一个double类型的数组,然后用for循环对其进行遍历,然后统计其每次访问内存平均耗时情况。核心代码如下:
/*
* 初始化一个指定大小的数组
* 通过对内存大小不断变化,逐步制造出一级、二级、三级溢出、最后穿透到内存IO的case。
*/
void init_data(double *data, int n){
int i;
for (i = 0; i < n; i++) {
data[i] = i;
}
}
/*
* 对内存以指定步长执行顺序访问
* 这块测试代码有个加法额外开销。算术运算是以CPU周期来计算的,比内存周期要快。
* 所以本测试中忽略这个算术运算带来的额外开销。
*/
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;
}
/*
* 对内存执行随机访问,其中random_index_arr是提前随机好的数组下标。
* 这实际比顺序多了一次内存IO,但由于对random_index_arr的访问时顺序的,而且数组要相对小很多。
* 所以它绝大部分情况下都能命中高速缓存,可以忽略这个小内存IO对主测试过程的影响。
*/
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;
}
实验结果数据
注意,不同的机器的实验数据会略有差异。越新的机器数据表现越好,因为硬件参数不一样,但是偏差不会太大。
case 1: 内存2k,步长从1到64
步长(字节) | 延迟(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 |
结论:内存足够小的时候,一级高速缓存完全能装的下,因此绝大部分的请求都是一级缓存IO,内存IO并没有怎么进行。所以无论步长怎么变化,延迟基本都在1ns左右。
case2:内存从8k到64M,步长固定8
内存大小 | 延迟(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 |
结论:内存越来越大,CPU的高速缓存逐渐hold不住了,越来越多的请求穿透到了内存。延迟已经逐步接近真实的内存IO了
case3:内存从8k到64M,步长固定64
内存大小 | 延迟(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 |
结论:步长从case2的8增加到了本case的64以后,空间局部性进一步被打乱。内存在2m以下的时候,延迟没有太大变化。因为虽然局部性乱,但CPU缓存都能hold住,穿透到内存的情况不多。但当内存编程8m或者64m的情况下,高速缓存命中率就会降低,因此平均延迟也增加到了9ns左右。这个数字其实就是内存在顺序IO时的大概耗时。
case4 内存8k到64m,彻底随机访问
内存大小 | 延迟(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 |
结论:当彻底随机访问以后,不光是穿透到内存的IO变多了,而且穿透到内存的IO也增大了行地址变化的概率。也就是说内存IO也不能以高效的顺序IO进行了,更多的请求进行的是行列地址都变了的随机IO。因此64m的情况下,内存IO延迟增加到了将近40ns
最终结论
- 内存顺序IO延迟在9ns左右
- 内存随机IO延迟在40ns左右
- 如果程序局部性写的足够好,操作系统会帮你用CPU缓存访问减少低效的内存IO。
个人公众号“开发内功管理”,打通理论与实践的任督二脉。
网友评论