多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

内存延迟与带宽实测:Intel MLC性能分析工具详解

内存延迟与带宽实测:Intel MLC性能分析工具详解 1. 项目概述为什么要盯着内存延迟和带宽搞服务器性能、做数据库调优、玩超频或者单纯想搞清楚自己买的内存条到底值不值迟早会遇到一个问题内存到底快不快很多人第一反应是跑个分看看总分数高不高。但真到了排查性能瓶颈的时候你会发现大而化之的分数几乎没用你得知道具体某个访问模式下的延迟是多少、不同通道之间的带宽差异有多大、NUMA节点之间的通信要付出多少代价。MLC全称Intel Memory Latency Checker就是干这个的工具。它由Intel开发命令行运行没有花哨界面但能针对内存子系统做非常精细的压力测试和延迟探测。我第一次用MLC是在一次数据库延迟问题排查中当时系统日志显示内存层面的等待异常偏高常规测试工具跑出来却一切正常后来用MLC做了延迟矩阵和带宽矩阵的详细测试才定位到是NUMA拓扑下跨节点访问导致的性能退化。从那以后MLC就成了我机器上常驻的测试工具之一。这篇文章面向的是需要评估服务器内存性能的运维和性能工程师、折腾硬件想验证内存频率和时序是否生效的玩家、以及写性能测试脚本时想找可靠测量手段的开发人员。文章会把MLC的核心功能、参数使用、结果解读和我在实际测试中踩过的坑都过一遍争取让你拿到手就能上手跑出有用的数据。2. MLC到底测什么一张图看懂它的核心能力2.1 和常见跑分工具的本质区别先理清一个容易混淆的概念。很多跑分软件测试的是“综合性能”背后是一堆混合负载的加权平均。MLC不一样它默认就把内存系统拆成几个维度分别测试随机访问延迟、顺序读带宽、顺序写带宽、以及不同访问模式下的读写混合带宽。这样做的好处非常明显——你可以直接看到内存子系统在哪个维度上存在短板。举个例子。之前我遇到过一台机器跑综合测试分数并不低但实际跑数据库查询时性能总上不去。用MLC测出来发现顺序读带宽远低于同型号内存应有水平而延迟表现正常。进一步排查发现是BIOS里的NUMA均衡策略配置问题导致内存控制器没有充分利用所有通道。这种问题你要靠总分类的软件去定位基本是不可能完成的任务。2.2 核心测量项拆解延迟、带宽、混合负载MLC的主要测试类型可以分成三大类所有参数和输出都是围绕它们展开的延迟测试。这是MLC最经典的能力核心是测量CPU在访问内存时经历的平均等待时间单位纳秒。它会通过构造特定大小的数据集合分别测试命中L1缓存、L2缓存、L3缓存以及真实内存时的延迟。关键点在于它可以通过参数控制是否只测某个缓存层级比如用--idle_latency测的是纯内存延迟用--latency_matrix测的是不同CPU和内存节点之间的互访延迟。带宽测试。这里说的带宽是指每秒能够从内存读或写多少GB数据。MLC会通过不同大小的数据块、不同访问方式来测出峰值带宽和实际可持续带宽。参数上对应--max_bandwidth和--bandwidth_matrix。前者测的是当前机器上能达到的最大带宽后者测的是每个CPU节点访问所有内存节点的带宽矩阵是定位NUMA问题的利器。混合负载测试。真实业务场景里很少是纯读或者纯写大多是读写混合的。MLC提供--mixed_read_percentage之类的参数可以指定读操作占多少比例然后在不同读写下压测带宽和延迟变化用来模拟数据库或缓存服务这类真实负载。2.3 MLC的底层测量逻辑为什么数据可信MLC之所以被业内当作参考级工具是因为它的测量逻辑很讲究。延迟测试通过构造一个固定大小的链表或指针追逐结构让CPU连续访问内存中不连续地址。由于每次访问都要等待上一次的结果返回才能计算下一次地址因此整个循环的耗时基本就取决于内存的随机访问延迟。用这种结构测出来的延迟能把CPU乱序执行和预取机制的干扰降到最低。带宽测试则相反它用连续大块数据填充配合不同指令集和预取策略尽量压满内存控制器的吞吐极限。默认情况下MLC会尝试用多种访问方式最后输出表现最好的结果。这背后涉及CPU微架构对prefetch指令的支持程度也是为什么MLC建议在支持不同代际的Intel CPU上使用时能发挥更大价值。3. 实操篇从编译到跑出第一份有效数据3.1 获取和编译Linux版实测流程MLC在Linux下通常以源码或预编译二进制形式分发。如果你拿到的是源码包编译过程很简单需要系统里有gcc和make。我在Ubuntu 20.04和CentOS 7.9上都编译过没遇到依赖缺失问题。# 解压后进入目录执行 make编译完成后会生成mlc可执行文件。然后给执行权限就能直接运行了。如果遇到权限错误检查一下是不是没有加可执行权限chmod x mlc sudo ./mlc注意MLC需要较高级别的权限来设置CPU亲和性、实时调度以及访问性能计数器。我的经验是直接sudo运行避免权限不够导致测试结果偏差甚至无法启动。3.2 延迟测试最常用的参数组合延迟测试实操中我用得最多的是这两个命令# 测量本机空闲状态下的内存延迟统计信息完整 sudo ./mlc --idle_latency # 生成跨CPU和内存节点的延迟矩阵定位NUMA性能差异 sudo ./mlc --latency_matrix--idle_latency的结果里会有一行关键数据比如3.000ns表示L1缓存命中延迟75.000ns表示内存访问延迟。很多第一次跑的人以为多个数值里取哪个都行实际上要看懂表格结构。以我的经验直接关注表格里对应内存控制器那一列也就是所有数据集合都远超缓存容量的那一行那才是有效内存延迟。--latency_matrix的输出是一个矩阵行代表发起访问的CPU节点列代表被访问的内存节点。举个例子如果输出对角线上的延迟是80ns而非对角线上的延迟是120ns这说明跨节点访问确实损失明显。这个矩阵在验证BIOS的NUMA设置时非常直观。3.3 带宽测试压满内存通道的实操指令带宽测试我常用--max_bandwidth来测峰值再用--bandwidth_matrix看不同节点的差异。# 测试最大可持续带宽默认使用多种读比例 sudo ./mlc --max_bandwidth # 生成带宽矩阵 sudo ./mlc --bandwidth_matrix--max_bandwidth输出中包含Read Bandwidth、Write Bandwidth和UTLB等列。在一台配置了双路CPU和16条DDR4内存的服务器上我实测读带宽大概在190GB/s左右。如果你发现测试结果远低于同配置机器水平优先排查内存通道数是否正确启用。现实中经常有人插了4条内存但BIOS设置不对实际只有2条通道在工作这种情况跑MLC一眼就能看出来——带宽数字直接砍半。对于更细粒度的测试MLC允许指定测试时长和并发线程数。比如在带宽测试中加-t参数可以设置单次测试时间默认以秒为单位# 每次测试跑5秒减少波动 sudo ./mlc --max_bandwidth -t 53.4 Windows环境下的使用差异MLC也支持Windows运行的是编译好的exe文件。需要注意的是Windows版运行时建议用管理员权限打开命令行否则访问性能计数器或设置线程优先级时会失败。Windows下无法像Linux那样通过taskset指定CPU亲和性可以考虑用start /affinity命令来限定进程在指定核心上运行方便做对照实验。4. 不会读结果等于白测关键指标解析4.1 延迟结果里的门道MLC的延迟测试输出通常包含多行每一行对应不同的内存访问足迹大小。让我们看一个典型的--idle_latency部分输出当数据集合大小为32KB时延迟数字反映的是L1缓存命中延迟。当数据集合为256KB时反映的是L2缓存命中延迟。当数据集合为几MB到几十MB时反映的是L3缓存命中延迟。当数据集合为几百MB甚至更大时基本就是在测真实内存延迟了。所以判断内存延迟时一定要看数据集合大于内存缓存总量的那几行。很多刚接触MLC的人拿着第一行3ns的数字到处说“我的内存延迟3ns”这显然是误解。切到L3缓存行业内的经验值是10到20ns不等而真实内存延迟通常在70到100ns范围具体取决于CPU代际和内存频率。补充一下MLC测试结果会因为CPU频率调整和节能机制波动。为了拿到稳定数值建议测试前把CPU调到固定频率或者在BIOS里暂时关闭C-States和Turbo Boost。性能分析讲究的是控制变量否则你测出来的不是内存性能而是电源管理策略的随机表现。4.2 带宽结果的内存通道判断法带宽输出中最重要的一列是单通道带宽和整体带宽的对比。假设你用的是DDR4-3200理论单通道带宽是25.6GB/s。如果系统配置了8条内存理论上满通道带宽应该是204.8GB/s但实际测试中由于协议开销和刷新开销能达到80%到90%就算健康。我的经验是如果实测带宽和理论值的比值低于70%就要开始排查内存条是否都插在正确通道上或者是否因为某些内存条频率不匹配导致降频。--bandwidth_matrix这个选项在双路服务器上特别有用。正常配置下每个CPU应该能全速访问本地内存跨CPU访问带宽会有所下降。如果你发现本地访问带宽和远端访问带宽差异极大甚至访问远端内存的带宽比本地还高这极可能是ACPI SLIT表或者NUMA距离配置出了问题。操作系统会根据这个表来决定内存分配策略配置错误会导致所有内存访问都跑到远端去。4.3 混合负载下的延迟-带宽曲线MLC还有个高级玩法就是控制读比例看延迟如何变化。通过--mixed_read_percentage和--max_bandwidth结合可以测出在多少带宽占用下延迟开始飙升。这个数据对容量规划和性能隔离非常关键。举个例子# 测50%读、50%写混合场景的最大带宽和相应延迟 sudo ./mlc --max_bandwidth --mixed_read_percentage50输出里会多出延迟数据列体现的是在高负载下的延迟表现。我习惯在数据库服务器调优时记录几组关键数据纯读延迟、纯写延迟、50%混合延迟、以及90%读延迟。如果发现50%混合场景下延迟猛增可能说明内存控制器在读写切换时处理效率不高这种机器就不适合承担高并发读写混合的负载。5. 实战场景MLC怎么帮我和解决真实问题5.1 判断内存条频率是否真的生效有次我帮朋友看一台AMD平台机器标称DDR4 3600结果系统里显示内存频率只有2666。他一度怀疑是CPU内存控制器问题。我用MLC测了--bandwidth_matrix发现读带宽只有42GB/s左右而3600双通道的预期带宽在57.6GB/s左右。通过带宽数据推算内存实际运行频率确实只有2666。最后查出来是XMP没有正确加载手动开启Expo之后重新测试带宽数值明显回升。这个场景说明MLC不仅能测延迟还能间接验证内存频率和双通道是否生效特别适合硬件排障。5.2 验证BIOS设置对NUMA性能的影响之前提到的那次数据库延迟排查其实当时的现象是应用偶尔出现几十毫秒的尖峰延迟数据库自身所有指标看起来正常。我用MLC的--latency_matrix一测发现跨NUMA节点延迟是本地延迟的两倍还多。再配合系统里numactl --hardware的输出确认部分内存被分配到远端节点导致某些查询线程访问内存时承担了额外开销。调整了内存分配策略后尖峰延迟明显缓解MLC的矩阵数据也恢复正常水平。这种问题如果不用矩阵模式测试很难量化。5.3 采购选型时评估内存体质如果你需要对比不同品牌或不同批次的内存条MLC也能派上用场。在保证CPU、主板完全一致的前提下使用同一版本的MLC分别测试--idle_latency和--max_bandwidth延迟低且带宽高的那批内存明显体质更好。这个方法比自己看芯片颗粒赌体质靠谱得多。说个细节测试内存体质时记得把测试集合大小设置到远大于所有缓存的总和。比如一台机器L3缓存有32MB那测试数据至少要跑到512MB以上才能确保所有样本都来自真实内存。MLC默认的集合大小已经考虑到了这点但如果机器缓存特别大建议用-M参数手动扩大测试集合范围。5.4 量化虚拟化环境的内存性能损耗做虚拟化或容器平台性能评估时MLC可以用来对比宿主机和虚拟机内的内存性能差异。在VM里跑MLC时要注意虚拟化层会引入额外的内存映射开销延迟数值通常会比宿主机高一些。如果高出太多就需要检查是不是开启了嵌套页表以外的低效内存虚拟化模式。我遇到过KVM环境里延迟高出60%的情况检查后确认是因为CPU的VT-x/AMD-V特性没有直通给虚拟机修正配置后延迟显著下降。6. 常见报错和排查技巧6.1 运行报错无法打开性能计数器在Linux下最常见的是这个提示Failed to open /dev/cpu/.../msr。这不是MLC本身坏了而是权限不够。解决办法是sudo运行或者给当前用户添加对应的设备节点访问权限。6.2 结果差异大为什么两次测试数值差很多最简单的答案是CPU频率波动。只要你没有锁定频率测试过程中频率会动态调整。解决方案是提前设置好电源模式Linux下可以用cpupower frequency-set -g performance将CPU调为性能模式。Windows下则选择“高性能”电源计划。6.3 测试时间过长默认测试可能需要十几分钟甚至更久特别是--max_bandwidth在多路服务器上。可以通过-t参数缩小单次测试时间或者直接测试子集。比如只测读带宽就加--read_percentage100。平时做快速验证我习惯用-t 2 -i 2组合把迭代次数控制在两次五分钟内拿到初步数据。6.4 矩阵测试卡在某一节点6.5 在虚拟化环境中执行失败如果在虚拟机里跑MLC提示缺少某些MSR寄存器支持不用慌。这是正常的因为很多硬件性能计数器不会直接暴露给虚拟机。此时测试出的数据参考价值有限我更建议物理机上跑。6.6 测试结果与其他工具不一致这是最多人问的问题。同一台机器MLC测出的延迟和AIDA64测出的延迟不一样哪个准答案是都准但测的维度不同。AIDA64偏向于在更接近真实负载的条件下测量MLC则试图构造最纯的访问模式。两者之间的差异恰好反映了不同访问模型下的性能表现实际分析时应以MLC的纯延迟为基准再结合其他工具观察综合表现。我做性能归档时会同时记录两个工具的数据后续排查多一个参考维度。7. 一点个人经验和流程建议MLC这种工具初看只是给你刷几个数字但用久了你会发现它其实是内存子系统的一个显微镜。测延迟、跑带宽都不难难的是知道什么时候该跑哪个测试、怎么排除干扰变量。我个人常用的流程是先跑--idle_latency拿到延迟基线再跑--bandwidth_matrix查通道完整性和NUMA状态最后按业务负载特征跑一组混合比例带宽测试。整个过程大约十几分钟但每一组数据都能对应到具体系统配置和潜在瓶颈。另外MLC的输出一定要结合当时的CPU频率、内存频率、通道数、NUMA拓扑这些信息一起存档。光记结果数字没有意义因为脱离配置谈性能就是耍流氓。建议每次测试后把lscpu、dmidecode -t memory的输出一并存档以后回溯问题会轻松很多。我自己踩过不少坑最深刻的一条就是不要依赖印象中的内存参数一切以测试工具的实际输出为准。内存标称是标称实际跑成什么样只有仪器不会骗你。
返回列表