尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
cachebench 实战:MB/s 曲线定位 L1/L2/L3 和内存带宽
简介LLCbench 缓存性能测试工具包面向 Linux 平台下从事处理器缓存层次分析、体系结构实验与平台性能对比的开发者、教师和学生核心集成 MPBench、CacheBench 与 BLASBench其中 CacheBench 侧重考察不同缓存层级、工作集大小和访存模式下的性能变化可用于课程实验、硬件选型、编译优化验证及论文实验数据采集等场景测试模块可单独编译选用。压缩包共 81 个文件、约 83KB以 C 源码与头文件承载测试主体配合多套 Makefile 及 sys 平台配置模板适配不同 Linux/Unix 环境另有 shell 脚本、ps 与 gp 绘图脚本、HTML 说明和 README 文档目录按 cachebench、mpbench、blasbench、doc 等模块划分源码、配置与说明文档相互配套体量轻便而结构完整。已有 873 人学习下载适合希望获得可编译的缓存基准代码、复现缓存性能实验并借助脚本生成结果图表的读者参考通过结果图表可直观比较不同平台和编译选项的缓存表现帮助定位访存瓶颈也能为性能测试方案设计提供可复用的配置与排错线索。1. 新机器跑分和老机器持平我为什么又翻出 cachebench 这包老代码上个月给一台新装的机器做处理器性能测试单核浮点分数涨了快一半可一跑真实业务里的查表逻辑吞吐几乎没动。这种“算力涨了、访存没跟上”的场面见得太多了最后能一句话说清问题的还是 cachebench 这种老派 linux 开源工具——喂它一个从 1KB 到几百 MB 的数组它把每一档的读写带宽用 MB/s 打出来拐点在哪、L3 有多大、内存实际能喂多快一眼就能看出来。llcbench.tar.gz 就是这套东西的源码包里头不止 cachebench还带着单机浮点算力和多进程消息传递两套测试。适合谁要给出机器性能结论的人、要做选型对比的人、以及怀疑自己程序被内存卡住却拿不出证据的人。2. 解包到跑通把 llcbench.tar.gz 编成三个可执行文件的完整路径2.1 先看骨架再动手别上来就 make拿到 tar 包第一件事不是编译是看结构。这类九十年代风格延续下来的 C 工程不同人打包时目录层级会变有可能顶层就是一个llcbench/也有可能解压出来直接是一堆并列的子目录编译入口可能是顶层Makefile加一堆Makefile.arch模板也可能是一个make.def让你改。先花三十秒看清结构比后面make报错十次再去猜要快得多。包里三个主要工具分工很明确我一般按“先 cachebench、需要时再补另外两个”的顺序推进子目录名称以实际解包为准测什么什么时候必须跑cachebench单一地址流下的读、写、拷贝、memset 带宽按数组尺寸逐档输出每次都要跑是判断缓存层级的核心blasbench单机 BLAS 级别的浮点算力单线程/多线程各一份需要把“算力”和“访存”拆开归因时mpbench多进程之间消息传递的延迟与带宽多路机器怀疑跨节点扩展性时才需要Makefile / make.def 之类各架构编译模板复制一份改成自己的别直接改原文件# 不解压也能看结构先确认顶层目录名叫什么 tar tzf llcbench.tar.gz | head -40 # 解压到独立目录避免污染工作区 mkdir -p ~/bench tar xzf llcbench.tar.gz -C ~/bench cd ~/bench ls -la # 找编译入口顶层 Makefile、各架构模板、以及可能的 make.def find . -maxdepth 2 \( -iname Makefile* -o -iname make.def -o -iname *.mk \) | sort这段的目的只有一个确定“编译入口文件名”和“产物会落在哪个目录”。tar tzf只列名字不落盘几百 KB 的包秒出结果find的-maxdepth 2是刻意的这类老工程子目录里常有一堆测试脚本也叫 Makefile搜太深反而干扰判断。如果find出来的模板里有Makefile.linux基本就是它了只有make.def的话通常是先改make.def里的CC、CFLAGS再回顶层make。2.2 编译选项定死为什么我把 -O3 换成了 -O2压测程序最怕的事情是编译器比你还聪明。cachebench 的读测试本质上就是“把数组从头到尾读一遍把结果攒起来”如果编译器判定这个循环的最终结果没人用它会直接删掉——你测出来的“带宽”其实是空循环的速度。这类工程内部一般会用一些手段防止被优化掉把结果写进全局变量、提供保留结果的开关但你的-O3会额外打开向量化和更激进的循环变换让读循环一次搬 32 字节甚至 64 字节测出来的数就变成了“SIMD 宽度测试”跨机器一比全是噪声。所以我的固定做法是统一-O2不用-marchnative把gcc --version和uname -a一起写进报告。-marchnative在同一台机器上自测很爽但只要涉及两台以上机器对比编译选项不一致数据就没有可比性——这是血泪经验不是洁癖。另外注意 32 位与 64 位构建指针宽度变了数组内部地址计算的指令序列也会变同一个 size 下的带宽能差出百分之十几。# 环境快照先落盘后面写报告直接引用省得回头补 { uname -a gcc --version | head -1 lscpu | grep -Ei model name|mhz|cache|numa free -g } ~/bench/env.txt # 编译优先用顶层入口失败再退回模板 make clean /dev/null 21 make # 若提示需要指定架构改成 make linux 或 make -f Makefile.linux # 产物一般在各子目录下按可执行权限筛一遍 find . -maxdepth 2 -type f -perm -ux -name *bench* -print这段里make clean先跑一遍很关键老工程的 Makefile 常常不写头文件依赖改过配置后不清理会链接到上一次的.o症状是“明明改了选项结果一点没变”。find那条是按可执行权限加名字双重过滤比ls */靠谱因为部分版本会把产物放在bin/或上一级。编译失败最常见的两类原因一是模板里的编译器名写死成了某个厂商的cc改CCgcc即可二是 64 位环境下老代码里的指针强转报 warning 被-Werror升级成 error去掉-Werror就好不用去修代码。2.3 先跑一次 smoke test把用法抄下来不同打包版本的参数集合会有出入所以第一步永远是“不带参数跑一次”让它自己把 usage 打出来。把 usage 存进文件后面写脚本时对着抄比记 memo 靠谱。# 不带参数多数版本会打印用法后退出把 usage 留档 cd ~/bench/llcbench # 目录名以实际解压结果为准 ./cachebench 21 | head -30 | tee ~/bench/usage.txt # 最小一次可跑只做读测试最大数组给 64MB taskset -c 2 ./cachebench -r -s 67108864 | tee ~/bench/read_smoke.txt-r是读测试-s给的是“扫到多大为止”的上限而不是单点尺寸——这一点最容易误解很多人以为-s是“测一个 64MB 的数组”结果看到输出里一长串不同尺寸的行就懵了。taskset -c 2把进程钉在 2 号核上是为了排除调度器在核间搬进程带来的抖动具体绑哪个核要看lscpu -e尽量避开 0 号核很多系统中断默认落在 0 号。跑完先别急着看图head -20 read_smoke.txt人工确认两件事第一列到底是不是字节数、最后一列到底是不是 MB/s。这个确认动作只花十秒但决定了后面解析脚本写得对不对。3. cachebench 的四种访存模式读、写、拷贝、memset 的扫参与落盘3.1 四种模式分别对应什么真实负载读、写、拷贝、memset 这四种模式不是凑数的它们对应四类完全不同的访存路径。读测试走的是 load 通路最接近查表、遍历、哈希探测这类逻辑写测试走 store 通路现代 CPU 上写往往是“写分配”会先把 cache line 读进来再改所以写带宽经常比读低一截看到这个不要以为机器有问题拷贝测的是memcpy类路径同时吃读写两条通路还要看有没有非临时存储指令参与memset 则是纯写、且可以整行覆盖硬件能做写合并通常跑出来是四个里最“好看”的一个。如果你只跑一个-r很容易得出“这台机器内存带宽 20GB/s”的结论但这个数字换到 memcpy 密集的场景完全不成立。我的习惯是四个模式一起跑落成四份原始文本后面既画单条曲线也画四条叠在一起的对比图。mkdir -p ~/bench/raw # 固定绑核 固定最大尺寸四个模式各跑一遍-x 是每个尺寸的重复次数 for mode in r w b f; do taskset -c 2 ./cachebench -$mode -s 268435456 -x 3 \ ~/bench/raw/${mode}.txt 21 done # 顺手把每份输出的前几行打出来确认格式没变 for f in ~/bench/raw/*.txt; do echo $f; head -5 $f; done-x 3是每个尺寸重复三次取其中的最好值或中位数不同版本策略不同看输出里是不是出现多列。重复的意义在于压制瞬时噪声后台的一次日志刷盘、一次中断就能让某一个尺寸点掉 15%单次采样根本分不清是噪声还是真实的层级跌落。-s 268435456给到 256MB是为了确保一定能跨过 L3 落到内存——现在不少服务器 L3 就有 32MB 到 64MB上限给小了曲线只到 L3 就结束了看不到内存那一段。注意-s的单位在不同版本里可能按字节、也可能按元素个数解释。跑完先看输出的尺寸列最大值是不是接近你给的数字对不上就按元素个数理解乘以元素大小换算回来。3.2 想要更细的采样点就改进尺寸递增步长这类工具内部通常是按 2 的幂往上翻1KB、2KB、4KB……一路翻到上限。粗粒度扫的时候够用但拐点会被夹在两个点中间你只能得出“L2 在 256KB 到 512KB 之间”这种模糊结论。更麻烦的是纯 2 的幂会引入组相联索引冲突这个在 4.2 节单独说。想细化就去源码里找那条递增语句把翻倍改成 1.25 倍。# 定位尺寸递增的位置常见写法是 size 1 或 size * 2 grep -n -E | ?1|\* ?2|size ?\* cachebench/*.c | head -20改之前把原文件备份一份改完重新make。步长改成 1.25 倍之后采样点数会从十几个涨到六十几个单次运行时间大概是原来的三四倍但拐点位置能精确到一个窄区间值得。改完记得在报告里注明“步长经过修改”否则别人拿原版跑出来的点数和你对不上会以为你数据有问题。3.3 把文本表格变成 CSV解析脚本与列校验原始输出是给人看的要做对比就得转成表。不同版本的列数和列序有差异所以解析脚本不能写死列号我的做法是“取行内第一个数字当尺寸、最后一个数字当带宽”再加一道合理性校验异常行直接丢掉并在日志里计数。import re import csv import pathlib # 版本间列序有差异用宽松策略首个数字尺寸(字节)末个数字带宽(MB/s) NUM re.compile(r[-]?\d(?:\.\d)?) rows, dropped [], 0 for p in sorted(pathlib.Path(raw).glob(*.txt)): for line in p.read_text(errorsignore).splitlines(): s line.strip() if not s or s.startswith(#): # 跳过注释与空行 continue nums NUM.findall(s) if len(nums) 2: dropped 1 continue size, bw float(nums[0]), float(nums[-1]) # 合理性校验尺寸递增、带宽为正且不超过 1e7 MB/s if size 0 or bw 0 or bw 1e7: dropped 1 continue rows.append({mode: p.stem, size_bytes: int(size), mb_per_s: bw}) rows.sort(keylambda r: (r[mode], r[size_bytes])) with open(cache_curve.csv, w, newline) as f: w csv.DictWriter(f, fieldnames[mode, size_bytes, mb_per_s]) w.writeheader() w.writerows(rows) print(f保留 {len(rows)} 行丢弃 {dropped} 行) for r in rows[:5]: # 人工核对前几行 print(r)这段的逻辑说明errorsignore是因为个别版本会在输出里混入非 UTF-8 的控制字符不忽略会直接抛异常nums[-1]取最后一个数字而不是固定列是为了兼容“带宽前面还有一列耗时”的版本bw 1e7这个上限看着随意实际非常有用——解析错位时经常把行号或时间戳当成带宽读进来会出现上千万的荒谬值用一条上限就能整行拦掉。最后打印前五行是强制的人工校验环节绝不能省。提示把dropped打进日志。如果丢弃比例超过两成说明输出格式和你预期不符先回去看原始文件别硬跑。4. 处理器性能测试的读数MB/s 曲线怎么对上 L1、L2、L3 和内存4.1 拐点的判定方法以及各层的典型量级把size_bytes取对数做横轴、mb_per_s做纵轴画出来是一条阶梯最左边平、然后掉一档、再平、再掉最后到内存段变成一条缓坡。判拐点我不用肉眼用相邻点的衰减比bw[i]/bw[i-1] 0.85就记一次“掉层”记录下来那个尺寸区间再和lscpu报的 cache 容量对照。层级典型容量单线程读带宽量级曲线特征L132KB64KB数十 GB/s 到上百 GB/s全程最平最高的一段尾部开始抖L2256KB2MB几十 GB/s第一次明显跌落跌幅常在三成以上L38MB64MB部分型号更大十几到几十 GB/s第二次跌落坡度变缓但不消失内存按实际装机单线程常见几 GB/s 到十几 GB/s缓坡尺寸再翻倍带宽也基本不动要强调的是“单线程”这三个字。cachebench 是单线程的量出来的内存带宽只代表一个核能拉起来的部分跟厂商宣传的整机峰值带宽差好几倍是正常的。拿这个数去和整机峰值比然后得出结论“内存性能不达标”是新手最容易犯的错。4.2 2 的幂尺寸会造出假拐点这是我在这个工具上踩过最隐蔽的坑。数组尺寸全部取 2 的幂而且起始地址又是对齐的那么步长和 cache 的组相联索引之间就会形成固定关系地址低位截出来的组号总是落在很少的几个组上L1 或 L2 里明明还有空间却因为冲突缺失被迫反复回下层取数。表现在曲线上就是“本该在 L2 段的位置提前掉了一档”你会误判 L2 只有一半大。识别方法很简单把起始地址人为错开。要么改步长3.2 节的 1.25 倍递增要么在数组前面多申请几十个字节做偏移。做完这两件事其中一个如果那一档突然回升就确认是冲突而不是容量。我现在的固定做法是步长用 1.25 倍并加偏移两条都上省得事后怀疑。注意不要用-O3加手动改源码的方式“优化”这个问题动了访存循环测出来的就不是同一件事了。4.3 和 perf、blasbench 交叉验证确认你测的是缓存单一工具的数字再漂亮也只是自证。我的验证办法是用perf stat挂在同一次运行上看 LLC 相关的计数有没有随尺寸同步变化。# 在带宽明显跌落的尺寸上挂一次 perf看末级缓存缺失是否同步跳起 perf stat -e cycles,instructions,cache-references,cache-misses,LLC-loads,LLC-load-misses \ taskset -c 2 ./cachebench -r -s 33554432 # 算力侧对照blasbench 拉一遍单线程浮点确认算力不是瓶颈 cd ../blasbench taskset -c 2 ./blasbench 21 | tail -20判读逻辑如果尺寸刚从 L3 跨到内存LLC-load-misses占LLC-loads的比例应该同步升高同时 cycles 里等待访存的比例上升。反过来如果带宽掉了但 LLC 缺失率没动说明你测的可能不是缓存层级而是被别的因素频率降了、页没驻留、进程被抢干扰了。blasbench 那一侧的作用是排除法算力分数正常而 cachebench 曲线异常问题就落在访存路径上方向立刻收窄。5. 避坑与排查五个让缓存测试数据白测的坑5.1 读带宽高得离谱而且整条曲线是平的现象-r跑出来每一档都在同一个极高的数值上从 1KB 到 256MB 几乎不掉看着像“这台机器没有缓存层级”。 原因读循环被编译器优化掉了或者结果被判定为无用而整段删除测到的是空循环速度。老代码里防优化手段有限遇上新一代编译器就顶不住。 解决先降到-O2重编如果依然平看工具是否提供“保留结果”的开关usage 里常有一个类似的选项并打开再不行就手动在循环末尾把累加结果赋给一个全局变量重编后再跑。验证方式是整条曲线的绝对值应该落在合理量级而不是高出十倍。5.2 同一份二进制两次运行差出百分之二十现象什么都没改连跑两遍内存段的带宽一次 12GB/s 一次 9GB/s。 原因多次运行之间变量太多——CPU 频率还在爬升、页表还没热、进程被调度到别的核、邻居进程在抢 LLC。第一次运行尤其吃亏大量缺页发生在测试过程中。 解决先空跑一轮作为预热并丢弃结果用taskset把进程钉死在一个核上把-x重复次数提高取重复里的稳定值跑之前确认机器上没有别的重负载。做完这四件事两次运行的差异一般能压到 5% 以内。5.3 拐点位置和 lscpu 报的 cache 容量对不上现象lscpu显示 L2 是 1MB曲线却在 256KB 就掉了一档。 原因多半是 4.2 节的索引冲突少数情况是透明大页把相邻页合并改变了实际的地址映射或者数组没有按大页对齐。 解决先把步长改成 1.25 倍、给数组加偏移重测仍然对不上就临时把透明大页设为madvise并确认进程没有主动使用大页最后检查是不是把“每核 L2”看成了“整颗 L2”多核机器的lscpu输出容易看串行。5.4 大尺寸段的带宽低到不像内存像磁盘现象数组开到 512MB 以上带宽掉到几百 MB/s且抖动极大。 原因申请的数组超过了可用物理内存或者第一次触碰触发大量缺页测试过程里混进了换页和页清零。 解决把-s的上限压到物理内存的一半以内free -g先看一眼可用量测试前确认vmstat 1里si/so一直是 0如果机器上还跑着别的服务换一台干净的机器测这种测量对内存压力非常敏感。5.5 多进程那套工具起不来或者只跑了一个进程现象想顺手验证多核扩展性结果进程数始终是 1或者直接报连接错误。 原因多进程部分需要一个启动器来拉起多个实例单独执行可执行文件只会跑单进程另外 MPI 环境没配对时进程间握手会直接失败。 解决先看该子目录里有没有配套的启动脚本按脚本里的参数格式传进程数确认mpirun之类的前置环境可用且版本一致实在跑不通就先放弃它cachebench 的单线程结论已经足够支撑大部分判断不必为了“跑全”卡在这里。6. 进阶把一次性的缓存测试做成可回归的基线单次测量只能回答“现在是多少”真正省事的是把它变成一条能对比的基线。我的做法是把第 3 章的采集脚本和第 3.3 节的解析脚本串起来一次跑完输出一个带环境指纹的 CSV文件名里带日期和 CPU 型号内容第一行写uname -r、gcc版本、-s上限、是否改过步长。以后任何人问“这台机器缓存是不是变慢了”拿两份 CSV 对齐size_bytes做差值就行不用重新组织测试。对比的时候必须设阈值否则你会被噪声牵着走。我的经验阈值是同一尺寸两次测量差异在 5% 以内视为一致5% 到 15% 之间需要复测一次再判断超过 15% 才认为发生了变化。这个阈值不是拍脑袋是按 5.2 节把预热、绑核、重复次数都做到位之后实测残留的波动范围定的。# 一键基线采集 解析 打指纹产物可直接进版本库 STAMP$(date %Y%m%d)-$(lscpu | awk -F: /Model name/{gsub(/^ /,,$2); print $2} | tr -d ) bash sweep.sh python3 parse.py mv cache_curve.csv baseline-${STAMP}.csv sha256sum baseline-${STAMP}.csv baseline.sha256这套东西跑顺之后还有个额外收益换编译选项、换内核、换固件这类变更只要跑一遍基线就能看出访存路径有没有受影响比等业务侧反馈“最近好像慢了”要早得多。绑核那一步别偷懒我吃过亏——有次忘了taskset曲线整体平移了 8%排查了半天以为是驱动问题最后发现是进程被搬到了另一个 NUMA 节点的核上。从那以后我每次跑基线都强制走一遍taskset绑核加一轮预热丢弃脚本里写成默认行为不给自己留手动省略的机会。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

ThinkPHP教学管理系统实战:排课冲突检测与成绩批量录入

ThinkPHP教学管理系统实战:排课冲突检测与成绩批量录入

简介:这是一套基于ThinkPHP框架搭建的简单教学管理系统源码,面向PHP初学者、Web开发入门者及需要课程设计参考的学生,帮助理解MVC架构下的教务管理实现。压缩包共2000个文件,约11.04MB,以1105个php文件为核心业务代码&…

📅 2026/10/23 15:16:26
Linux分区工具parted完全指南:GPT分区、在线扩容与自动化脚本实践

Linux分区工具parted完全指南:GPT分区、在线扩容与自动化脚本实践

做服务器运维这些年,我几乎每天都要跟磁盘分区打交道。今天想认真聊一聊parted分区工具,这是GNU出品的一个命令行分区工具,也是我在处理GPT分区表、超大容量磁盘和自动化初始化任务时的首选。很多人一上来就习惯用fdisk,但碰到超过…

📅 2026/10/23 15:16:25
Windows Server 2012 R2 安装 .NET 3.5 遇阻?SXS 源文件与 DISM 命令实战指南

Windows Server 2012 R2 安装 .NET 3.5 遇阻?SXS 源文件与 DISM 命令实战指南

简介:这份资源是面向 Windows Server 2012 R2 系统管理员与运维人员的 .NET Framework 3.5 离线安装 SXS 文件包,主要解决服务器默认未集成该框架、在无外网或带宽受限环境下无法通过在线方式启用 NetFX3 功能的问题,适合需要部署旧版企业应用…

📅 2026/10/23 15:16:25
MORE NEWS

更多资讯

📰

WinForm 上传共享文件夹:SMB 凭据、流式复制与 1219 排错

简介:这份资料是一套基于VS2010与C# Winform的文件上传示例,面向需要把本地文件写入局域网共享目录的开发者,也可用于企业内部数据传输、共享盘上传等场景。压缩包共23个文件,约36KB,以6个cs源码为核心,配有…

📰

信号分析与处理实验全解析:从时域到频域再到系统

简介:这份资源是南京邮电大学「信号分析与处理实验」课程的完整实验报告,面向正在修读数字信号处理、信号与系统相关课程的高校学生,以及需要借助 MATLAB 完成实验与课程设计的自学者。报告覆盖信号的产生和运算、连续时间信号的频域分析、时…

📰

Polymarket-bot DipArb 暴跌套利揭秘:3秒15%信号检测与双腿对冲的完整实现原理

【免费下载链接】Polymarket-bot 4 Strategies to trade on Polymarket in one bot 项目地址: https://gitcode.com/gh_mirrors/po/Polymarket-bot 点击查看 免费下载 Polymarket-bot 内置的 DipArb(暴跌套利) 策略,是预测市场上…

📰

从 Wan2.2 到 MLX:AnimeGen-I2V-A14B-Lightning-bf16 的 bf16 转换流程与键名映射实战复盘

【免费下载链接】AnimeGen-I2V-A14B-Lightning-bf16 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/AnimeGen-I2V-A14B-Lightning-bf16 点击查看 免费下载 AnimeGen-I2V-A14B-Lightning-bf16 是一套面向 Apple Silicon(MLX) 的…

📰

Git 第八章:fetch 只更新 origin/*,pull 还要再合并一次

上一章讲的都是从本地到远程的方向,git push 用本地的分支去更新 GitHub 上的分支。但是一个仓库往往不止一个人在推送。如果合作者在这期间往 GitHub 上推送了新的提交,本地仓库并不会自动知道,这时就需要反方向的操作,把远程的新…

📰

Python八股文速查PDF:核心概念高频问答与面试考点解析

简介:这份《Python 八股文.pdf》面向准备技术面试的 Python 开发者与在校学生,系统梳理语言核心概念与高频考点,帮助读者在短时间内建立完整的知识框架。内容围绕内置电池、生成器与迭代器、列表与元组、字典实现、GIL、Python 2 与 3 差异、…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬