- The
PERF_TYPE_HARDWARE
and PERF_TYPE_HW_CACHE
事件被映射到涉及性能监控的两组寄存器。第一组 MSR 称为IA32_PERFEVTSELx
其中 x 的范围为 0 到 N-1,N 是可用通用计数器的总数。这PERFEVTSEL
是“性能事件选择”的缩写,它们指定了发生事件计数的各种条件。第二组 MSR 称为IA32_PMCx
,其中 x 的变化类似于PERFEVTSEL
。这些 PMC 寄存器存储性能监控事件的计数。每个PERFEVTSEL
寄存器与相应的配对PMC
登记。
映射发生如下 -
在内核的体系结构特定部分初始化时,会注册一个用于测量硬件特定事件的 pmuhere https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/core.c#L1908与类型PERF_TYPE_RAW
. All PERF_TYPE_HARDWARE
and PERF_TYPE_HW_CACHE
事件被映射到PERF_TYPE_RAW
事件来识别pmu,可以看出here https://elixir.bootlin.com/linux/v5.6.15/source/kernel/events/core.c#L10511.
if (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)
type = PERF_TYPE_RAW;
相同的架构特定初始化负责设置上述每组性能监控事件寄存器的第一/基址寄存器的地址,here https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/intel/core.c#L3950
.eventsel = MSR_ARCH_PERFMON_EVENTSEL0,
.perfctr = MSR_ARCH_PERFMON_PERFCTR0,
The event_init
特定于PMU识别的功能,负责设置和“保留”两组性能监控寄存器,以及检查事件约束等,here https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/core.c#L2350。预订发生here https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/core.c#L148.
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
操作说明。
除此之外,还有一些额外的寄存器 https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/intel/core.c#L232监视核心外事件(例如 LLC 缓存特定事件)。
在架构性能监控的更高版本中,一些硬件事件是借助固定用途寄存器来测量的,如下所示here https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/intel/core.c#L193。这些都是固定目的的寄存器 https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/include/asm/perf_event.h#L158 -
#define MSR_ARCH_PERFMON_FIXED_CTR0 0x309
#define MSR_ARCH_PERFMON_FIXED_CTR1 0x30a
#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b
The PERF_TYPE_HARDWARE
预定义的events https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/intel/core.c#L30都是建筑学性能监控事件。这些事件是架构性的,因为每个架构性能事件的行为预计在支持该事件的所有处理器上保持一致。全部PERF_TYPE_HW_CACHE
事件是非建筑,这意味着它们是特定于型号的,并且可能因处理器系列而异。
对于我拥有的 Intel Kaby Lake 机器,总共 20PERF_TYPE_HW_CACHE
事件是预先定义的。事件约束involved https://elixir.bootlin.com/linux/v5.6.15/source/arch/x86/events/intel/core.c#L192,确保 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
。它可以通过名为的 sysfs 条目进行配置/sys/devices/cpu/perf_event_mux_interval_ms
。该值不能降低超过一个点,可以看出here https://elixir.bootlin.com/linux/v5.6.15/source/kernel/events/core.c#L1112.
- 监视最多 8 个硬件或硬件缓存事件需要禁用超线程。请注意,有关可用通用计数器数量的信息是使用以下命令检索的:
CPUID
指令和此类计数器的数量在内核启动的体系结构初始化部分通过early_initcall
功能。这个可以看到here https://elixir.bootlin.com/linux/v5.6.15/source/kernel/events/core.c#L1112。初始化完成后,内核知道只有 4 个计数器可用,以后超线程功能的任何更改都不会产生任何影响。