开发内功修炼@张彦飞开发内功修炼@张彦飞

talk is cheap,
show me the code!

揭穿内存厂家“谎言”,实测内存带宽真实表现

带宽是存储系统一个的一个非常重要的衡量指标,内存带宽指的是CPU从内存读取或写入数据到内存的速率。我们今天来思考一个问题,在你的应用程序里内存的访问带宽能够达到多少?

各代内存带宽规格

理论上内存带宽的计算公式是:Band Width = Speed * Data Width。在这个公式的基础上,我们来看一下各代规格的内存带宽表现。

图1.png

从上图来看,DDR3代的内存 DDR3代1066MHz的带宽都已经达到了8.5GB/s, DDR4代3200MHz更是达到了惊人的25.6G。 厂家在进行销售的时候也都是用这个公式来算,言外之意就是告诉你:“我家的产品非常快,大家赶快来买啊!”

厂家这么宣传似乎也不算错,但是有诱导性的谎言在其中。 厂家这个数据的基础是内存严格以顺序IO的方式工作,而且把传输的内存地址也当数据来看,进而计算出的结果。但我们在《内存随机访问也比顺序慢,带你深入理解内存IO过程》中明白了的是,内存在并一定是所有的Data Speed周期都在源源不断往外吐数据。在CPU传入了行地址后,内存打开一行需要tRCD+tRP个时钟周期的延迟。输入列地址后,又需要等待CL个时钟周期。而内存作为易失性存储元器件,又需要定时对所有的行进行充电,来保证数据不丢失。所以,在实践中,我们并不是总是能够达到厂家宣传的理论带宽值。

注意:理论带宽值计算时采用的是内存的Speed,也就是其数据频率。而内存的延迟呢,用的是时钟周期。现代的内存在时钟周期的上沿和下沿都可以传输数据,所以数据频率比Speed又慢了一半。对于笔者Speed为1067MHz的内存条,其时钟频率是553MHz。

邓爷爷说过,实践是检验真理的唯一标准。我们今天就来进行一下实际的测试,看看内存的到底每秒能给我们吐出多少的数据。和前文《实际测试内存在顺序IO和随机IO时的访问延时差异》的测试方法类似,我们今天对方法进行下小改动,用它来测试带宽。

顺序IO情况核心测试方法

测试代码主体上和延迟测试差不多,定义一个指定大小的数组,然后以指定步长对其进行访问。

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;  
}

考虑到内存对齐能提高性能,所以公平起见,我们每次都是按内存位宽去取的(一个double 8个字节,正好是一个内存位宽)。带宽就是一秒内访问过的字节总数,所以我们通过如下代码进行计算。

result = total_accessed_bytes * 1000  / used_microseconds;

顺序IO情况带宽测试结果

笔者的服务器上的内存条是DDR3,1067MHz,延迟参数为7-7-7-24。我们进行了多场景的测试。

场景一: 固定数组大小2K,调节步长

步长1917253341495764
带宽MB618562636001593061165660552356826316

数组为2k,足够小到L1 cache全部都能装的下。这时候其实基本实际内存IO发生的很少,大部分都是更高效的L1 cache的IO,在CPU内部就完成了。但最高值也才6G而已,也没有达到厂家宣称的8GB。

场景二: 固定步长为8,数组从32K到64M

数组大小8K32K64K256K512K2M8M16M64M
带宽MB606663144577396030593047276915511386

数组越大,Cache越装不下,数据访问的IO会更多地往后穿透到L2、L3和内存。L1、L2、L3和内存IO的性能依次递减,因此数组越大,平均带宽就会越低。

场景三: 步长为32,数组从32K到64M

数组大小8K32K64K256K512K2M8M16M64M
带宽MB63766394464737353193317914811002902

步长增加后,穿透到内存的次数进一步增加,带宽进一步下降。这个时候,我们应用程序视角看到的数据带宽已经下降到1GB以下了。

再测随机IO情况

前面的测试情况,虽然步长也在变化,但都是有序递增。这样内存的连续两次IO之间,虽然列地址会变,但是行地址极有可能不发生变化,因此效率还是算比较高。 我们这次是来彻底随机进行访问,再来看一下。

随机访问,数组从8K到64M

数组大小8K32K64K256K512K2M8M16M64M
带宽MB637858504764340531932948967749474

当数组比较小的时候,虽然乱序访问,但是cache能兜住,因此内存IO实际上发生的很少。但当数组增加到64M的时候,再加上哪一级Cache都兜不住了。再加上访问又足够随机,因此都穿透到了内存,而且行的行地址也极大可能发生变化。这时,内存带宽竟然下降到了474M。

结论

厂家在宣传的内存带宽数据的时候,用的是严格顺序IO情况下的数据,而且把地址的传输也当数据算了。但是厂家不会告诉你,在实际中内存的带宽并没有你想象的那么快,在随机IO工作模式的情况下,带宽只有474M而已。 现在SSD固态硬盘顺序IO也差不多能达到这个数量级了。所以,我们以后不要鲁莽地说内存比硬盘要快很多。快不快,关键取决于你怎么用!

写在最后,由于我的这些知识在公众号里文章比较分散,很多人似乎没有理解到我对知识组织的体系结构。而且图文也不像视频那样理解起来更直接。所以我在知识星球上规划了视频系列课程,包括硬件原理、内存管理、进程管理、文件系统、网络管理、Golang语言、容器原理、性能观测、性能优化九大部分大约 120 节内容,每周更新。加入方式参见我要开始搞知识星球啦如何才能高效地学习技术,我投“融汇贯通”一票

Github:https://github.com/yanfeizhang/coder-kung-fu
关注公众号:微信扫描下方二维码
qrcode2_640.png

本原创文章未经允许不得转载 | 当前页面:开发内功修炼@张彦飞 » 揭穿内存厂家“谎言”,实测内存带宽真实表现