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

多路复用场景下Perf计数器结果异常问题排查

问题背景与疑问

我使用Intel(R) Core(TM) i7-4720HQ CPU @ 2.60GHz(Haswell架构)处理器,运行Linux 4.15.0-20-generic内核。在系统相对空闲状态下,执行Perf命令监测offcore_response.all_data_rd.l3_miss.local_dram、offcore_response.all_code_rd.l3_miss.local_dram和mem_load_uops_retired.l3_miss三个计数器时,因PMU(性能监控单元)共享出现事件多路复用问题。

给第三个事件追加:D修饰符(强制独占计数器,避免多路复用)后,所有计数器数值大幅增大,且该现象仅在多路复用发生时出现。我还分别在空循环程序、内存密集型程序场景下进行了带:D和不带:D的多轮测试,发现不同场景下:D对各计数器的影响存在差异。

现提出以下疑问:

  1. 这种输出是否正常?
  2. 括号内的百分比数值是否有效?
  3. 如何避免计数器数值差异?

测试详情

1. 空闲系统测试

不带:D的命令及输出:

sudo perf stat -a -e offcore_response.all_data_rd.l3_miss.local_dram,offcore_response.all_code_rd.l3_miss.local_dram,mem_load_uops_retired.l3_miss
^C
 Performance counter stats for 'system wide':

           229,579      offcore_response.all_data_rd.l3_miss.local_dram                                     (99.72%)
           489,151      offcore_response.all_code_rd.l3_miss.local_dram                                     (99.77%)
           110,543      mem_load_uops_retired.l3_miss                                     (99.79%)

       2.868899111 seconds time elapsed

带:D的命令及输出:

sudo perf stat -a -e offcore_response.all_data_rd.l3_miss.local_dram,offcore_response.all_code_rd.l3_miss.local_dram,mem_load_uops_retired.l3_miss:D
^C
 Performance counter stats for 'system wide':

           539,397      offcore_response.all_data_rd.l3_miss.local_dram                                     (68.71%)
           890,344      offcore_response.all_code_rd.l3_miss.local_dram                                     (68.67%)
           193,555      mem_load_uops_retired.l3_miss:D                                   

       2.853095575 seconds time elapsed

2. 空循环程序测试

测试程序:

#include <iostream>

using namespace std;

int main()
{
        for (unsigned long i = 0; i < 3 * 1e9; i++)
                ;

        return 0;
}

测试命令:

执行7次以下两组命令:

sudo perf stat -e offcore_response.all_data_rd.l3_miss.local_dram,offcore_response.all_code_rd.l3_miss.local_dram,mem_load_uops_retired.l3_miss ./loop

和

sudo perf stat -e offcore_response.all_data_rd.l3_miss.local_dram,offcore_response.all_code_rd.l3_miss.local_dram,mem_load_uops_retired.l3_miss:D ./loop

测试结果:

all_data_rd受:D影响极小,all_code数值降低,load_uops_retired数值增大。

3. 内存密集型程序测试

测试程序:

#include <iostream>
#include <cstring>

#define DIM_SIZE 10000

using namespace std;

char from [DIM_SIZE][DIM_SIZE], to [DIM_SIZE][DIM_SIZE];

int main()
{
        for (char x = 'a'; x <= 'z'; x++)
        {
                for (int i = 0; i < DIM_SIZE; i++)
                        memset(from[i], x, DIM_SIZE);

                for (int i = 0; i < DIM_SIZE; i++)
                        memcpy(to[i], from[i], DIM_SIZE);
        }

        return 0;
} 

带:D的输出:

sudo perf stat -e offcore_response.all_data_rd.l3_miss.local_dram,offcore_response.all_code_rd.l3_miss.local_dram,mem_load_uops_retired.l3_miss:D ./loop

 Performance counter stats for './loop':

        19,836,745      offcore_response.all_data_rd.l3_miss.local_dram                                     (50.04%)
            47,309      offcore_response.all_code_rd.l3_miss.local_dram                                     (49.96%)
         6,556,957      mem_load_uops_retired.l3_miss:D                                   

       0.592795335 seconds time elapsed

不带:D的输出:

sudo perf stat -e offcore_response.all_data_rd.l3_miss.local_dram,offcore_response.all_code_rd.l3_miss.local_dram,mem_load_uops_retired.l3_miss ./loop

 Performance counter stats for './loop':

        18,742,540      offcore_response.all_data_rd.l3_miss.local_dram                                     (66.64%)
            76,854      offcore_response.all_code_rd.l3_miss.local_dram                                     (66.64%)
         6,967,919      mem_load_uops_retired.l3_miss                                     (66.72%)

       0.575828303 seconds time elapsed

测试结果:

此场景下带:D和不带:D的计数器数值几乎一致。


问题解答

1. 输出是否正常?

这种现象完全正常,核心原因是PMU多路复用机制和:D修饰符的作用逻辑:

  • 不带:D时,三个事件共享Haswell核的4个通用PMU计数器,perf会通过时间分片轮询统计不同事件,低频事件的触发次数容易被遗漏,导致统计值偏低;
  • 加上:D后,mem_load_uops_retired.l3_miss会独占一个计数器,剩下两个事件只能共享剩余计数器,时间分片比例改变:原本三个事件分时间片,现在两个事件分剩余窗口,每个事件的统计占比提升,统计值更接近真实触发次数,所以数值增大;
  • 不同场景的差异源于事件触发频率:
    • 空闲系统事件触发频率低,多路复用的遗漏效应更显著,加:D后统计值提升幅度大;
    • 内存密集型场景中,all_data_rd触发频率极高,perf会优先分配更多时间片给它,因此带不带:D的统计值差异极小;
    • 空循环场景中,all_code(指令读取的L3 miss)触发频率低,:D独占计数器后,all_code的统计窗口占比下降,数值降低,而load_uops_retired.l3_miss因独占计数器,统计更完整,数值增大。

2. 括号内的百分比数值是否有效?

这些百分比完全有效,它代表该事件在统计周期内被PMU计数器实际监测的时间占比:

  • 不带:D时,三个事件共享计数器,空闲系统事件少,轮询切换开销可忽略,因此每个事件的监测占比接近100%;
  • 带:D时,mem_load_uops_retired.l3_miss:D独占计数器,全程被监测(无百分比),剩下两个事件共享其他计数器,因此它们的监测占比降到约68%;
  • 这个百分比可以用来估算真实触发次数:真实值≈统计值 / 百分比,用来修正多路复用带来的统计偏差。

3. 如何避免计数器数值差异?

可以通过以下几种方式解决:

  • 匹配事件数与硬件计数器数量:Haswell每个核有4个通用PMU计数器,若监测事件数≤4,不会发生多路复用,带不带:D结果一致;
  • 合理使用:D修饰符:对核心关键事件用:D独占计数器,确保统计完整,但需注意这会加重其他事件的多路复用,需根据需求权衡;
  • 拆分多次统计:把事件分成多组,每组事件数不超过CPU的PMU计数器数量,分别执行perf stat后合并结果,保证每个事件都能被完整统计;
  • 使用-A选项:多核系统中用-A(--per-core)让每个核独立统计事件,减少跨核多路复用的影响;
  • 升级内核或perf版本:较新的内核(5.x及以上)对PMU多路复用的调度更优化,能有效降低统计误差。

内容的提问来源于stack exchange,提问作者TheAhmad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:40:34