买云服务器时,厂商给的配置单上只写了CPU核数、内存大小和带宽,却从来不会告诉你内存延迟是多少。而内存延迟恰恰是影响数据库查询、Redis缓存命中率、高并发响应速度的关键指标之一。两台同样是8核32G的云主机,如果一台使用了计算优化型实例,另一台是共享型实例,它们的主内存访问延迟可能相差一倍以上。要准确拿到这个数据,LMbench是目前最常用也是最可信的工具之一,它的lat_mem_rd子命令专门用来测量内存读取延迟,还能顺带把各级缓存的延迟曲线测出来。

LMbench是什么,它能测出哪些数据
LMbench是由Larry McVoy开发的一套Unix系统微基准测试工具集,诞生至今已有二十多年历史,但它依然是Linux内核开发者和云厂商内部评测的常备工具。它的核心思路是用极小的、精心设计的负载去测量系统各个子系统的裸性能,包括内存带宽、内存延迟、上下文切换开销、进程创建速度、网络延迟等。因为它足够底层、干扰因素少,测出来的数据非常稳定,适合做横向对比。
在内存测量方面,LMbench提供了两个关键工具:lat_mem_rd负责测量内存读取延迟,bw_mem负责测量内存带宽。本文重点讲前者。lat_mem_rd的原理是在一块指定大小的内存区域上做指针追逐(pointer chasing):预先把这块区域按固定步长串成一个链表,每个缓存行里存放下一个缓存行的地址,然后CPU沿着这条链不断跳转读取。由于每一步读取都依赖上一步的结果,CPU无法提前预取,流水线完全被串行化,这样测出来的时间就非常接近真实的内存访问延迟。
测试时会从很小的数组开始逐步扩大,比如从512字节一直测到几百MB。当数组大小小于L1缓存时,测出的延迟就是L1的访问延迟;超过L1但仍在L2范围内时,延迟会跳升一个台阶;依次类推,最终你会得到一条阶梯状的曲线,每一级台阶对应一层缓存,最后一段平坦区域就是主内存(DRAM)的延迟。典型x86服务器上,L1大约在4到5纳秒,L2在14纳秒上下,L3在40纳秒左右,主内存则通常在80到120纳秒之间,具体数值取决于CPU型号、内存代际和NUMA拓扑。
在云服务器上安装和编译LMbench
主流云服务器大多提供CentOS、Ubuntu或Debian系统,LMbench在多数发行版的软件仓库里都有收录。以Ubuntu为例,直接执行apt-get install lmbench即可完成安装,CentOS用户可以先安装EPEL源再执行yum install lmbench。不过仓库版本往往较旧,如果想要最新代码,建议从官方源码编译,过程也不复杂。
编译前需要准备好make、gcc以及基本的开发库。以Ubuntu为例,先执行apt-get install -y build-essential安装编译环境,然后获取源码并编译:
# 安装编译依赖 apt-get update apt-get install -y build-essential git # 下载LMbench源码 git clone https://github.com/intel/lmbench.git cd lmbench # 编译(-j 加速编译过程) make -j$(nproc) # 编译完成后,可执行文件在 bin 目录下 ls bin/x86_64-linux-gnu/ # 输出中应包含 lat_mem_rd、bw_mem、lat_ctx 等工具
有一个常见的编译坑需要提醒:在较新的GCC版本下编译老版本LMbench可能报隐式函数声明错误,导致make中断。遇到这种情况,可以在执行make时追加CFLAGS="-Wno-implicit-function-declaration -fcommon"参数,或者直接使用发行版仓库里的预编译包,功能上没有区别。另外,云服务器如果是最小化安装的系统,可能连wget都没有,先确认基础工具齐全再开始。
执行lat_mem_rd测试并解读结果
假设我们要测试一台8核32G的云主机,命令很简单:./lat_mem_rd 512M 64。第一个参数是测试数组的最大尺寸,建议设为物理内存的四分之一左右,太大会触发swap影响结果,太小则测不到主内存的真实延迟。第二个参数64表示指针追逐的步长,单位是字节,64正好是一个标准缓存行的大小,这是最常用的取值,可以让相邻两次访问落在不同缓存行上,避免数据预取干扰。
完整执行过程如下:
# 关闭其他业务进程,绑定到固定CPU核心减少调度干扰 taskset -c 2 ./lat_mem_rd 512M 64
输出结果分两列,第一列是数组大小(单位MB),第二列是对应的访问延迟,单位是纳秒。摘取一段典型的输出:当数组为0.00049MB(即512字节,完全落在L1内)时延迟约0.4093ns;数组增大到0.0625MB时延迟仍是0.4093ns附近,说明L1缓存至少有64KB;数组超过0.25MB后延迟跳到1.6ns左右,这是L2的延迟;超过1MB后延迟再次跳到5ns量级,对应L3;当数组超过16MB、明显大于L3容量后,延迟稳定在21ns附近,这个值就是主内存的读取延迟。需要说明的是,这里的数据来自一台主频较高的实例,如果你测出的主存延迟明显偏高,比如超过100ns,先别急着下结论,很可能是实例规格限制了内存带宽或者开启了NUMA跨节点访问。
解读曲线时有几个实用技巧。第一,找台阶的拐点,拐点对应的数组大小就近似等于该级缓存的容量,比如在32MB附近出现最后一个台阶,可以推断L3是32MB。第二,观察最后一段是否平稳,如果曲线在尾部仍然缓慢上扬,说明测试受到了页表遍历或TLB miss的影响,可以尝试开启大页内存再测一次对比。第三,多次运行取平均值,每次至少跑三遍,取后两次的数据,第一次可能因为页错误分配较多而偏高。
测试注意事项与结果分析
云服务器和物理机不同,虚拟化层会带来额外的干扰。为了让数据可信,测试前要做几项准备:关闭不必要的后台服务,避免与其他租户抢资源的时间点(虽然这个无法完全控制),用taskset把测试进程绑定到固定核心,避免跨NUMA节点访问内存。可以用lscpu先查看NUMA拓扑,如果实例有多个NUMA节点,分别在节点内和跨节点两种情况下各测一遍,对比数据会很有意思——跨节点访问延迟通常会增加30%到80%。
另一个影响因素是CPU频率。云厂商的实例普遍支持睿频,轻负载下CPU跑在最高频率,测试延迟时数据会偏乐观;如果实例存在突发性能限制,长时间高负载后被降频,测出的延迟会变差。严谨的做法是先用cpupower frequency-info确认当前频率策略,测试过程中可以同时开一个终端跑cat /proc/cpuinfo | grep MHz观察频率变化,必要时固定频率再测试。
拿到数据后如何判断好坏?可以参考一个简单标准:现代主流平台L1延迟应在1ns以内,L2在2到5ns之间,L3在10到20ns之间,主内存延迟在60到100ns之间属于正常水平。如果你要对比多家云厂商,建议用同一套命令、同样的数组规模和步长,在相近的时间段各测多次,把主内存延迟这一项单独拉出来比较,这个数字对数据库类业务的性能预测参考价值最大。此外,还可以搭配bw_mem测一下内存带宽,延迟和带宽结合起来看,就能对一台云主机的内存子系统有完整的认知,为后续的实例选型和容量规划提供扎实的数据支撑。