PERF_TYPE_HARDWARE 和 PERF_TYPE_HW_CACHE 并发监控

PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE concurrent monitoring

我正在 perf_event_open 系统调用之上进行自定义实现。

该实现旨在为任何内核.[=35]上的特定线程支持各种PERF_TYPE_HARDWAREPERF_TYPE_SOFTWAREPERF_TYPE_HW_CACHE事件=]

Intel® 64 和 IA-32 架构软件开发人员手册第 3B 卷中,我看到以下内容用于我的测试 CPU (Kaby Lake):

据我所知,可以(理论上)同时监视无限 PERF_TYPE_SOFTWARE 个事件,但同时监视有限(没有多路复用)PERF_TYPE_HARDWAREPERF_TYPE_HW_CACHE 个事件,因为每个事件都是通过CPU 的 PMU 的计数器数量有限(如上面的手册所示)。

因此,对于启用了超线程的四核 Kaby Lake CPU,我假设最多可以同时监视 4 个 PERF_TYPE_HARDWARE/PERF_TYPE_HW_CACHE 事件(或者最多 8 个,如果仅使用了 4 个线程)。

对上述假设进行试验后,我发现虽然我可以成功监控最多 4 个 PERF_TYPE_HARDWARE 事件(对于 8 个线程),但 PERF_TYPE_HW_CACHE 事件并非如此,最多只能监控 2 个事件可以并发监控!

我也试过只使用 4 个线程,但并发监控的上限 'PERF_TYPE_HARDWARE' 事件仍然是 4。同样的事情发生在 HyperThreading disabled!

有人会问:为什么要避免多路复用。首先,实施需要尽可能准确,避免多路复用的潜在盲点,其次,当超过 "upper limit" 时,所有事件值都是 0...

我定位的 PERF_TYPE_HW_CACHE 事件是:

CACHE_LLC_READ(PERF_HW_CACHE_TYPE_ID.PERF_COUNT_HW_CACHE_LL.value  | PERF_HW_CACHE_OP_ID.PERF_COUNT_HW_CACHE_OP_READ.value << 8 | PERF_HW_CACHE_OP_RESULT_ID.PERF_COUNT_HW_CACHE_RESULT_ACCESS.value << 16),
CACHE_LLC_WRITE(PERF_HW_CACHE_TYPE_ID.PERF_COUNT_HW_CACHE_LL.value  | PERF_HW_CACHE_OP_ID.PERF_COUNT_HW_CACHE_OP_WRITE.value << 8 | PERF_HW_CACHE_OP_RESULT_ID.PERF_COUNT_HW_CACHE_RESULT_ACCESS.value << 16),
CACHE_LLC_READ_MISS(PERF_HW_CACHE_TYPE_ID.PERF_COUNT_HW_CACHE_LL.value  | PERF_HW_CACHE_OP_ID.PERF_COUNT_HW_CACHE_OP_READ.value << 8 | PERF_HW_CACHE_OP_RESULT_ID.PERF_COUNT_HW_CACHE_RESULT_MISS.value << 16),
CACHE_LLC_WRITE_MISS(PERF_HW_CACHE_TYPE_ID.PERF_COUNT_HW_CACHE_LL.value  | PERF_HW_CACHE_OP_ID.PERF_COUNT_HW_CACHE_OP_WRITE.value << 8 | PERF_HW_CACHE_OP_RESULT_ID.PERF_COUNT_HW_CACHE_RESULT_MISS.value << 16),

全部使用提供的公式实现:

(perf_hw_cache_id) | (perf_hw_cache_op_id << 8) |
(perf_hw_cache_op_result_id << 16)

并作为一个群体被操纵(第一个是组长等)。

所以,我的问题如下:

  1. PMU 的哪些计数器用于 PERF_TYPE_HARDWARE,哪些用于 PERF_TYPE_HW_CACHE 事件,我在哪里可以找到此信息?
  2. PERF_TYPE_HARDWARE预定义事件(例如PERF_COUNT_HW_CACHE_MISSES)和PERF_TYPE_HW_CACHE事件有什么区别?
  3. 关于如何在不多路复用所有列出的 PERF_TYPE_HW_CACHE 事件的情况下进行监视的任何建议?
  4. 关于如何在不多路复用多达 8 个 PERF_TYPE_HARDWARE or/and PERF_TYPE_HW_CACHE 事件的情况下进行监控的任何建议?

提前致谢!

  1. PERF_TYPE_HARDWAREPERF_TYPE_HW_CACHE事件映射到性能监控涉及的两组寄存器。第一组 MSR 称为 IA32_PERFEVTSELx,其中 x 可以从 0 到 N-1 变化,N 是可用通用计数器的总数。 PERFEVTSEL 是 "performance event select" 的缩写,它们指定了满足事件计数的各种条件。第二组 MSR 称为 IA32_PMCx,其中 x 的变化与 PERFEVTSEL 相似。这些 PMC 寄存器存储性能监控事件的计数。每个 PERFEVTSEL 寄存器都与相应的 PMC 寄存器配对。

映射发生如下-

在内核的体系结构特定部分初始化时,注册了一个用于测量硬件特定事件的 pmu here with type PERF_TYPE_RAW. All PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE events are mapped to PERF_TYPE_RAW events to identify the pmu, as can be seen here

if (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)
        type = PERF_TYPE_RAW;

相同架构特定的初始化负责设置上述每组性能监控事件寄存器的first/base寄存器的地址,here

    .eventsel       = MSR_ARCH_PERFMON_EVENTSEL0,
    .perfctr        = MSR_ARCH_PERFMON_PERFCTR0,

识别出的PMU特有的event_init函数,负责设置和"reserving"两组性能监控寄存器,以及检查事件约束等,here. The reservation happens here.

for (i = 0; i < x86_pmu.num_counters; i++) {
        if (!reserve_perfctr_nmi(x86_pmu_event_addr(i)))
            goto perfctr_fail;
    }

    for (i = 0; i < x86_pmu.num_counters; i++) {
        if (!reserve_evntsel_nmi(x86_pmu_config_addr(i)))
            goto eventsel_fail;
    }

num_counters = CPUID 指令标识的通用计数器的数量。

除此之外,还有一些额外的 registers 可以监控非核心事件(例如 LLC 缓存特定事件)。

在更高版本的架构性能监控中,一些硬件事件是在固定用途寄存器的帮助下测量的,如here. These are the fixed-purpose registers-

#define MSR_ARCH_PERFMON_FIXED_CTR0 0x309
#define MSR_ARCH_PERFMON_FIXED_CTR1 0x30a
#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b
  1. 预定义的PERF_TYPE_HARDWAREevents都是架构性能监控事件。这些事件是架构性的,因为每个架构性能事件的行为预计在支持该事件的所有处理器上都是一致的。所有 PERF_TYPE_HW_CACHE 事件都是 非架构的 ,这意味着它们是特定于模型的,并且可能因处理器系列而异。

  2. 对于我拥有的 Intel Kaby Lake 机器,总共预定义了 20 个 PERF_TYPE_HW_CACHE 事件。事件约束 involved,确保可用的 3 个固定功能计数器映射到 3 个 PERF_TYPE_HARDWARE 架构事件。每个固定功能计数器只能测量一个事件,因此我们可以丢弃它们进行分析。另一个限制是只能同时测量两个针对 LLC 缓存的事件,因为只有两个 OFFCORE RESPONSE 寄存器。此外,nmi-watchdog 可以将事件固定到通用计数器系列中的另一个计数器。如果禁用 nmi-watchdog,我们将剩下 4 个通用计数器。

考虑到所涉及的限制以及可用计数器的数量有限,如果同时测量所有 20 个硬件缓存事件,则无法避免多路复用。在不引起多路复用及其错误的情况下测量所有事件的一些解决方法是 -

3.1。将所有 PERF_TYPE_HW_CACHE 事件分成 4 组,这样所有 4 个事件都可以同时安排在 4 个通用计数器中的每一个上。确保一个组中的 LLC 缓存事件不超过 2 个。 运行 相同的配置文件并分别获取每个组的计数。

3.2。如果要同时监视所有 PERF_TYPE_HW_CACHE 事件,则可以通过减小 perf_event_mux_interval_ms 的值来减少多路复用的一些错误。它可以通过名为 /sys/devices/cpu/perf_event_mux_interval_ms 的 sysfs 条目进行配置。这个值不能降低超过一个点,可以看出here.

  1. 监视多达 8 个硬件或硬件缓存事件需要禁用超线程。请注意,有关可用通用计数器数量的信息是使用 CPUID 指令检索的,并且此类计数器的数量是通过 early_initcall 函数在内核启动的体系结构初始化部分设置的。这可以看出here。一旦初始化完成,内核就会知道只有 4 个计数器可用,之后超线程功能的任何更改都不会产生任何影响。