Skip to content

Commit

Permalink
Merge e731169 into 847c006
Browse files Browse the repository at this point in the history
  • Loading branch information
dominichamon committed Jul 2, 2018
2 parents 847c006 + e731169 commit 4225cfb
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 13 deletions.
43 changes: 36 additions & 7 deletions include/benchmark/benchmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);

namespace benchmark {
class BenchmarkReporter;
class MemoryManager;

void Initialize(int* argc, char** argv);

Expand All @@ -267,12 +268,9 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter);
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter,
BenchmarkReporter* file_reporter);

// If this routine is called, peak memory allocation past this point in the
// benchmark is reported at the end of the benchmark report line. (It is
// computed by running the benchmark once with a single iteration and a memory
// tracer.)
// TODO(dominic)
// void MemoryUsage();
// Register a MemoryManager instance that will be used to collect and report
// allocation measurements for benchmark runs.
void RegisterMemoryManager(MemoryManager* memory_manager);

namespace internal {
class Benchmark;
Expand Down Expand Up @@ -1280,7 +1278,10 @@ class BenchmarkReporter {
complexity_n(0),
report_big_o(false),
report_rms(false),
counters() {}
counters(),
has_memory_result(false),
allocs_per_iter(0.0),
max_bytes_used(0) {}

std::string benchmark_name;
std::string report_label; // Empty if not set by benchmark.
Expand Down Expand Up @@ -1324,6 +1325,11 @@ class BenchmarkReporter {
bool report_rms;

UserCounters counters;

// Memory metrics.
bool has_memory_result;
double allocs_per_iter;
int64_t max_bytes_used;
};

// Construct a BenchmarkReporter with the output stream set to 'std::cout'
Expand Down Expand Up @@ -1438,6 +1444,29 @@ class BENCHMARK_DEPRECATED_MSG("The CSV Reporter will be removed in a future rel
std::set<std::string> user_counter_names_;
};

// If a MemoryManager is registered, it can be used to collect and report
// allocation metrics for a run of the benchmark.
class MemoryManager {
public:
struct Result {
Result() : num_allocs(0), max_bytes_used(0) {}

// The number of allocations made in total between Start and Stop.
int64_t num_allocs;

// The peak memory use between Start and Stop.
int64_t max_bytes_used;
};

virtual ~MemoryManager() {}

// Implement this to start recording allocation information.
virtual void Start() = 0;

// Implement this to stop recording and fill out the given Result structure.
virtual void Stop(Result* result) = 0;
};

inline const char* GetTimeUnitString(TimeUnit unit) {
switch (unit) {
case kMillisecond:
Expand Down
41 changes: 36 additions & 5 deletions src/benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ DEFINE_int32(v, 0, "The level of verbose logging to output");
namespace benchmark {

namespace {
static const size_t kMaxIterations = 1000000000;
static const int64_t kMaxIterations = 1000000000;

static MemoryManager* memory_manager = nullptr;
} // end namespace

namespace internal {
Expand All @@ -115,7 +117,8 @@ namespace {

BenchmarkReporter::Run CreateRunReport(
const benchmark::internal::Benchmark::Instance& b,
const internal::ThreadManager::Result& results, double seconds) {
const internal::ThreadManager::Result& results, int64_t memory_iterations,
const MemoryManager::Result& memory_result, double seconds) {
// Create report about this benchmark run.
BenchmarkReporter::Run report;

Expand Down Expand Up @@ -150,6 +153,16 @@ BenchmarkReporter::Run CreateRunReport(
report.complexity_lambda = b.complexity_lambda;
report.statistics = b.statistics;
report.counters = results.counters;

if (memory_iterations > 0) {
report.has_memory_result = true;
report.allocs_per_iter =
memory_iterations ? static_cast<double>(memory_result.num_allocs) /
memory_iterations
: 0;
report.max_bytes_used = memory_result.max_bytes_used;
}

internal::Finish(&report.counters, results.iterations, seconds, b.threads);
}
return report;
Expand All @@ -158,7 +171,7 @@ BenchmarkReporter::Run CreateRunReport(
// Execute one thread of benchmark b for the specified number of iterations.
// Adds the stats collected for the thread into *total.
void RunInThread(const benchmark::internal::Benchmark::Instance* b,
size_t iters, int thread_id,
int64_t iters, int thread_id,
internal::ThreadManager* manager) {
internal::ThreadTimer timer;
State st(iters, b->arg, thread_id, b->threads, &timer, manager);
Expand Down Expand Up @@ -186,7 +199,7 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(
std::vector<BenchmarkReporter::Run> reports; // return value

const bool has_explicit_iteration_count = b.iterations != 0;
size_t iters = has_explicit_iteration_count ? b.iterations : 1;
int64_t iters = has_explicit_iteration_count ? b.iterations : 1;
std::unique_ptr<internal::ThreadManager> manager;
std::vector<std::thread> pool(b.threads - 1);
const int repeats =
Expand Down Expand Up @@ -249,7 +262,23 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(
// clang-format on

if (should_report) {
BenchmarkReporter::Run report = CreateRunReport(b, results, seconds);
MemoryManager::Result memory_result;
int64_t memory_iterations = 0;
if (memory_manager != nullptr) {
// Only run a few iterations to reduce the impact of one-time
// allocations in benchmarks that are not properly managed.
memory_iterations = std::min<int64_t>(16, iters);
memory_manager->Start();
manager.reset(new internal::ThreadManager(1));
RunInThread(&b, memory_iterations, 0, manager.get());
manager->WaitForAllThreads();
manager.reset();

memory_manager->Stop(&memory_result);
}

BenchmarkReporter::Run report = CreateRunReport(
b, results, memory_iterations, memory_result, seconds);
if (!report.error_occurred && b.complexity != oNone)
complexity_reports->push_back(report);
reports.push_back(report);
Expand Down Expand Up @@ -550,6 +579,8 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter,
return benchmarks.size();
}

void RegisterMemoryManager(MemoryManager* manager) { memory_manager = manager; }

namespace internal {

void PrintUsageAndExit() {
Expand Down
2 changes: 1 addition & 1 deletion src/benchmark_api_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct Benchmark::Instance {
bool last_benchmark_instance;
int repetitions;
double min_time;
size_t iterations;
int64_t iterations;
int threads; // Number of concurrent threads to us
};

Expand Down
6 changes: 6 additions & 0 deletions src/json_reporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ void JSONReporter::PrintRunData(Run const& run) {
for (auto& c : run.counters) {
out << ",\n" << indent << FormatKV(c.first, c.second);
}

if (run.has_memory_result) {
out << ",\n" << indent << FormatKV("allocs_per_iter", run.allocs_per_iter);
out << ",\n" << indent << FormatKV("max_bytes_used", run.max_bytes_used);
}

if (!run.report_label.empty()) {
out << ",\n" << indent << FormatKV("label", run.report_label);
}
Expand Down
40 changes: 40 additions & 0 deletions test/memory_manager_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <memory>

#include "../src/check.h"
#include "benchmark/benchmark.h"
#include "output_test.h"

class TestMemoryManager : public benchmark::MemoryManager {
void Start() {}
void Stop(Result* result) {
result->num_allocs = 42;
result->max_bytes_used = 42000;
}
};

void BM_empty(benchmark::State& state) {
for (auto _ : state) {
benchmark::DoNotOptimize(state.iterations());
}
}
BENCHMARK(BM_empty);

ADD_CASES(TC_ConsoleOut, {{"^BM_empty %console_report$"}});
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_empty\",$"},
{"\"iterations\": %int,$", MR_Next},
{"\"real_time\": %float,$", MR_Next},
{"\"cpu_time\": %float,$", MR_Next},
{"\"time_unit\": \"ns\",$", MR_Next},
{"\"allocs_per_iter\": %float,$", MR_Next},
{"\"max_bytes_used\": 42000$", MR_Next},
{"}", MR_Next}});
ADD_CASES(TC_CSVOut, {{"^\"BM_empty\",%csv_report$"}});


int main(int argc, char *argv[]) {
std::unique_ptr<benchmark::MemoryManager> mm(new TestMemoryManager());

benchmark::RegisterMemoryManager(mm.get());
RunOutputTests(argc, argv);
benchmark::RegisterMemoryManager(nullptr);
}

0 comments on commit 4225cfb

Please sign in to comment.