直接读取CPU PMC失败:指令退休计数器0x000000c0返回0,求排查
直接读取CPU性能监控计数器(PMC)结果为0的问题
我尝试不使用Perf等工具直接读取CPU性能监控计数器(PMC),但统计已退休指令数的计数器0x000000c0输出结果始终为0,请问测试或代码中是否存在错误?
测试代码
#include <stdint.h> #include <stdio.h> #include "pmc.h" #include <fstream> #include <iostream> using namespace std; counter programmables[] = { { 0x000000c0ull, "INSTRUCTION_RETIRED"}, }; int core = 0; static void pin_cpu(size_t core_ID) { cpu_set_t set; CPU_ZERO(&set); CPU_SET(core_ID, &set); if (sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0) { printf("Unable to Set Affinity\n"); exit(EXIT_FAILURE); } } int main(){ pin_cpu(core); int result = 0; int n = 10000; double cnt = 0; srand(10769); size_t n_programmables = sizeof(programmables) / sizeof(programmables[0]); setup_pmc(core, programmables, n_programmables); zero_pmc(n_programmables); start_pmc(); double a = 0; for(int i = 1;i <= n;i++){ a = (double)(rand() + 300 + i)/(rand() - i); cnt += a; } stop_pmc(); result = get_stats_single(); printf("%lf %d\n",cnt,result); }
可能的错误点
- 权限不足:读取PMC需要内核级权限,普通用户进程默认无法配置性能监控寄存器。必须以root权限运行程序,或者调整内核参数
perf_event_paranoid为0或-1(执行echo 0 > /proc/sys/kernel/perf_event_paranoid)。 - 计数器配置不完整:x86架构下,PMC需要完整的配置参数。仅指定事件选择码0xc0不够,还需要设置对应的UMASK(比如Intel架构下INSTRUCTION_RETIRED.ANY的UMASK为0x00),同时要在控制寄存器(IA32_PERFEVTSELx)中开启用户态计数位(USR位,位16),确保计数器统计用户态执行的指令。
pmc.h核心函数实现缺陷:setup_pmc是否正确初始化了目标核心的性能监控控制寄存器,是否绑定到当前CPU核心?zero_pmc是否真正重置了计数器的64位值?get_stats_single是否读取了完整的64位PMC寄存器?如果仅读取32位部分,可能因计数器未溢出而得到0。
- 指令执行顺序问题:启动计数器、测试代码、停止计数器的操作可能被CPU指令重排序干扰,导致计数器未正确统计测试代码的指令。需要在
start_pmc后、测试循环前,以及stop_pmc前、读取计数器前添加内存屏障(如__asm__ volatile("mfence" ::: "memory");),确保指令执行顺序符合预期。
验证步骤
- 以root权限运行程序,同时调整
perf_event_paranoid参数为宽松值。 - 检查
pmc.h中的setup_pmc函数,确认是否正确设置了IA32_PERFEVTSELx寄存器的USR位,以及事件选择码和UMASK的组合是否匹配当前CPU微架构(可参考Intel/AMD官方手册)。 - 确认
get_stats_single读取的是64位的IA32_PMCx寄存器(使用rdpmc指令读取完整64位值)。 - 添加内存屏障指令,确保计数器的启停与测试代码的执行顺序正确。
内容的提问来源于stack exchange,提问作者moep0
相关产品推荐
相关产品推荐

