Allow for collection of more PMUs than are physically available.

Normally we can only collect as many PMUs as we have physical PMU
counters because they are managed as a single pinned group and extra
requested PMUs will not be collected. With this patch we instead collect
each PMU as a separate group once this limit is hit using PMU
multiplexing.
This commit is contained in:
Jamison Collins 2024-01-12 19:10:42 +00:00
parent ea71a14891
commit 2d609d4fa1
2 changed files with 23 additions and 15 deletions

View File

@ -86,6 +86,12 @@ PerfCounters PerfCounters::Create(
Initialize(); Initialize();
} }
// Initially try to create a single group which holds all PMUs since that
// has lower overhead. Group multiplexing doesn't work on all platforms so if
// that fails try again without using groups.
bool group_pmus = true;
retry_without_groups:
// Valid counters will populate these arrays but we start empty // Valid counters will populate these arrays but we start empty
std::vector<std::string> valid_names; std::vector<std::string> valid_names;
std::vector<int> counter_ids; std::vector<int> counter_ids;
@ -96,11 +102,6 @@ PerfCounters PerfCounters::Create(
counter_ids.reserve(counter_names.size()); counter_ids.reserve(counter_names.size());
const int kCounterMode = PFM_PLM3; // user mode only const int kCounterMode = PFM_PLM3; // user mode only
// Group leads will be assigned on demand. The idea is that once we cannot
// create a counter descriptor, the reason is that this group has maxed out
// so we set the group_id again to -1 and retry - giving the algorithm a
// chance to create a new group leader to hold the next set of counters.
int group_id = -1; int group_id = -1;
// Loop through all performance counters // Loop through all performance counters
@ -150,7 +151,7 @@ PerfCounters PerfCounters::Create(
// case. // case.
attr.disabled = is_first; attr.disabled = is_first;
attr.inherit = true; attr.inherit = true;
attr.pinned = is_first; attr.pinned = group_pmus;
attr.exclude_kernel = true; attr.exclude_kernel = true;
attr.exclude_user = false; attr.exclude_user = false;
attr.exclude_hv = true; attr.exclude_hv = true;
@ -171,10 +172,14 @@ PerfCounters PerfCounters::Create(
} }
if (id < 0) { if (id < 0) {
// If the file descriptor is negative we might have reached a limit // If the file descriptor is negative we might have reached a limit
// in the current group. Set the group_id to -1 and retry // in the current group. Let's try again without groups.
if (group_id >= 0) { if (group_id >= 0 && group_pmus) {
// Create a new group // Close all performance counters
group_id = -1; for (int close_id : counter_ids) {
::close(close_id);
}
group_pmus = false;
goto retry_without_groups;
} else { } else {
// At this point we have already retried to set a new group id and // At this point we have already retried to set a new group id and
// failed. We then give up. // failed. We then give up.
@ -197,8 +202,10 @@ PerfCounters PerfCounters::Create(
if (group_id < 0) { if (group_id < 0) {
// This is a leader, store and assign it to the current file descriptor // This is a leader, store and assign it to the current file descriptor
leader_ids.push_back(id); leader_ids.push_back(id);
if (group_pmus) {
group_id = id; group_id = id;
} }
}
// This is a valid counter, add it to our descriptor's list // This is a valid counter, add it to our descriptor's list
counter_ids.push_back(id); counter_ids.push_back(id);
valid_names.push_back(name); valid_names.push_back(name);
@ -214,9 +221,9 @@ PerfCounters PerfCounters::Create(
// This should never happen but if it does, we give up on the // This should never happen but if it does, we give up on the
// entire batch as recovery would be a mess. // entire batch as recovery would be a mess.
GetErrorLogInstance() << "***WARNING*** Failed to start counters. " GetErrorLogInstance() << "***WARNING*** Failed to start counters. "
"Claring out all counters.\n"; "Clearing out all counters.\n";
// Close all peformance counters // Close all performance counters
for (int id : counter_ids) { for (int id : counter_ids) {
::close(id); ::close(id);
} }
@ -254,7 +261,7 @@ bool PerfCounters::IsCounterSupported(const std::string&) { return false; }
PerfCounters PerfCounters::Create( PerfCounters PerfCounters::Create(
const std::vector<std::string>& counter_names) { const std::vector<std::string>& counter_names) {
if (!counter_names.empty()) { if (!counter_names.empty()) {
GetErrorLogInstance() << "Performance counters not supported.\n"; GetErrorLogInstance() << "Performance counters not supported.";
} }
return NoCounters(); return NoCounters();
} }
@ -280,3 +287,4 @@ PerfCounters& PerfCounters::operator=(PerfCounters&& other) noexcept {
} }
} // namespace internal } // namespace internal
} // namespace benchmark } // namespace benchmark

View File

@ -71,7 +71,7 @@ class BENCHMARK_EXPORT PerfCounterValues {
} }
// This reading is complex and as the goal of this class is to // This reading is complex and as the goal of this class is to
// abstract away the intrincacies of the reading process, this is // abstract away the intricacies of the reading process, this is
// a better place for it // a better place for it
size_t Read(const std::vector<int>& leaders); size_t Read(const std::vector<int>& leaders);