mirror of https://github.com/google/benchmark.git
Filter performance counter names, not invalidate all (#1554)
* Filter performance counter names, not invalidate all Currently, the performance counters are validated while they are being created and one failure returns NoCounters(), ie it effecitvely invalidates all the counters. I would like to propose a new behavior: filter instead. If an invalid name is added to the counter list, or if that particular counter is not supported on this platform, that counter is dropped from the list and an error messages is created, while all the other counters remain active. This will give testers a peace of mind that if one mistake is made or if something is changed or removed from libpfm, their entire test will not be invalidated. This feature gives more tolerance with respect to versioning. Another positive is that testers can now input a superset of all desired counters for all platforms they support and just let Benchmark drop all those that are not supported, although it will create quite a lot of noise down the line, in which case perhaps we should drop silently or make a consolidated, single error line but this was not implemented in this change set. * Removed unused helper type.
This commit is contained in:
parent
27c1d8ace9
commit
2d5012275a
|
@ -71,32 +71,61 @@ bool PerfCounters::IsCounterSupported(const std::string& name) {
|
||||||
return (ret == PFM_SUCCESS);
|
return (ret == PFM_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validates all counter names passed, returning only the valid ones
|
||||||
|
static std::vector<std::string> validateCounters(
|
||||||
|
const std::vector<std::string>& counter_names) {
|
||||||
|
// All valid names to be returned
|
||||||
|
std::vector<std::string> valid_names;
|
||||||
|
|
||||||
|
// Loop through all the given names
|
||||||
|
int invalid_counter = 0;
|
||||||
|
for (const std::string& name : counter_names) {
|
||||||
|
// Check trivial empty
|
||||||
|
if (name.empty()) {
|
||||||
|
GetErrorLogInstance() << "A counter name was the empty string\n";
|
||||||
|
invalid_counter++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (PerfCounters::IsCounterSupported(name)) {
|
||||||
|
// we are about to push into the valid names vector
|
||||||
|
// check if we did not reach the maximum
|
||||||
|
if (valid_names.size() == PerfCounterValues::kMaxCounters) {
|
||||||
|
GetErrorLogInstance()
|
||||||
|
<< counter_names.size()
|
||||||
|
<< " counters were requested. The maximum is "
|
||||||
|
<< PerfCounterValues::kMaxCounters << " and "
|
||||||
|
<< counter_names.size() - invalid_counter - valid_names.size()
|
||||||
|
<< " will be ignored\n";
|
||||||
|
// stop the loop and return what we have already
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
valid_names.push_back(name);
|
||||||
|
} else {
|
||||||
|
GetErrorLogInstance() << "Performance counter " << name
|
||||||
|
<< " incorrect or not supported on this platform\n";
|
||||||
|
invalid_counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// RVO should take care of this
|
||||||
|
return valid_names;
|
||||||
|
}
|
||||||
|
|
||||||
PerfCounters PerfCounters::Create(
|
PerfCounters PerfCounters::Create(
|
||||||
const std::vector<std::string>& counter_names) {
|
const std::vector<std::string>& counter_names) {
|
||||||
if (counter_names.empty()) {
|
std::vector<std::string> valid_names = validateCounters(counter_names);
|
||||||
|
if (valid_names.empty()) {
|
||||||
return NoCounters();
|
return NoCounters();
|
||||||
}
|
}
|
||||||
if (counter_names.size() > PerfCounterValues::kMaxCounters) {
|
std::vector<int> counter_ids(valid_names.size());
|
||||||
GetErrorLogInstance()
|
|
||||||
<< counter_names.size()
|
|
||||||
<< " counters were requested. The minimum is 1, the maximum is "
|
|
||||||
<< PerfCounterValues::kMaxCounters << "\n";
|
|
||||||
return NoCounters();
|
|
||||||
}
|
|
||||||
std::vector<int> counter_ids(counter_names.size());
|
|
||||||
std::vector<int> leader_ids;
|
std::vector<int> leader_ids;
|
||||||
|
|
||||||
const int mode = PFM_PLM3; // user mode only
|
const int mode = PFM_PLM3; // user mode only
|
||||||
int group_id = -1;
|
int group_id = -1;
|
||||||
for (size_t i = 0; i < counter_names.size(); ++i) {
|
for (size_t i = 0; i < valid_names.size(); ++i) {
|
||||||
const bool is_first = (group_id < 0);
|
const bool is_first = (group_id < 0);
|
||||||
struct perf_event_attr attr {};
|
struct perf_event_attr attr {};
|
||||||
attr.size = sizeof(attr);
|
attr.size = sizeof(attr);
|
||||||
const auto& name = counter_names[i];
|
const auto& name = valid_names[i];
|
||||||
if (name.empty()) {
|
|
||||||
GetErrorLogInstance() << "A counter name was the empty string\n";
|
|
||||||
return NoCounters();
|
|
||||||
}
|
|
||||||
pfm_perf_encode_arg_t arg{};
|
pfm_perf_encode_arg_t arg{};
|
||||||
arg.attr = &attr;
|
arg.attr = &attr;
|
||||||
|
|
||||||
|
@ -159,7 +188,7 @@ PerfCounters PerfCounters::Create(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PerfCounters(counter_names, std::move(counter_ids),
|
return PerfCounters(valid_names, std::move(counter_ids),
|
||||||
std::move(leader_ids));
|
std::move(leader_ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +227,9 @@ Mutex PerfCountersMeasurement::mutex_;
|
||||||
int PerfCountersMeasurement::ref_count_ = 0;
|
int PerfCountersMeasurement::ref_count_ = 0;
|
||||||
PerfCounters PerfCountersMeasurement::counters_ = PerfCounters::NoCounters();
|
PerfCounters PerfCountersMeasurement::counters_ = PerfCounters::NoCounters();
|
||||||
|
|
||||||
|
// The validation in PerfCounter::Create will create less counters than passed
|
||||||
|
// so it should be okay to initialize start_values_ and end_values_ with the
|
||||||
|
// upper bound as passed
|
||||||
PerfCountersMeasurement::PerfCountersMeasurement(
|
PerfCountersMeasurement::PerfCountersMeasurement(
|
||||||
const std::vector<std::string>& counter_names)
|
const std::vector<std::string>& counter_names)
|
||||||
: start_values_(counter_names.size()), end_values_(counter_names.size()) {
|
: start_values_(counter_names.size()), end_values_(counter_names.size()) {
|
||||||
|
|
|
@ -40,26 +40,40 @@ TEST(PerfCountersTest, NegativeTest) {
|
||||||
EXPECT_FALSE(PerfCounters::Create({}).IsValid());
|
EXPECT_FALSE(PerfCounters::Create({}).IsValid());
|
||||||
EXPECT_FALSE(PerfCounters::Create({""}).IsValid());
|
EXPECT_FALSE(PerfCounters::Create({""}).IsValid());
|
||||||
EXPECT_FALSE(PerfCounters::Create({"not a counter name"}).IsValid());
|
EXPECT_FALSE(PerfCounters::Create({"not a counter name"}).IsValid());
|
||||||
|
EXPECT_TRUE(PerfCounters::Create(
|
||||||
|
{kGenericPerfEvent1, kGenericPerfEvent2, kGenericPerfEvent3})
|
||||||
|
.IsValid());
|
||||||
|
{
|
||||||
|
auto counter =
|
||||||
|
PerfCounters::Create({kGenericPerfEvent2, "", kGenericPerfEvent1});
|
||||||
|
EXPECT_TRUE(counter.IsValid());
|
||||||
|
EXPECT_EQ(counter.num_counters(), 2);
|
||||||
|
EXPECT_EQ(counter.names(), std::vector<std::string>(
|
||||||
|
{kGenericPerfEvent2, kGenericPerfEvent1}));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto counter = PerfCounters::Create(
|
||||||
|
{kGenericPerfEvent3, "not a counter name", kGenericPerfEvent1});
|
||||||
|
EXPECT_TRUE(counter.IsValid());
|
||||||
|
EXPECT_EQ(counter.num_counters(), 2);
|
||||||
|
EXPECT_EQ(counter.names(), std::vector<std::string>(
|
||||||
|
{kGenericPerfEvent3, kGenericPerfEvent1}));
|
||||||
|
}
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
|
EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
|
||||||
kGenericPerfEvent3})
|
kGenericPerfEvent3})
|
||||||
.IsValid());
|
.IsValid());
|
||||||
}
|
}
|
||||||
EXPECT_FALSE(
|
|
||||||
PerfCounters::Create({kGenericPerfEvent2, "", kGenericPerfEvent1})
|
|
||||||
.IsValid());
|
|
||||||
EXPECT_FALSE(PerfCounters::Create({kGenericPerfEvent3, "not a counter name",
|
|
||||||
kGenericPerfEvent1})
|
|
||||||
.IsValid());
|
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
|
auto counter = PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
|
||||||
kGenericPerfEvent3})
|
kGenericPerfEvent3,
|
||||||
.IsValid());
|
"MISPREDICTED_BRANCH_RETIRED"});
|
||||||
|
EXPECT_TRUE(counter.IsValid());
|
||||||
|
EXPECT_EQ(counter.num_counters(), 3);
|
||||||
|
EXPECT_EQ(counter.names(),
|
||||||
|
std::vector<std::string>({kGenericPerfEvent1, kGenericPerfEvent2,
|
||||||
|
kGenericPerfEvent3}));
|
||||||
}
|
}
|
||||||
EXPECT_FALSE(
|
|
||||||
PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
|
|
||||||
kGenericPerfEvent3, "MISPREDICTED_BRANCH_RETIRED"})
|
|
||||||
.IsValid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PerfCountersTest, Read1Counter) {
|
TEST(PerfCountersTest, Read1Counter) {
|
||||||
|
@ -157,9 +171,9 @@ void measure(size_t threadcount, PerfCounterValues* values1,
|
||||||
auto work = [&]() { BM_CHECK(do_work() > 1000); };
|
auto work = [&]() { BM_CHECK(do_work() > 1000); };
|
||||||
|
|
||||||
// We need to first set up the counters, then start the threads, so the
|
// We need to first set up the counters, then start the threads, so the
|
||||||
// threads would inherit the counters. But later, we need to first destroy the
|
// threads would inherit the counters. But later, we need to first destroy
|
||||||
// thread pool (so all the work finishes), then measure the counters. So the
|
// the thread pool (so all the work finishes), then measure the counters. So
|
||||||
// scopes overlap, and we need to explicitly control the scope of the
|
// the scopes overlap, and we need to explicitly control the scope of the
|
||||||
// threadpool.
|
// threadpool.
|
||||||
auto counters =
|
auto counters =
|
||||||
PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent3});
|
PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent3});
|
||||||
|
|
Loading…
Reference in New Issue