Refactor BenchmarkInstance (#1148)

* Refactor BenchmarkInstance (precursor to #1105)

* fix bazel (debug) build

* clang-format on header

* fix build error on g++-4.8
This commit is contained in:
Dominic Hamon 2021-05-10 17:12:09 +01:00 committed by GitHub
parent 1f47b6b64c
commit 3b508fad1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 159 additions and 130 deletions

View File

@ -448,7 +448,7 @@ struct Statistics {
: name_(name), compute_(compute) {} : name_(name), compute_(compute) {}
}; };
struct BenchmarkInstance; class BenchmarkInstance;
class ThreadTimer; class ThreadTimer;
class ThreadManager; class ThreadManager;
class PerfCountersMeasurement; class PerfCountersMeasurement;
@ -703,7 +703,7 @@ class State {
internal::ThreadManager* const manager_; internal::ThreadManager* const manager_;
internal::PerfCountersMeasurement* const perf_counters_measurement_; internal::PerfCountersMeasurement* const perf_counters_measurement_;
friend struct internal::BenchmarkInstance; friend class internal::BenchmarkInstance;
}; };
inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunning() { inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunning() {
@ -981,6 +981,7 @@ class Benchmark {
private: private:
friend class BenchmarkFamilies; friend class BenchmarkFamilies;
friend class BenchmarkInstance;
std::string name_; std::string name_;
AggregationReportMode aggregation_report_mode_; AggregationReportMode aggregation_report_mode_;

View File

@ -253,10 +253,10 @@ void RunBenchmarks(const std::vector<BenchmarkInstance>& benchmarks,
size_t stat_field_width = 0; size_t stat_field_width = 0;
for (const BenchmarkInstance& benchmark : benchmarks) { for (const BenchmarkInstance& benchmark : benchmarks) {
name_field_width = name_field_width =
std::max<size_t>(name_field_width, benchmark.name.str().size()); std::max<size_t>(name_field_width, benchmark.name().str().size());
might_have_aggregates |= benchmark.repetitions > 1; might_have_aggregates |= benchmark.repetitions() > 1;
for (const auto& Stat : *benchmark.statistics) for (const auto& Stat : benchmark.statistics())
stat_field_width = std::max<size_t>(stat_field_width, Stat.name_.size()); stat_field_width = std::max<size_t>(stat_field_width, Stat.name_.size());
} }
if (might_have_aggregates) name_field_width += 1 + stat_field_width; if (might_have_aggregates) name_field_width += 1 + stat_field_width;
@ -425,7 +425,7 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
if (FLAGS_benchmark_list_tests) { if (FLAGS_benchmark_list_tests) {
for (auto const& benchmark : benchmarks) for (auto const& benchmark : benchmarks)
Out << benchmark.name.str() << "\n"; Out << benchmark.name().str() << "\n";
} else { } else {
internal::RunBenchmarks(benchmarks, display_reporter, file_reporter); internal::RunBenchmarks(benchmarks, display_reporter, file_reporter);
} }

View File

@ -1,17 +1,91 @@
#include "benchmark_api_internal.h" #include "benchmark_api_internal.h"
#include <cinttypes>
#include "string_util.h"
namespace benchmark { namespace benchmark {
namespace internal { namespace internal {
BenchmarkInstance::BenchmarkInstance(Benchmark* benchmark,
const std::vector<int64_t>& args,
int thread_count)
: benchmark_(*benchmark),
aggregation_report_mode_(benchmark_.aggregation_report_mode_),
args_(args),
time_unit_(benchmark_.time_unit_),
measure_process_cpu_time_(benchmark_.measure_process_cpu_time_),
use_real_time_(benchmark_.use_real_time_),
use_manual_time_(benchmark_.use_manual_time_),
complexity_(benchmark_.complexity_),
complexity_lambda_(benchmark_.complexity_lambda_),
statistics_(benchmark_.statistics_),
repetitions_(benchmark_.repetitions_),
min_time_(benchmark_.min_time_),
iterations_(benchmark_.iterations_),
threads_(thread_count) {
name_.function_name = benchmark_.name_;
size_t arg_i = 0;
for (const auto& arg : args) {
if (!name_.args.empty()) {
name_.args += '/';
}
if (arg_i < benchmark->arg_names_.size()) {
const auto& arg_name = benchmark_.arg_names_[arg_i];
if (!arg_name.empty()) {
name_.args += StrFormat("%s:", arg_name.c_str());
}
}
name_.args += StrFormat("%" PRId64, arg);
++arg_i;
}
if (!IsZero(benchmark->min_time_)) {
name_.min_time = StrFormat("min_time:%0.3f", benchmark_.min_time_);
}
if (benchmark_.iterations_ != 0) {
name_.iterations = StrFormat(
"iterations:%lu", static_cast<unsigned long>(benchmark_.iterations_));
}
if (benchmark_.repetitions_ != 0) {
name_.repetitions = StrFormat("repeats:%d", benchmark_.repetitions_);
}
if (benchmark_.measure_process_cpu_time_) {
name_.time_type = "process_time";
}
if (benchmark_.use_manual_time_) {
if (!name_.time_type.empty()) {
name_.time_type += '/';
}
name_.time_type += "manual_time";
} else if (benchmark_.use_real_time_) {
if (!name_.time_type.empty()) {
name_.time_type += '/';
}
name_.time_type += "real_time";
}
if (!benchmark_.thread_counts_.empty()) {
name_.threads = StrFormat("threads:%d", threads_);
}
}
State BenchmarkInstance::Run( State BenchmarkInstance::Run(
IterationCount iters, int thread_id, internal::ThreadTimer* timer, IterationCount iters, int thread_id, internal::ThreadTimer* timer,
internal::ThreadManager* manager, internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement) const { internal::PerfCountersMeasurement* perf_counters_measurement) const {
State st(iters, arg, thread_id, threads, timer, manager, State st(iters, args_, thread_id, threads_, timer, manager,
perf_counters_measurement); perf_counters_measurement);
benchmark->Run(st); benchmark_.Run(st);
return st; return st;
} }
} // internal } // namespace internal
} // benchmark } // namespace benchmark

View File

@ -1,9 +1,6 @@
#ifndef BENCHMARK_API_INTERNAL_H #ifndef BENCHMARK_API_INTERNAL_H
#define BENCHMARK_API_INTERNAL_H #define BENCHMARK_API_INTERNAL_H
#include "benchmark/benchmark.h"
#include "commandlineflags.h"
#include <cmath> #include <cmath>
#include <iosfwd> #include <iosfwd>
#include <limits> #include <limits>
@ -11,33 +8,57 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "benchmark/benchmark.h"
#include "commandlineflags.h"
namespace benchmark { namespace benchmark {
namespace internal { namespace internal {
// Information kept per benchmark we may want to run // Information kept per benchmark we may want to run
struct BenchmarkInstance { class BenchmarkInstance {
BenchmarkName name; public:
Benchmark* benchmark; BenchmarkInstance(Benchmark* benchmark, const std::vector<int64_t>& args,
AggregationReportMode aggregation_report_mode; int threads);
std::vector<int64_t> arg;
TimeUnit time_unit; const BenchmarkName& name() const { return name_; }
int range_multiplier; AggregationReportMode aggregation_report_mode() const {
bool measure_process_cpu_time; return aggregation_report_mode_;
bool use_real_time; }
bool use_manual_time; TimeUnit time_unit() const { return time_unit_; }
BigO complexity; bool measure_process_cpu_time() const { return measure_process_cpu_time_; }
BigOFunc* complexity_lambda; bool use_real_time() const { return use_real_time_; }
UserCounters counters; bool use_manual_time() const { return use_manual_time_; }
const std::vector<Statistics>* statistics; BigO complexity() const { return complexity_; }
BigOFunc& complexity_lambda() const { return *complexity_lambda_; }
const std::vector<Statistics>& statistics() const { return statistics_; }
int repetitions() const { return repetitions_; }
double min_time() const { return min_time_; }
IterationCount iterations() const { return iterations_; }
int threads() const { return threads_; }
bool last_benchmark_instance; bool last_benchmark_instance;
int repetitions;
double min_time;
IterationCount iterations;
int threads; // Number of concurrent threads to us
State Run(IterationCount iters, int thread_id, internal::ThreadTimer* timer, State Run(IterationCount iters, int thread_id, internal::ThreadTimer* timer,
internal::ThreadManager* manager, internal::ThreadManager* manager,
internal::PerfCountersMeasurement* perf_counters_measurement) const; internal::PerfCountersMeasurement* perf_counters_measurement) const;
private:
BenchmarkName name_;
Benchmark& benchmark_;
AggregationReportMode aggregation_report_mode_;
const std::vector<int64_t>& args_;
TimeUnit time_unit_;
bool measure_process_cpu_time_;
bool use_real_time_;
bool use_manual_time_;
BigO complexity_;
BigOFunc* complexity_lambda_;
UserCounters counters_;
const std::vector<Statistics>& statistics_;
int repetitions_;
double min_time_;
IterationCount iterations_;
int threads_; // Number of concurrent threads to us
}; };
bool FindBenchmarksInternal(const std::string& re, bool FindBenchmarksInternal(const std::string& re,

View File

@ -154,76 +154,9 @@ bool BenchmarkFamilies::FindBenchmarks(
for (auto const& args : family->args_) { for (auto const& args : family->args_) {
for (int num_threads : *thread_counts) { for (int num_threads : *thread_counts) {
BenchmarkInstance instance; BenchmarkInstance instance(family.get(), args, num_threads);
instance.name.function_name = family->name_;
instance.benchmark = family.get();
instance.aggregation_report_mode = family->aggregation_report_mode_;
instance.arg = args;
instance.time_unit = family->time_unit_;
instance.range_multiplier = family->range_multiplier_;
instance.min_time = family->min_time_;
instance.iterations = family->iterations_;
instance.repetitions = family->repetitions_;
instance.measure_process_cpu_time = family->measure_process_cpu_time_;
instance.use_real_time = family->use_real_time_;
instance.use_manual_time = family->use_manual_time_;
instance.complexity = family->complexity_;
instance.complexity_lambda = family->complexity_lambda_;
instance.statistics = &family->statistics_;
instance.threads = num_threads;
// Add arguments to instance name const auto full_name = instance.name().str();
size_t arg_i = 0;
for (auto const& arg : args) {
if (!instance.name.args.empty()) {
instance.name.args += '/';
}
if (arg_i < family->arg_names_.size()) {
const auto& arg_name = family->arg_names_[arg_i];
if (!arg_name.empty()) {
instance.name.args += StrFormat("%s:", arg_name.c_str());
}
}
instance.name.args += StrFormat("%" PRId64, arg);
++arg_i;
}
if (!IsZero(family->min_time_))
instance.name.min_time =
StrFormat("min_time:%0.3f", family->min_time_);
if (family->iterations_ != 0) {
instance.name.iterations =
StrFormat("iterations:%lu",
static_cast<unsigned long>(family->iterations_));
}
if (family->repetitions_ != 0)
instance.name.repetitions =
StrFormat("repeats:%d", family->repetitions_);
if (family->measure_process_cpu_time_) {
instance.name.time_type = "process_time";
}
if (family->use_manual_time_) {
if (!instance.name.time_type.empty()) {
instance.name.time_type += '/';
}
instance.name.time_type += "manual_time";
} else if (family->use_real_time_) {
if (!instance.name.time_type.empty()) {
instance.name.time_type += '/';
}
instance.name.time_type += "real_time";
}
// Add the number of threads used to the name
if (!family->thread_counts_.empty()) {
instance.name.threads = StrFormat("threads:%d", instance.threads);
}
const auto full_name = instance.name.str();
if ((re.Match(full_name) && !isNegativeFilter) || if ((re.Match(full_name) && !isNegativeFilter) ||
(!re.Match(full_name) && isNegativeFilter)) { (!re.Match(full_name) && isNegativeFilter)) {
instance.last_benchmark_instance = (&args == &family->args_.back()); instance.last_benchmark_instance = (&args == &family->args_.back());

View File

@ -71,28 +71,28 @@ BenchmarkReporter::Run CreateRunReport(
// Create report about this benchmark run. // Create report about this benchmark run.
BenchmarkReporter::Run report; BenchmarkReporter::Run report;
report.run_name = b.name; report.run_name = b.name();
report.error_occurred = results.has_error_; report.error_occurred = results.has_error_;
report.error_message = results.error_message_; report.error_message = results.error_message_;
report.report_label = results.report_label_; report.report_label = results.report_label_;
// This is the total iterations across all threads. // This is the total iterations across all threads.
report.iterations = results.iterations; report.iterations = results.iterations;
report.time_unit = b.time_unit; report.time_unit = b.time_unit();
report.threads = b.threads; report.threads = b.threads();
report.repetition_index = repetition_index; report.repetition_index = repetition_index;
report.repetitions = b.repetitions; report.repetitions = b.repetitions();
if (!report.error_occurred) { if (!report.error_occurred) {
if (b.use_manual_time) { if (b.use_manual_time()) {
report.real_accumulated_time = results.manual_time_used; report.real_accumulated_time = results.manual_time_used;
} else { } else {
report.real_accumulated_time = results.real_time_used; report.real_accumulated_time = results.real_time_used;
} }
report.cpu_accumulated_time = results.cpu_time_used; report.cpu_accumulated_time = results.cpu_time_used;
report.complexity_n = results.complexity_n; report.complexity_n = results.complexity_n;
report.complexity = b.complexity; report.complexity = b.complexity();
report.complexity_lambda = b.complexity_lambda; report.complexity_lambda = b.complexity_lambda();
report.statistics = b.statistics; report.statistics = &b.statistics();
report.counters = results.counters; report.counters = results.counters;
if (memory_iterations > 0) { if (memory_iterations > 0) {
@ -104,7 +104,7 @@ BenchmarkReporter::Run CreateRunReport(
report.max_bytes_used = memory_result.max_bytes_used; report.max_bytes_used = memory_result.max_bytes_used;
} }
internal::Finish(&report.counters, results.iterations, seconds, b.threads); internal::Finish(&report.counters, results.iterations, seconds, b.threads());
} }
return report; return report;
} }
@ -115,7 +115,7 @@ void RunInThread(const BenchmarkInstance* b, IterationCount iters,
int thread_id, ThreadManager* manager, int thread_id, ThreadManager* manager,
PerfCountersMeasurement* perf_counters_measurement) { PerfCountersMeasurement* perf_counters_measurement) {
internal::ThreadTimer timer( internal::ThreadTimer timer(
b->measure_process_cpu_time b->measure_process_cpu_time()
? internal::ThreadTimer::CreateProcessCpuTime() ? internal::ThreadTimer::CreateProcessCpuTime()
: internal::ThreadTimer::Create()); : internal::ThreadTimer::Create());
State st = State st =
@ -141,12 +141,12 @@ class BenchmarkRunner {
std::vector<BenchmarkReporter::Run>* complexity_reports_) std::vector<BenchmarkReporter::Run>* complexity_reports_)
: b(b_), : b(b_),
complexity_reports(*complexity_reports_), complexity_reports(*complexity_reports_),
min_time(!IsZero(b.min_time) ? b.min_time : FLAGS_benchmark_min_time), min_time(!IsZero(b.min_time()) ? b.min_time() : FLAGS_benchmark_min_time),
repeats(b.repetitions != 0 ? b.repetitions repeats(b.repetitions() != 0 ? b.repetitions()
: FLAGS_benchmark_repetitions), : FLAGS_benchmark_repetitions),
has_explicit_iteration_count(b.iterations != 0), has_explicit_iteration_count(b.iterations() != 0),
pool(b.threads - 1), pool(b.threads() - 1),
iters(has_explicit_iteration_count ? b.iterations : 1), iters(has_explicit_iteration_count ? b.iterations() : 1),
perf_counters_measurement( perf_counters_measurement(
PerfCounters::Create(StrSplit(FLAGS_benchmark_perf_counters, ','))), PerfCounters::Create(StrSplit(FLAGS_benchmark_perf_counters, ','))),
perf_counters_measurement_ptr(perf_counters_measurement.IsValid() perf_counters_measurement_ptr(perf_counters_measurement.IsValid()
@ -157,13 +157,13 @@ class BenchmarkRunner {
FLAGS_benchmark_display_aggregates_only); FLAGS_benchmark_display_aggregates_only);
run_results.file_report_aggregates_only = run_results.file_report_aggregates_only =
FLAGS_benchmark_report_aggregates_only; FLAGS_benchmark_report_aggregates_only;
if (b.aggregation_report_mode != internal::ARM_Unspecified) { if (b.aggregation_report_mode() != internal::ARM_Unspecified) {
run_results.display_report_aggregates_only = run_results.display_report_aggregates_only =
(b.aggregation_report_mode & (b.aggregation_report_mode() &
internal::ARM_DisplayReportAggregatesOnly); internal::ARM_DisplayReportAggregatesOnly);
run_results.file_report_aggregates_only = run_results.file_report_aggregates_only =
(b.aggregation_report_mode & internal::ARM_FileReportAggregatesOnly); (b.aggregation_report_mode() & internal::ARM_FileReportAggregatesOnly);
CHECK(b.threads == 1 || !perf_counters_measurement.IsValid()) CHECK(b.threads() == 1 || !perf_counters_measurement.IsValid())
<< "Perf counters are not supported in multi-threaded cases.\n"; << "Perf counters are not supported in multi-threaded cases.\n";
CHECK(FLAGS_benchmark_perf_counters.empty() || CHECK(FLAGS_benchmark_perf_counters.empty() ||
perf_counters_measurement.IsValid()) perf_counters_measurement.IsValid())
@ -178,7 +178,7 @@ class BenchmarkRunner {
run_results.aggregates_only = ComputeStats(run_results.non_aggregates); run_results.aggregates_only = ComputeStats(run_results.non_aggregates);
// Maybe calculate complexity report // Maybe calculate complexity report
if ((b.complexity != oNone) && b.last_benchmark_instance) { if ((b.complexity() != oNone) && b.last_benchmark_instance) {
auto additional_run_stats = ComputeBigO(complexity_reports); auto additional_run_stats = ComputeBigO(complexity_reports);
run_results.aggregates_only.insert(run_results.aggregates_only.end(), run_results.aggregates_only.insert(run_results.aggregates_only.end(),
additional_run_stats.begin(), additional_run_stats.begin(),
@ -214,10 +214,10 @@ class BenchmarkRunner {
double seconds; double seconds;
}; };
IterationResults DoNIterations() { IterationResults DoNIterations() {
VLOG(2) << "Running " << b.name.str() << " for " << iters << "\n"; VLOG(2) << "Running " << b.name().str() << " for " << iters << "\n";
std::unique_ptr<internal::ThreadManager> manager; std::unique_ptr<internal::ThreadManager> manager;
manager.reset(new internal::ThreadManager(b.threads)); manager.reset(new internal::ThreadManager(b.threads()));
// Run all but one thread in separate threads // Run all but one thread in separate threads
for (std::size_t ti = 0; ti < pool.size(); ++ti) { for (std::size_t ti = 0; ti < pool.size(); ++ti) {
@ -244,23 +244,23 @@ class BenchmarkRunner {
manager.reset(); manager.reset();
// Adjust real/manual time stats since they were reported per thread. // Adjust real/manual time stats since they were reported per thread.
i.results.real_time_used /= b.threads; i.results.real_time_used /= b.threads();
i.results.manual_time_used /= b.threads; i.results.manual_time_used /= b.threads();
// If we were measuring whole-process CPU usage, adjust the CPU time too. // If we were measuring whole-process CPU usage, adjust the CPU time too.
if (b.measure_process_cpu_time) i.results.cpu_time_used /= b.threads; if (b.measure_process_cpu_time()) i.results.cpu_time_used /= b.threads();
VLOG(2) << "Ran in " << i.results.cpu_time_used << "/" VLOG(2) << "Ran in " << i.results.cpu_time_used << "/"
<< i.results.real_time_used << "\n"; << i.results.real_time_used << "\n";
// By using KeepRunningBatch a benchmark can iterate more times than // By using KeepRunningBatch a benchmark can iterate more times than
// requested, so take the iteration count from i.results. // requested, so take the iteration count from i.results.
i.iters = i.results.iterations / b.threads; i.iters = i.results.iterations / b.threads();
// Base decisions off of real time if requested by this benchmark. // Base decisions off of real time if requested by this benchmark.
i.seconds = i.results.cpu_time_used; i.seconds = i.results.cpu_time_used;
if (b.use_manual_time) { if (b.use_manual_time()) {
i.seconds = i.results.manual_time_used; i.seconds = i.results.manual_time_used;
} else if (b.use_real_time) { } else if (b.use_real_time()) {
i.seconds = i.results.real_time_used; i.seconds = i.results.real_time_used;
} }
@ -301,7 +301,7 @@ class BenchmarkRunner {
// CPU time is specified but the elapsed real time greatly exceeds // CPU time is specified but the elapsed real time greatly exceeds
// the minimum time. // the minimum time.
// Note that user provided timers are except from this sanity check. // Note that user provided timers are except from this sanity check.
((i.results.real_time_used >= 5 * min_time) && !b.use_manual_time); ((i.results.real_time_used >= 5 * min_time) && !b.use_manual_time());
} }
void DoOneRepetition(int64_t repetition_index) { void DoOneRepetition(int64_t repetition_index) {
@ -360,7 +360,7 @@ class BenchmarkRunner {
CreateRunReport(b, i.results, memory_iterations, memory_result, CreateRunReport(b, i.results, memory_iterations, memory_result,
i.seconds, repetition_index); i.seconds, repetition_index);
if (!report.error_occurred && b.complexity != oNone) if (!report.error_occurred && b.complexity() != oNone)
complexity_reports.push_back(report); complexity_reports.push_back(report);
run_results.non_aggregates.push_back(report); run_results.non_aggregates.push_back(report);