MLC - Intel内存延迟测试工具
介绍
影响应用程序性能的一个重要因素是应用程序从处理器缓存和从内存子系统获取数据所消耗的时间。在激活了非统一内存访问架构(Non-Uniform Memory Access, NUMA)的多插槽系统(multi-socket system)中,本地内存延迟和交叉插槽内存延迟(cross-socket memory latencies)之间差别非常明显。
除了延迟,带宽(bandwidth, b/w)也在决定性能时扮演重要角色。
所以,检测这些延迟和带宽就是建立系统测试基线和性能分析非常重要的因素。
Intel Memory Latency Checker(Intel MLC)是一个测试内存延迟和带宽的工具,并且可以测试延迟和带宽随着系统负载增加的变化。这个工具也提供了一些选项用于更好细粒度调查从特定处理器核心到缓存或内存的一系列选项的测试。
安装
Intel MLC支持Linux和Windows:
Linux
复制
mlc
执行程序到任意目录MLC动态链接了GNU C库(glibc/lpthread),所以需要确保系统中安装了对应库
使用MLC需要root权限,因为这个工具修改H/W prefetch control MSR(硬件预取控制寄存器)来
激活/禁用
预取器 用于延迟和带宽测试。(在虚拟机测试中需要使用-e
参数关闭此功能)系统需要加载MSR驱动(不在安装包中),通常可以使用
modprobe msr
命令完成。
在RHEL/CentOS 7系统中,无需使用
modprobe msr
,因为内核编译时已经built-in了MSR支持。通过以下命令可以检查使用内核是否支持MSR
/dev/cpu/CPUNUM/msr
提供了读写x86处理器的model-specific registers (MSRs) (型号特定寄存器)的接口。通过打开这个
msr
文件并寻找MSR编号位移,然后读或者写这个8字节块来实现寄存器访问。超过8字节的I/O传输则表示多次读或者写相同的寄存器。
Windows
将
mle.exe
和mlcdrv.sys
驱动复制到相同目录。这个mlcdrv.sys
驱动用于修改硬件预取器设置。
有两个执行程序(mlc
和mlc_avx512
)。mlc
则支持SSE2
和AVX2
指令。mlc_avx512
使用支持AVX512
指令的较新工具链编译,是支持SSE2/AVX2
的超集。所以,mlc_avx512
也可以运行在不支持AVX512
的处理器。
不管处理器是否支持AVX512
,默认情况下,mlc_avx512
都不会使用AVX512
指令,除非在命令行参数使用了-Z
参数。
建议先使用mlc_avx512
,如果你的系统没有较新版本的glibc,则可以回退使用mlc
。
H/W 预取器控制
在新型的Intel处理器上精确测试内存延迟是非常困难的,因为它有复杂的硬件预取器。Intel MLC在测试延迟时会自动禁用这些预取器,并且在测试完成后自动恢复预期器原状态。预期器控制是通过修改MSR实现(Disclosure of H/W prefetcher control on some Intel processors),所以在Linux上需要root权限。在Windows平台,提供了签名的驱动用于访问MSR。
MLC测试内容
当Intel MLC没有使用任何参数来运行时,它会自动检测系统拓扑并按照以下步骤测试:
从每个socket发出到每个可用socket的对空闲内存延迟测试
峰值内存带宽测试(假设所有访问都是本地内存)来请求不同数量的读和写
从每个socket发出到每个可用socket的请求(交叉)测试内存带宽值
不同带宽情况下的延迟
处理器缓存间的延迟
Intel MLC也提供了命令行参数来调整控制延迟和带宽的测量。
透明大页(Transparent Huge Pages)的影响
一些Linux发行版支持透明大页(Transparent Huge Pages, THP)提供了高级内存管理支持。这个功能自动合并小(4KB)内存页成为大(2MB)内存页,并且可能将内存迁移到一个单一节点。
然而,Intel MLC依赖内存保留在本地已确保精确测试本地内存延迟和带宽。所以,需要禁用这个功能:
生成延迟 vs. 带宽数据
Intel MLC的一个主要功能是测量带宽需求增加时的延迟。为了加快实现,MLC创建了多个线程,线程数等于主机逻辑处理器数量减 1。这些线程用户生成负载(以下,这些线程被引用为负载生成线程或者带宽生成线程)。这个负载生成线程的主要功能是尽可能生成更多的内存引用。此时系统负载类似,剩下的一个CPU(也就是没有用于产生负载的CPU)运行了一个用于测量延迟的线程。这个线程通常运行在cpu#0
,被称为延迟线程(latency thread)和分发依赖的读。基本上,这个线程穿过点阵,这个点阵的每个点指向下一个,这样创建读的依赖。每个读取所花费的平均时间提供了延迟度量。
基于通过负载生成线程产生的负载,这个延迟是变化的。一旦每个短暂的时间负载生成线程通过注入延迟来自动限流负载的产生,这样就可以在不同的负载下测试延迟。默认,运行延迟线程的处理器核心会禁用硬件预取器,这样延迟线程就是顺序访问方式。
默认情况下,每个负载生成线程会pin在一个逻辑cpu上。例如,在激活了超线程的10核系统上,MLC创建18个负载生成线程并保留物理核0来运行延迟线程。每个负载生成线程可以配置成生成对缓存层级(cache hierarchy)生成不同程度的读和写。每个线程分配一个buffer用于读并且一个独立的buffer用于写(任何线程之间没有共享数据)。通过相应的不同大小缓存,可以确保引用满足任何缓存级别或者由内存提供服务。
有一些选项可以控制负载生成线程数量,每个线程使用的缓存大小,在哪里分配它们的内存,读写的比例以及顺序存取或随机存取。
缓存到缓存(cache-to-cache)传输延迟
MLC v3.0增加了支持缓存到缓存传输延迟度量。这个测量方法的思路是在L1/L2/L3缓存填充数据(bring in lines into L1/L2/L3)然后将控制传给另外一个线程(这个线程运行在相同socket或不同socket的另外一个CPU核心上)。这个线程然后读取相同的数据以强制已经具有这些数据的缓存实现缓存到缓存的数据传输。
这样通过操作初始化线程要么只读取数据到干净状态、要么修改数据使之进入M转台,就可以同时测量Hit(命中干净的行)和HitM(命中修改状态的行)的延迟。
命令行参数
不使用任何参数调用Intel MLC会测量一系列内容。使用参数则可以指定特定任务:
mlc --latency_matrix
输出本地和交叉socket内存延迟
mlc --bandwidth_matrix
输出本地和交叉socket的内存带宽
mlc --peak_bandwidth
输出在不同读写速率下本地内存访问的尖峰内存带宽
mlc --idle_latency
输出平台的空闲内存延迟
mlc --loaded_latency
输出平台有负载的内存延迟
mlc --c2c_latency
输出平台 hit/hitm 延迟
mlc -e
输出不修改预取器设置的测试结果
当使用
-e
参数时,MLC在所有测量中都不会修改硬件预取器。这个参数适合在虚拟机内部测试(无法修改MSR)
mlc -X
当使用
-X
参数时,每个core只有一个超线程(hyperthread)用于所有的带宽测试。否则这个核的所有线程都会被用于带宽测试。
详细参数摘选
-a
测试所有可用CPU的idle延迟-b
设置每个CPU的分配缓存大小(KB),默认是200MB延迟测试,100MB带宽测试-c
将延迟测试的线程pin到指定CPU。所有内存访问都将从这个特定的CPU发出-d
设置特定的负载注入延迟
使用案例
采集所有延迟和带宽数据
默认使用不需要任何参数
没有root权限情况下使用
-e
参数
不修改任何预取器MSR设置。
然而,在启动测试前关闭所有硬件预取器是非常必要的。否则,默认情况下,MLC会报告非常低的延迟,因为只有顺序步幅被用于延迟测试。
为了能够在没有修改预取器设置情况下尽可能精确测量,以下命令通过增加-r
和–l128
参数来强制随机存取以beat可能激活的预取器。
不过,我测试
mlc --bandwidth_matrix –e
和mlc --bandwidth_matrix –e –l128 –r
结果似乎没啥差别。
参考
Last updated