You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用rdpmc高效获取LLC引用与缺失计数的问题

使用rdpmc高效获取LLC引用与缺失计数的问题

嘿,我太懂你这个问题了——在用Ubuntu+GCC的C环境里,想用rdpmc实现零运行时系统调用开销,统计最后一级缓存(LLC)的引用和缺失次数,结果卡在了没法同时拿到两个事件的有效索引,只能读取其中一个事件的数据,这确实挺闹心的。我之前折腾性能计数器的时候踩过同款坑,给你捋捋问题出在哪以及怎么解决:

问题根源:没给两个事件分配独立的硬件计数器

你现在只能拿到一个事件的数据,大概率是因为你要么尝试用同一个硬件计数器来统计两个事件,要么初始化时没正确给LLC引用、缺失分别绑定不同的通用性能监控计数器(PMC),导致rdpmc只能读取到其中一个事件的计数。

正确的初始化流程(允许用系统调用)

初始化阶段你完全可以用perf_event_open这个系统调用,给两个事件分别申请硬件计数器,关键是要拿到每个事件对应的rdpmc索引:

  • 首先要对应好LLC事件的编码:不同CPU架构(Intel/AMD)的编码不一样,比如Intel的LLC引用是事件代码0x2E,umask 0x4F,LLC缺失是事件代码0x2E,umask 0x41;AMD的LLC引用是事件代码0x40,umask 0x03,缺失是事件代码0x41,umask 0x03,初始化时要先检测CPU架构再配置。
  • 用perf_event_open分别为两个事件创建文件描述符,注意要指定PERF_TYPE_RAW(用原始事件编码时)或者PERF_TYPE_HARDWARE(如果系统定义了PERF_COUNT_HW_CACHE_LL相关宏的话),并且要确保分配到不同的硬件计数器。
  • 把每个事件的perf_event_mmap_page映射到用户态,通过这个映射页的index字段拿到该事件对应的rdpmc索引——这个索引是动态分配的,别硬编码,不然换个CPU或者系统配置就失效了。

运行时读取的正确姿势

拿到两个事件的rdpmc索引后,运行时直接用内嵌汇编调用rdpmc就行,完全不需要系统调用,示例代码大概是这样:

#include <stdint.h>
#include <perf_event.h>
#include <sys/mman.h>
#include <prctl.h>

// 封装rdpmc读取函数
static inline uint64_t rdpmc_read(unsigned int idx) {
    uint32_t low, high;
    // rdpmc指令:c寄存器传索引,a和d寄存器返回低32位和高32位
    asm volatile("rdpmc" : "=a"(low), "=d"(high) : "c"(idx));
    return ((uint64_t)high << 32) | low;
}

int main() {
    // 初始化阶段:省略perf_event_open创建LLC引用、缺失事件的fd过程
    // 假设已经拿到llc_ref_fd和llc_miss_fd
    struct perf_event_mmap_page *ref_page = mmap(NULL, 4096, PROT_READ, MAP_SHARED, llc_ref_fd, 0);
    struct perf_event_mmap_page *miss_page = mmap(NULL, 4096, PROT_READ, MAP_SHARED, llc_miss_fd, 0);

    // 启用性能计数器(初始化时做一次就行)
    prctl(PR_TASK_PERF_EVENTS_ENABLE, 0);

    // 运行时核心逻辑:无系统调用开销读取两个事件计数
    uint64_t start_ref = rdpmc_read(ref_page->index);
    uint64_t start_miss = rdpmc_read(miss_page->index);

    // 这里放你要统计的业务代码
    // ...

    uint64_t end_ref = rdpmc_read(ref_page->index);
    uint64_t end_miss = rdpmc_read(miss_page->index);

    uint64_t llc_ref_count = end_ref - start_ref;
    uint64_t llc_miss_count = end_miss - start_miss;

    // 清理阶段:关闭计数器、释放映射(允许用系统调用)
    prctl(PR_TASK_PERF_EVENTS_DISABLE, 0);
    munmap(ref_page, 4096);
    munmap(miss_page, 4096);

    return 0;
}

要避开的几个坑

  • 别硬编码rdpmc索引:不同CPU、不同系统配置会分配不同的计数器,必须从perf_event_mmap_page里拿动态索引。
  • 确保用户态能调用rdpmc:初始化时要通过prctl或者perf_event_open的参数开启用户态访问权限,不然会触发非法指令错误。
  • 先验证事件编码正确性:可以先用perf stat -e LLC-loads,LLC-load-misses ./your_program命令对比统计结果,确保你的事件编码和系统默认统计的是同一个事件。

总之,核心就是初始化阶段给LLC引用、缺失两个事件分别分配独立的硬件计数器,拿到各自的rdpmc索引,运行时直接读取就行,完全不用碰系统调用,这样就能高效拿到两个事件的计数了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 12:48:13