Linux下无法直接读取缓存命中率,因其非现成硬件字段,须通过perf_event_open接口采集PMU原始事件计数后自行计算,该接口是内核提供的唯一标准、安全且可移植的用户态入口。

Linux 下无法直接用 C++ 读取“缓存命中率”这个抽象指标——它不是硬件寄存器里现成的字段,而是必须通过 PMU(Performance Monitoring Unit)采集原始事件计数,再自行计算得出。
为什么 perf_event_open 是唯一靠谱的入口
用户态程序不能直接读 MSR 或访问 PMU 寄存器;perf_event_open 是内核提供的标准接口,封装了权限检查、事件复用、溢出处理等细节。绕过它(比如用 rdmsr)需要 root + 特定 CPU 支持 + 手动处理 event encoding,极易出错且不可移植。
- 必须指定
PERF_TYPE_RAW或使用预定义类型(如PERF_TYPE_HARDWARE),避免依赖 perf 工具链 - 要查清 CPU 微架构对应的事件编码:Intel 用
0x412e(L2_RQSTS.ALL_CODE_RD)和0xf24(L3_LAT_CACHE.REFERENCE)这类 raw code;AMD 则完全不同 - 内核需启用
CONFIG_PERF_EVENTS=y,且/proc/sys/kernel/perf_event_paranoid≤ 2(推荐设为 1)
如何组合事件算出 L2/L3 命中率
命中率 = 1 − (未命中数 / 总请求次数),所以至少得同时采集两个事件。但注意:PMU 通道有限(通常 4–8 个),不能无脑开一堆;更不能只采一个再除以时间——那算的是带宽,不是命中率。
- L2 命中率常用组合:
PERF_COUNT_HW_CACHE_L2:READ:MISS(或 raw0x412e拆解) +PERF_COUNT_HW_CACHE_L2:READ:ACCESS - L3 命中率更复杂:Linux perf 不直接暴露 L3 MISS/ACCESS,得用 raw events,例如 Intel Skylake:
0x4f2e(L3_UNSHARED_HIT_E) 和0x412e(L3_MISS_LOCAL_DRAM)需配对,再结合0x4124(L3_TOTAL_REFERENCE)估算 - 必须用
perf_event_attr::disabled = 1+ioctl(fd, PERF_EVENT_IOC_RESET)+ioctl(fd, PERF_EVENT_IOC_ENABLE)控制采样窗口,否则拿到的是累计值
实际代码里最常崩的三个点
不是编译不过,而是运行时 silent fail 或数据全零——多数因为没看清文档细节。
-
attr.config设错:raw event 必须把 bit 32 置 1(即attr.config |= 1ULL ),否则内核当 hardware event 处理,返回 EINVAL - 没调
ioctl(fd, PERF_EVENT_IOC_REFRESH, 1)就 read():尤其多事件时,第一次 read 可能返回 -EAGAIN - 忘记
attr.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID:否则 read() 返回结构体长度不对,memcpy 直接越界
struct read_format {
uint64_t nr; // number of events
uint64_t time_enabled;
uint64_t time_running;
uint64_t values[nr]; // id, value pairs
};
真正在生产环境跑通,关键不在“怎么写”,而在“怎么验证”:先用 perf stat -e 'l2_rqsts:all_code_rd,l2_rqsts:code_rd_miss' sleep 1 对照输出,再比对你程序里 read() 出来的两个值是否量级一致——不一致就说明 event 编码或 attr 设置有偏差,别急着改算法。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











