Repository navigation
Replicate generic hardware events on all CPU PMUs - #2123
Conversation
|
cc @captain5050 |
|
Does this need any kind of guarding or is this always the right thing to do? |
This is always correct as far as I'm aware. I've tested that it does the right thing on an M2 Ultra Mac Studio, a Pixel 10 and a regular x86 PC (where it just finds the single PMU which returns the same results as not specifying a PMU). |
LebedevRI
left a comment
There was a problem hiding this comment.
Didn't test, but seems fine.
|
can we add google test unit tests for this please? i don't know how tricky it is to test it but i'm a little concerned having no tests at all. |
We can test it by verifying that counters are non-zero when the process is pinned to each CPU in the system. We can adapt the test |
Done |
|
looks like we need to update this from head and do some clang-format fun. |
On systems with more than one PMU for the CPUs (e.g. Apple M series SOCs), generic hardware events are only created for an arbitrary PMU. Usually this is the big cluster's PMU, which can cause inaccuracies when the process is scheduled onto a little core. To fix this, teach PerfCounters to register generic hardware events on all CPU PMUs. CPU PMUs are identified using the same method as perf.
Done |
|
thank you so much :) |
Instead of falling back to a plain hardware event inside the open loop when no extended hardware event type works (which needed the try_extended_hw_type flag, an index-based loop that appends to the PMU list, and restoring group_id), decide up front which CPU PMUs to replicate generic hardware events on: probe each CPU PMU once per process by opening (and closing) a CYCLES event with its extended type, and only replicate on the PMUs that accept it. If none do, open plain hardware events, as before "Replicate generic hardware events on all CPU PMUs" (google#2123). This leaves PerfCounters::Create() unchanged from before the fix, and also falls back to plain events if no CPU PMUs are found at all. The --v=1 logging of failed and successful perf_event_open calls is replaced by logging the probe results, e.g.: perf_counters: found CPU PMU armv8_pmuv3 type 8 perf_counters: PMU type 8 does not support extended hardware event types: Invalid argument AI disclosure: this change was written with the help of an AI coding tool.
Instead of falling back to a plain hardware event inside the open loop (which needed the try_extended_hw_type flag, an index-based loop that appends to the PMU list, and restoring group_id), move the code that opens a counter (with retries and the fallback to a new group) into a helper that returns 0 if the counter could be opened, or else the errno of the failed perf_event_open call. Use it to first try opening each generic hardware event on all CPU PMUs with extended types and, if none of these copies can be opened, to open it as a plain hardware event, as before "Replicate generic hardware events on all CPU PMUs" (google#2123). Each event is only opened once, and no state is carried from one counter to the next. Since the helper only updates group_id when a counter is opened, a counter that cannot be opened no longer forces the following counters into a new group. The --v=1 logging of every perf_event_open call is replaced by logging the copies that cannot be opened, e.g.: perf_counters: found CPU PMU armv8_pmuv3 type 8 perf_counters: cannot open CYCLES on PMU type 8: Invalid argument AI disclosure: this change was written with the help of an AI coding tool.
…ted (#2367) * Fall back to plain hardware perf events when extended types are rejected Since "Replicate generic hardware events on all CPU PMUs" (#2123), generic hardware events are opened on every CPU PMU with the PMU type in the upper bits of attr.config (PERF_PMU_TYPE_SHIFT). That needs the PMU driver to advertise PERF_PMU_CAP_EXTENDED_HW_TYPE, which e.g. arm_pmu in Linux 6.1 does not, so the kernel rejects such events (with EINVAL on an arm64 emulator, ENOENT on a Pixel 9). There was no fallback, so every hardware counter was dropped with "Failed to get a file descriptor for performance counter" and Android benchmarks reported no PMU data. If a hardware event cannot be opened on any CPU PMU with an extended type, open it as a plain hardware event instead, and stop trying extended types for the remaining counters. Failures on extended types are logged at --v=1 rather than as warnings, since the fallback may succeed. On heterogeneous systems without extended type support, the kernel opens the plain event on a single CPU PMU of its choosing, so it only counts while the benchmark runs on that PMU's CPUs (as before #2123). Also log CPU PMU discovery and perf_event_open results at --v=1: perf_counters: found CPU PMU armv8_pmuv3 type 8 perf_counters: could not open CYCLES (pmu 8, type 0, config 0x800000000): Invalid argument perf_counters: extended hardware event types appear to be unsupported, falling back to plain hardware events perf_counters: opened CYCLES (pmu 0) as fd 3 AI disclosure: this change was written with the help of an AI coding tool. * perf_counters: fall back to plain hardware events without a flag Instead of falling back to a plain hardware event inside the open loop (which needed the try_extended_hw_type flag, an index-based loop that appends to the PMU list, and restoring group_id), move the code that opens a counter (with retries and the fallback to a new group) into a helper that returns 0 if the counter could be opened, or else the errno of the failed perf_event_open call. Use it to first try opening each generic hardware event on all CPU PMUs with extended types and, if none of these copies can be opened, to open it as a plain hardware event, as before "Replicate generic hardware events on all CPU PMUs" (#2123). Each event is only opened once, and no state is carried from one counter to the next. Since the helper only updates group_id when a counter is opened, a counter that cannot be opened no longer forces the following counters into a new group. The --v=1 logging of every perf_event_open call is replaced by logging the copies that cannot be opened, e.g.: perf_counters: found CPU PMU armv8_pmuv3 type 8 perf_counters: cannot open CYCLES on PMU type 8: Invalid argument AI disclosure: this change was written with the help of an AI coding tool. * perf_counters: address review comments on the plain event fallback * Keep GetPMUTypesForEvent() as before, only dropping the default {0} (non-replicated events now take the plain-event path in the caller). * Document that the plain event is only opened if the event could not be opened on any CPU PMU (or is not replicated): also opening it on some PMUs would count twice on the PMU that the kernel opens the plain event on, since the counts of all PMUs are summed. No functional change. AI disclosure: this change was written with the help of an AI coding tool. * perf_counters: restore upstream formatting of attr {} The previous change accidentally reformatted `struct perf_event_attr attr {};` to `attr{}`, as newer clang-format versions do. The clang-format-lint check uses clang-format 18, which formats it as `attr {}`. AI disclosure: this change was written with the help of an AI coding tool. --------- Co-authored-by: clm <clm@google.com> Co-authored-by: Roman Lebedev <lebedev.ri@gmail.com>
On systems with more than one PMU for the CPUs (e.g. Apple M series SOCs), generic hardware events are only created for an arbitrary PMU. Usually this is the big cluster's PMU, which can cause inaccuracies when the process is scheduled onto a little core. To fix this, teach PerfCounters to register generic hardware events on all CPU PMUs.
CPU PMUs are identified using the same method as perf.