简介:lmbench-3.0 是一款由 Larry McVoy 编写的多平台开源基准测试工具,面向系统管理员、内核与驱动开发者以及硬件性能验证工程师,用于评估系统综合性能,尤其在内存带宽与内存延时测试方面表现突出。资源包共 225 个文件,以 63 个 C 源码文件为核心,配合 10 个头文件、5 个 Makefile 及 configure 等构建脚本,另有大量 man 手册、tbl 表格、图表脚本与结果统计工具,压缩包约 508KB,结构紧凑、模块清晰。目前已有 3666 人学习下载。借助该工具,读者可完成内存拷贝、填充等带宽测试与读写访问延时测量,并通过可配置的数据块大小、迭代次数及多线程模式适配不同硬件场景;同时可参考其源码与文档理解基准测试的实现思路,为系统调优、算法对比和硬件选型提供可复现的性能数据支撑。
1. lmbench-3.0:为什么它仍是系统性能排查里绕不开的那把尺子
如果你在排查一台服务器的“卡顿”问题,top看到 CPU 不高、内存也够,但业务就是慢,这时候你大概率需要一把能拆到微秒级的尺子。lmbench-3.0就是这把尺子——它是一套经典的微基准测试工具集,专门测量操作系统和硬件的底层性能指标:内存延迟、上下文切换开销、进程创建成本、管道与网络通信带宽、文件系统读写延迟等。它不测“你的业务能跑多快”,它测的是“你的系统底座有多快”。
这套工具适合两类人:一类是做系统调优、内核参数调整、容器性能对比的工程师,需要量化“改之前和改之后差多少”;另一类是选型阶段要对比不同硬件、不同内核版本、不同虚拟化平台的人,需要一组可复现的底层数字。它的价值不在于跑分高低,而在于把“玄学卡顿”变成可对比的微秒数。我见过太多团队在容器密度上翻车,最后靠lmbench的上下文切换数据才定位到是调度器参数配错了。
2. 把 lmbench-3.0 跑起来:从编译到第一组数字
2.1 获取与编译:为什么我建议在目标机上直接编译
lmbench-3.0的源码结构很老派,但胜在依赖极少。常见做法是从官方渠道拿到源码包后,在目标机器上直接编译,而不是交叉编译后拷贝二进制。原因很简单:lmbench的很多测试项依赖编译时的系统头文件和库版本,交叉编译出来的二进制在目标机上跑,结果可能偏差很大,尤其是涉及gettimeofday和信号处理的测试。
# 解压源码包,进入目录 tar -xzf lmbench-3.0.tar.gz cd lmbench-3.0 # 先执行配置脚本,它会探测系统环境 # 这一步会问一些路径问题,直接回车用默认值即可 make config # 编译,-j 后面跟 CPU 核心数,加快编译速度 make -j$(nproc)编译完成后,当前目录下会生成bin/目录,里面是各个测试子程序。make config这一步会生成config文件,里面记录了编译器路径、编译选项和测试时用的临时目录。如果你后续要改测试参数,改这个文件比改 Makefile 更直接。
参数说明:make config里的CFLAGS默认是-O2,我一般会改成-O2 -fno-strict-aliasing,因为老代码里有些指针别名操作,开严格别名优化可能导致结果异常。另外TMPDIR建议指向内存文件系统(如/dev/shm),避免磁盘 I/O 干扰内存延迟测试。
2.2 跑第一组核心测试:内存延迟与上下文切换
lmbench的测试项很多,但排查系统卡顿最常用的就两组:lat_mem_rd测内存读延迟,lat_ctx测上下文切换延迟。先跑这两个,基本能判断系统底座的健康度。
# 进入 bin 目录 cd bin # 测内存读延迟:步长 128 字节,测试范围 512MB # 输出会按不同工作集大小给出延迟(纳秒) ./lat_mem_rd -t 512 128 # 测上下文切换:进程数从 2 到 16,每个规模测 1000 次 # 输出是每次切换的平均微秒数 ./lat_ctx -s 0 2 4 8 16逻辑说明:lat_mem_rd的第一个参数是测试的总内存范围(MB),第二个是步长(字节)。它会按 2 的幂次递增工作集大小,从 L1 缓存一直测到主存。你会看到延迟从几纳秒跳到几十纳秒,那个跳变点就是缓存边界。lat_ctx的-s 0表示进程间不共享内存,后面的数字是进程数列表。输出里每个进程数对应一行,单位是微秒。
参数怎么改:如果机器内存很大,lat_mem_rd的测试范围可以设到 4096 甚至更大,但跑一次要几分钟。lat_ctx的进程数一般测到 32 就够了,再大对业务参考意义不大。注意lat_ctx需要 root 权限来设置进程优先级,否则结果波动会很大。
2.3 批量测试与结果汇总:用脚本一次跑完
手动一条条跑太慢,我一般写个简单脚本把常用测试串起来,输出到带时间戳的文件里,方便前后对比。
#!/bin/bash # run_lmbench.sh - 批量跑核心测试并保存结果 OUTDIR="lmbench_results_$(date +%Y%m%d_%H%M%S)" mkdir -p "$OUTDIR" cd bin || exit 1 # 内存延迟 ./lat_mem_rd -t 1024 128 > "../$OUTDIR/mem_rd.txt" 2>&1 # 上下文切换 ./lat_ctx -s 0 2 4 8 16 32 > "../$OUTDIR/ctx.txt" 2>&1 # 进程创建开销 ./lat_proc fork > "../$OUTDIR/proc_fork.txt" 2>&1 # 管道通信延迟 ./lat_pipe > "../$OUTDIR/pipe.txt" 2>&1 echo "结果已保存到 $OUTDIR"逻辑说明:这个脚本把最常用的四项测试串起来,每项输出独立文件。lat_proc fork测的是fork()系统调用的开销,lat_pipe测的是通过管道传递一个字节的往返延迟。这两个数据对容器和微服务场景特别有用——容器创建慢、服务间通信延迟高,往往就是这两个指标出了问题。
参数说明:脚本里的-t 1024表示测到 1GB 工作集,适合内存 8GB 以上的机器。如果机器内存小,改成 512 或 256。lat_ctx的进程数列表我加到了 32,因为现在很多服务动辄几十个线程/进程,测到 32 能看出调度器在压力下的表现。
3. 读懂 lmbench-3.0 的输出:哪些数字值得盯,哪些是噪声
3.1 内存延迟曲线:缓存边界比绝对值更重要
lat_mem_rd的输出是一张两列表格:左边是工作集大小(MB),右边是平均延迟(纳秒)。很多人只看最小的那个数,觉得“延迟越低越好”,其实更有价值的是曲线形状。
正常机器的曲线应该是:工作集小于 L2 缓存时,延迟在 1-3 纳秒;跨过 L2 进入 L3,跳到 10-20 纳秒;超过 L3 进入主存,跳到 60-100 纳秒。如果你看到主存延迟超过 150 纳秒,或者缓存边界模糊不清,说明内存子系统有问题——可能是 NUMA 配置不对,或者内存条插错了通道。
我一般会把前后两次测试的曲线画在一起对比。如果换了内核参数后,主存延迟从 80 纳秒涨到 120 纳秒,那这个参数调整就是负优化,不管它宣称能提升多少吞吐。
3.2 上下文切换开销:2 微秒和 10 微秒是两个世界
lat_ctx的输出直接告诉你每次进程切换要花多少微秒。这个数字在 2 微秒以内,说明调度器很健康;2 到 5 微秒算正常范围;超过 10 微秒,你的系统在高并发下一定会出现明显的延迟抖动。
注意看不同进程数下的变化趋势。如果从 2 个进程到 8 个进程,切换开销翻了一倍,说明调度器在竞争条件下效率下降很快。这时候要检查是不是开了什么调试选项,或者 CPU 频率调节器设成了powersave。我踩过这个坑:一台机器上下文切换开销异常高,查了半天发现是 BIOS 里 C-State 没关,CPU 频繁进出低功耗状态导致切换变慢。
3.3 进程创建与管道延迟:容器场景的隐形瓶颈
lat_proc fork的输出是fork()加exit()的往返时间,单位微秒。裸机上这个值通常在 100-200 微秒,容器里可能到 500 微秒以上。如果你在跑一个需要频繁创建子进程的服务(比如某些 Web 服务器模型),这个数字直接决定了你的 QPS 上限。
lat_pipe测的是管道往返延迟,通常是个位数微秒。如果这个值超过 20 微秒,说明进程间通信有问题,可能是内核配置了过于激进的安全模块,或者pipe缓冲区大小被改过。这两个指标在容器密度高的机器上特别值得关注——它们能解释为什么“明明 CPU 没跑满,但服务就是吞吐上不去”。
4. 避坑指南:lmbench-3.0 跑出离谱数字的五个常见原因
4.1 现象:内存延迟测出来是 0 或者负数
原因:lat_mem_rd依赖高精度时钟,如果系统时钟源不对(比如用了jiffies而不是tsc),或者虚拟机里时钟被劫持,就会出这种结果。另外,如果编译时-O2把测试循环优化掉了,也会测出接近 0 的值。
解决:先检查/sys/devices/system/clocksource/clocksource0/current_clocksource,确保是tsc或hpet。然后在CFLAGS里加-fno-inline防止测试函数被内联优化。虚拟机里跑的话,结果只做相对对比,别当绝对值用。
4.2 现象:上下文切换开销波动极大,同一台机器跑三次差好几倍
原因:最常见的是 CPU 频率调节器在动态调频。测试过程中 CPU 从低频跳到高频,切换开销自然不稳定。其次是系统里有其他负载在抢 CPU,lat_ctx对干扰非常敏感。
解决:测试前把调节器设成performance,命令是cpupower frequency-set -g performance。然后确认没有其他重负载进程,最好在单用户模式下跑。如果是在容器里,给容器足够的 CPU 配额,别用--cpus=0.5这种限制。
4.3 现象:编译报错,提示找不到某个头文件或函数
原因:lmbench-3.0的代码比较老,在新版内核或新发行版上编译时,某些系统调用号或结构体定义变了。常见的是syscall相关的头文件路径变化。
解决:在make config时选择“使用系统自带的 syscall 定义”,不要用源码包里自带的。如果还报错,手动在CFLAGS里加上-D_GNU_SOURCE。实在不行就装一个老版本的开发环境,但我不建议在生产机上折腾,找个测试机编译好再拷过去。
4.4 现象:测试跑得特别慢,一个lat_mem_rd要十几分钟
原因:测试范围设得太大,或者步长设得太小。lat_mem_rd的测试时间大致和“范围/步长”成正比。设成-t 4096 64的话,跑半小时都正常。
解决:排查问题不需要那么细的粒度。先用-t 512 128快速跑一遍,定位到大概范围后再针对性地测。另外lat_ctx的测试次数也可以调小,默认是 1000 次,改成 100 次能快很多,但结果波动会大一点。
4.5 现象:容器里跑出来的结果和宿主机差很多,不知道信哪个
原因:容器里跑lmbench受 cgroup 限制、CPU 亲和性、网络命名空间等多重影响。内存延迟可能因为 NUMA 绑定而偏高,上下文切换可能因为调度器隔离而偏低。
解决:容器场景下,lmbench的结果只用于容器之间的横向对比,不要和宿主机绝对值比。测试时给容器--privileged或者至少--cap-add=SYS_NICE,否则lat_ctx没法设优先级。另外记得把容器的 CPU 配额设成和实际业务一致,别用默认的无限制。
5. 用 lmbench-3.0 做 A/B 对比:一个可复现的调优验证流程
5.1 建立基线:先跑三遍,取中位数
调优最怕的就是拿一次结果当真理。我的习惯是:任何改动之前,先在目标机上跑三遍完整测试,每遍之间重启一次系统,取三次结果的中位数作为基线。这样能过滤掉大部分随机噪声。
# 跑三遍基线,每次重启后执行 for i in 1 2 3; do echo "=== 第 $i 轮基线测试 ===" ./run_lmbench.sh echo "第 $i 轮完成,请重启系统后继续" read -p "重启完成后按回车继续..." done逻辑说明:这个循环强制每轮之间重启,是为了清空各种缓存和临时状态。read那行是等人手动重启,别偷懒跳过。三遍结果出来后,用sort和awk取中位数,比平均值更抗异常值。
5.2 改动与复测:一次只改一个变量
拿到基线后,每次只改一个内核参数或系统配置,然后复测。比如你要调vm.swappiness,那就只改这个值,其他不动。复测流程和基线一样,跑三遍取中位数。
对比的时候重点看三个指标的变化率:内存延迟、上下文切换开销、进程创建开销。如果某个指标恶化超过 10%,这个改动就值得怀疑。我一般会做一个简单的对比表:
| 指标 | 基线中位数 | 改动后中位数 | 变化率 | 判断 |
|---|---|---|---|---|
| 主存延迟 | 82 ns | 79 ns | -3.7% | 轻微改善 |
| 上下文切换(8进程) | 3.2 us | 4.8 us | +50% | 明显恶化 |
| fork 开销 | 145 us | 142 us | -2.1% | 基本不变 |
这张表一出来,结论就很清楚了:虽然内存延迟略有改善,但上下文切换开销大幅恶化,这个改动不能上生产。
5.3 一个具体技巧:用taskset绑定 CPU 减少噪声
如果测试机器上没法完全排除其他负载,可以用taskset把lmbench绑到固定的 CPU 核心上,减少调度带来的噪声。
# 把 lat_ctx 绑到 2 号 CPU 上跑 taskset -c 2 ./lat_ctx -s 0 2 4 8 16 # 把 lat_mem_rd 绑到 2 号 CPU,并且用 numactl 绑到 0 号 NUMA 节点 numactl --cpunodebind=0 --membind=0 taskset -c 2 ./lat_mem_rd -t 512 128逻辑说明:taskset -c 2让进程只在 2 号 CPU 上运行,避免迁移。numactl那行同时绑定了 CPU 和内存节点,确保内存访问不跨 NUMA,这样测出来的延迟才是节点内延迟。如果你的机器是单 NUMA 节点,numactl那行可以省略。
参数说明:CPU 编号用lscpu查,选一个不在中断处理列表里的核心。NUMA 节点用numactl --hardware看。绑核之后结果会稳定很多,但要注意别绑到 0 号 CPU,因为很多系统中断默认走 0 号。
这套流程我用了很多年,从物理机到虚拟机到容器都跑过。最深的教训是:lmbench的数字本身没有绝对意义,它的价值在于“同一套流程下的相对变化”。别拿网上的跑分来对比自己的机器,环境差一点,数字就能差一倍。每次要调系统参数之前,先花二十分钟跑个基线,比事后拍脑袋后悔强得多。希望帮到你。
本文还有配套的精品资源,点击获取