zero, 说明:

为了对系统的性能进行优化,因而需要分析系统的性能瓶颈在哪里,需要对系统的一些设备的性能进行测试。这一篇文章用于记录内存带宽的测试,如果文章中有不足的地方,请不吝赐教。

一,背景知识:

下面提供一些关于内存结构的链接,如若侵权,请联系我进行删除.
              CMU MainMemory
              圖解RAM結構與原理,系統記憶體的Channel、Chip與Bank

二,实验环境

所使用的CPU的信息如下 : 一台机器有24个物理核

       所使用的内存条的信息如下 : 一台机器有8个相同的内存条

三,初步测试

最初打算寻找一些现有的工具对直接进行测试,测试的结果如下:
       1,使用dd命令进行测试,命令如下 :

  dd if=/dev/zero of=/dev/shm/A bs=2M count=1024

测试的结果如下 :
       
       显然,2.7GB/s的内存带宽这个结果,是不能令人满意的。

2,使用mbw命令进行测试,命令如下:

 mbw 16 -b 4096

测试的结果如下 : (进行了多次测试,其中选取测试结果表现最好)
       
       由于mbw使用了三种不同的方式进行了测试 :
       (1), 使用memcpy将一个数组复制到另一个数组 :
              其avg bandwidth为5.2GB/s,但是由于需要从一个数组复制到另一个,所以应该包括内存读和内存写,假设读写速度一样的话,其avg bandwidth应该为 5.2 * 2 = 10.4GB/s
        (2), 使用for循环将一个数组复制到另一个数组 :
              同理,可以看出其avg bandwidth为 12.2 GB/s
        (3), 使用mempcpy将一个块复制到一个数组 :
              由于只是重复地复制一个块,所以可以看做只有内存写操作,故其avg bandwidth为 12.2GB/s

3, 使用sysbench进行测试,测试命令如下

 sysbench --test=memory --memory-block-size=4K --memory-totol-size=2G --num-threads=1 runsysbench --test=memory --memory-block-size=4K --memory-totol-size=2G --num-threads=16 run

其中第一个命令使用了1个线程,第二个命令使用了16个线程,测试结果如下 :


       从上图可以看出,单线程的情况下,bindwidth为 5.94GB/s


       从上图可以看出,多线程的情况下,bindwidth为 7.8 GB/s
       由于目前尚未了解sysbench是将一个块重复复制到一个数组中,还是将一个数组复制到另一个数组中。所以假设是将一个块重复复制,那么其bandwidth在单线程和多线程的情况下分别为5.94GB/s , 7.83GB/s

四,理论峰值

后来和同学的讨论下,可以根据内存条的参数计算bandwidth的峰值,计算如下 :
       因为内存条的频率为2400 MHz, 数据宽度为64bit,假设一个时钟周期能进行一次操作的话,那么最高的带宽为 : x=24001000×648=19.2GB/sx = \dfrac{2400}{1000} \times \dfrac{64}{8} = 19.2 GB/s x=10002400​×864​=19.2GB/s

所以查找到了一些资料[1] [2],打算根据这些资料,自己写一个程序来测试内存的带宽。

五,自行测试

原本打算使用将一个数组复制到另一个数组的方式,但是考虑到这样需要读一遍内存,再写一遍内存,感觉效率比较低。所以采用将一个字符直接写到一个数组中的方法,这样可以认为只有单独的写操作,因为一个字符可能会存放在寄存器或cache中,就无需重复地读取内存。

1,基本的测试函数体如下:

 #define G (1024*1024*1024LL)#define NS_PER_S 1000000000.0#define INLINE inline __attribute__((always_inline))char src[2*G] __attribute__((aligned(32))); char dst[2*G] __attribute__((aligned(32)));int main(int argc, char* argv[]){struct timespec start, end;unsigned int length = (unsigned int)2*G;memset(src, 1, length);memset(dst, 0, length); //这两个memset的作用是访问数组后,保证能加载所有的内存页,防止由于缺页中断影响测试的结果clock_gettime(CLOCK_MONOTONIC, &start);/** 这里是不同实现的memset函数*/clock_gettime(CLOCK_MONOTONIC, &end);double timeuse =  NS_PER_S * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec;double s = timeuse / NS_PER_S;printf("timeval = %lf, io speed is %lf\n", s, length/G/s);return 0;}

2, 使用memset()测试
       编译使用的命令 :

 gcc ./memory_io_v4.c -o memory_io -O3 -mavx -mavx2 -msse3 -lrt

1),使用简单的for循环语句:

  static INLINE void function_memset_for(char *src, char value, unsigned int length){for(unsigned int i = 0; i <  length; i++)src[i] = value;}

这个函数的结果测试如下:
       
       显然,这结果不能令人满意。

2), 使用CSAPP中第5章提到的k路展开,k路并行(这里使用的k = 4) :

  static INLINE void function_memset_k_fold(char *src, char value, unsigned int length){for(unsigned int i = 0; i < length; i+=4){src[i] = value;src[i+1] = value;src[i+2] = value;src[i+3] = value;}}

这是函数的测试结果如下:
       
       结果与直接使用for循环的差别不大,原因可能是由于编译器进行优化,但具体还需要研究一下汇编,但是由于没有系统地学过汇编 : -( ,所以还需要进一步探究。。。。

3), 使用操作系统提供的memset()函数 :

 static INLINE void function_memset(char *src, char value, unsigned int length){memset(src, value, length);}

测试结果如下:
       
       可以看出,内存的带宽接近 8 GB/s, 比上面的函数高出许多,但还是不能达不到理想状态。

4), 使用SIMD指令 :

static INLINE void function_memset_SIMD_32B(char *src, char value, unsigned int length){__m256i *vsrc = (__m256i *)src;__256i ymm0 = _mm256_set_epi8(value, value, value, value, value, value, value, value,value, value, value, value, value, value, value, value,value, value, value, value, value, value, value, value,value, value, value, value, value, value, value, value);unsigned int len  = length / 32;for(unsigned int i = 0; i < len; i++)_mm256_storeu_si256(&vsrc[i], ymm0);}

测试结果如下:
       
       可以看出,其性能与直接使用memset()的效果一样。目前猜测其原因是在不同的架构中,这些基本函数都会使用汇编语言进行实现,从而确保更高的性能,所以两者能够达到同样的性能。(这个需要在学完汇编后进一步验证。)。
       而且在实验过程中,还分别使用了一次读取64bit, 128bit的SIMD指令,其结果和上面所使用的一次读取256bit的SIMD指令的结果相差不大,这里的原因也需要探究。

5), 根据参考资料[2], 可以使用Non-temporal Instruction,避免一些cache的问题

 static INLINE void function_memset_SIMD_s32B(char *src, char value, unsigned int length){__m256i *vsrc = (__m256i *)src;__m256i ymm0 = _mm256_set_epi8(value, value, value, value, value, value, value, value,value, value, value, value, value, value, value, value,value, value, value, value, value, value, value, value,value, value, value, value, value, value, value, value);unsigned int len  = length / 32;for(unsigned int i = 0; i < len; i++)_mm256_stream_si256(&vsrc[i], ymm0);}

测试的结果如下 :
       
       可以看出,能够达到了 15.5GB/s 的带宽。
       关于为什么相比之前的能达到这么高的bandwidth,请见资料[2]中的解释,具体如下,由于每次写32B,并且每个cache line的大小为32B,也就是如果不使用Non-temporal Instruction, 每次写的时候,先写到cache line中,最后会将cache line写到内存,由于是遍历访问数组,即每次写32B,需要先将数组从内存读到cache line,再写cache line,最后cache line写回内存,相当于每次需要两次内存访问;而使用了Non-temporal Instruction,可以直接写到内存中,这样只需要一次内存访问。即使这样,但是还是不尽人意。

6), 使用rep指令,这里使用与参考资料[2]一样的程序,但是效果却不佳,结果如下 :
       
       涉及汇编指令的东西目前都尚不能解决,需要作进一步探究。

7), 使用Multi-core
       关于参考资料中使用Multi-core的实验还未做,因为可能与NUMA架构相关。所以暂且放一放。

六,总结

这篇文章记录了测试内存带宽的过程,包括使用的一些Ubuntu系统的测试工具dd, mbw, sysbench,以及自己根据资料编写的代码,可以看出,最高能到达到一个内存条带宽的80%。
       主要存在下面的三个问题还未解决 :
              1, 关于一些涉及到汇编指令的测试结果还未能解释。
              2, 根据内存条的标签的参数,以及机器的架构(NUMA, dual channel),可以计算出每个内存条的峰值为19.6GB/s, 并且机器是四通道,所以理论上一个Socket能达到的内存峰值为78.4GB/s.有没有什么方法能够利用机器提供的多通道来达到这个内存bandwidth呢?
              3,有没有办法到达一个内存条更高的bandwidth,而不只是80%?

关于以上两个问题,如果有大佬能够指点一二,或者提供一些资料,不胜感激。

七,参考资料

[1],SIMD Instructions Official
       [2],Achieving maximum memory bandwidth
       [3],Testing Memory I/O Bandwidth

设备性能测试 : 内存带宽的测试相关推荐

  1. 服务器内存延迟,内存带宽、延迟性能测试

    内存带宽.延迟性能测试 华为FusionServer 2488H V5服务器主板集成了48个内存插槽,但送测机器配备有限.由于送测机型整合了四通道内存控制器,这样使得平台的内存带宽得到了明显的提升. ...

  2. Linux内存带宽的一些测试笔记

    最近要测一下设备的内存性能,于是找了些资料,用了些工具,写了些笔记.那个设备是intel的CPU,而intel有个很强大的工具,叫PTU,但我死活找不到下载链接,找到的文章是几年前写的,从那篇文章看是 ...

  3. Linux下stream内存带宽测试参数和示例详解附源码(总结)

    目录 一.简介 二.使用简介 2.1 测试内容简介 2.2 编译参数简介 2.3 具体参数示例 三.源码下载及使用 四.其他相关知识链接 FIO测试硬盘性能参数和实例总结 一.简介 本文通过实例详细讲 ...

  4. stream.c 内存带宽测试

    内存带宽测试工具:stream 介绍 Stream测试是内存测试中业界公认的内存带宽性能测试基准工具,作为衡量服务器内存性能指标的通用工具. 2. 原理 申请了三个巨大的双精度浮点数组a[N], b[ ...

  5. 【开发工具】【stream】内存带宽测试工具(Stream)的使用

    获取更多相关的嵌入式开发工具,可收藏系列博文,持续更新中: [开发工具]嵌入式常用开发工具汇总帖 Stream简介 STREAM是一套综合性能测试程序集,通过fortran和C两种高级且高效的语言编写 ...

  6. bandwidth 0.32k 发布,内存带宽测试工具

    bandwidth 0.32k 修复了一些小的 AVX 问题. Bandwidth 是一个内存带宽测试的基准工具,但它也可以测量网络带宽.它可以测量每个内存系统的最大内存带宽,包括主内存,L1和L2缓 ...

  7. K1 Power Linux上Stream测试内存带宽

    K1 Power Linux上Stream测试内存带宽 本篇文章介绍在K1 Power Linux上理论内存带宽结算,实测内存带宽方法,衡量服务器的内存带宽水平.需要说明的是,对于Scale UP的P ...

  8. 嵌入式里如何给内存做压力测试?不妨试试memtester

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是内存读写正确性压力测试程序memtester. 在嵌入式系统中,内存(RAM)的重要性不言而喻,系统性能及稳定性都与内存息息相关.关于内 ...

  9. 【Android 高性能音频】OboeTester 音频性能测试应用 ( Oboe 输出测试参数 | API 选择 | 音频输出设备选择 | 采样率 | 通道 | 采样格式 | 播放偏好 )

    文章目录 一.Oboe 输出测试参数面板 二.Oboe 输出测试参数 API 及 设备选择 三.Oboe 输出测试参数 音频参数 四.Oboe 输出测试参数 播放偏好 五.Oboe 输出测试参数 ( ...

  10. Unity手游iOS内存分析和测试

    内存是Unity手游的硬伤,如果没有做好内存的相关管理和详细的测试,游戏极有可能出现卡顿.闪退等影响用户体验的现象.在此,笔者为我们介绍了一些Unity手游内存分析和测试过程中比较实用的测试场景案例. ...

最新文章

  1. HDFS分布式文件系统
  2. IOS APP 国际化 程序内切换语言实现 不重新启动系统(支持项目中stroyboard 、xib 混用。完美解决方案)
  3. python路径找类并获取静态字段
  4. JVM系列之:详解java object对象在heap中的结构
  5. 官宣,11月8号,.NET6+64位VS璀璨面世!
  6. Java 内存模型 JMM 详解
  7. 存储器容量扩展——位扩展、字扩展
  8. File存对象--android 的File存储到SD卡();
  9. jieba结巴分词--关键词抽取_初学者 | 知否?知否?一文学会Jieba使用方法
  10. SQL Cumulative Sum累积求和
  11. Hive练习之join
  12. 如何在股票软件画波浪?波浪原理?初级应用画线
  13. 5G到底有多快?和4G相比的直接数据给你更直观感受
  14. xp系统从u盘启动计算机,一键u盘装xp系统,教您如何使用U盘装xp系统
  15. H3C交换机关闭STP生成树协议的方法
  16. datax(13):源码解读Column-datax中的数据类型
  17. java小练习:乘法表的实现(不止是9)
  18. 材料力学 4.弯曲内力
  19. 将mytest目录归档 ,并将其压缩成gz格式,列出归档文件内容
  20. 就差你了,于腾格里沙漠无人之境,不止跨年

热门文章

  1. 2022java面试总结
  2. 起风了用计算机打,《起风了》计算机简谱
  3. markdown如何设置图片大小_Markdown 调整图片位置与大小
  4. 魔方心得-20180227
  5. 全球与中国电动车PTC辅助加热器市场深度研究分析报告
  6. 马歇尔·卢森堡《非暴力沟通》——备忘
  7. 如何扩展硬盘以及删除恢复分区?
  8. mysql 误删表怎么恢复_怎么恢复navicat删除的表
  9. 三目表达式的错误使用
  10. ECCV2022 | 大工(卢湖川团队)提出用于图像-文本匹配的深度跨模态投影学习