mirror of
https://github.com/google/benchmark.git
synced 2024-12-27 13:00:36 +08:00
296ec5693e
* Support State::KeepRunningBatch(). State::KeepRunning() can take large amounts of time relative to quick operations (on the order of 1ns, depending on hardware). For such sensitive operations, it is recommended to run batches of repeated operations. This commit simplifies handling of total_iterations_. Rather than predecrementing such that total_iterations_ == 1 signals that KeepRunning() should exit, total_iterations_ == 0 now signals the intention for the benchmark to exit. * Create better fast path in State::KeepRunningBatch() * Replace int parameter with size_t to fix signed mismatch warnings * Ensure benchmark State has been started even on error. * Simplify KeepRunningBatch()
193 lines
6.2 KiB
C++
193 lines
6.2 KiB
C++
|
|
#undef NDEBUG
|
|
#include <cassert>
|
|
#include <vector>
|
|
|
|
#include "../src/check.h" // NOTE: check.h is for internal use only!
|
|
#include "benchmark/benchmark.h"
|
|
|
|
namespace {
|
|
|
|
class TestReporter : public benchmark::ConsoleReporter {
|
|
public:
|
|
virtual bool ReportContext(const Context& context) {
|
|
return ConsoleReporter::ReportContext(context);
|
|
};
|
|
|
|
virtual void ReportRuns(const std::vector<Run>& report) {
|
|
all_runs_.insert(all_runs_.end(), begin(report), end(report));
|
|
ConsoleReporter::ReportRuns(report);
|
|
}
|
|
|
|
TestReporter() {}
|
|
virtual ~TestReporter() {}
|
|
|
|
mutable std::vector<Run> all_runs_;
|
|
};
|
|
|
|
struct TestCase {
|
|
std::string name;
|
|
bool error_occurred;
|
|
std::string error_message;
|
|
|
|
typedef benchmark::BenchmarkReporter::Run Run;
|
|
|
|
void CheckRun(Run const& run) const {
|
|
CHECK(name == run.benchmark_name) << "expected " << name << " got "
|
|
<< run.benchmark_name;
|
|
CHECK(error_occurred == run.error_occurred);
|
|
CHECK(error_message == run.error_message);
|
|
if (error_occurred) {
|
|
// CHECK(run.iterations == 0);
|
|
} else {
|
|
CHECK(run.iterations != 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::vector<TestCase> ExpectedResults;
|
|
|
|
int AddCases(const char* base_name, std::initializer_list<TestCase> const& v) {
|
|
for (auto TC : v) {
|
|
TC.name = base_name + TC.name;
|
|
ExpectedResults.push_back(std::move(TC));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define CONCAT(x, y) CONCAT2(x, y)
|
|
#define CONCAT2(x, y) x##y
|
|
#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = AddCases(__VA_ARGS__)
|
|
|
|
} // end namespace
|
|
|
|
void BM_error_before_running(benchmark::State& state) {
|
|
state.SkipWithError("error message");
|
|
while (state.KeepRunning()) {
|
|
assert(false);
|
|
}
|
|
}
|
|
BENCHMARK(BM_error_before_running);
|
|
ADD_CASES("BM_error_before_running", {{"", true, "error message"}});
|
|
|
|
|
|
void BM_error_before_running_batch(benchmark::State& state) {
|
|
state.SkipWithError("error message");
|
|
while (state.KeepRunningBatch(17)) {
|
|
assert(false);
|
|
}
|
|
}
|
|
BENCHMARK(BM_error_before_running_batch);
|
|
ADD_CASES("BM_error_before_running_batch", {{"", true, "error message"}});
|
|
|
|
void BM_error_before_running_range_for(benchmark::State& state) {
|
|
state.SkipWithError("error message");
|
|
for (auto _ : state) {
|
|
assert(false);
|
|
}
|
|
}
|
|
BENCHMARK(BM_error_before_running_range_for);
|
|
ADD_CASES("BM_error_before_running_range_for", {{"", true, "error message"}});
|
|
|
|
void BM_error_during_running(benchmark::State& state) {
|
|
int first_iter = true;
|
|
while (state.KeepRunning()) {
|
|
if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
|
|
assert(first_iter);
|
|
first_iter = false;
|
|
state.SkipWithError("error message");
|
|
} else {
|
|
state.PauseTiming();
|
|
state.ResumeTiming();
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(BM_error_during_running)->Arg(1)->Arg(2)->ThreadRange(1, 8);
|
|
ADD_CASES("BM_error_during_running", {{"/1/threads:1", true, "error message"},
|
|
{"/1/threads:2", true, "error message"},
|
|
{"/1/threads:4", true, "error message"},
|
|
{"/1/threads:8", true, "error message"},
|
|
{"/2/threads:1", false, ""},
|
|
{"/2/threads:2", false, ""},
|
|
{"/2/threads:4", false, ""},
|
|
{"/2/threads:8", false, ""}});
|
|
|
|
void BM_error_during_running_ranged_for(benchmark::State& state) {
|
|
assert(state.max_iterations > 3 && "test requires at least a few iterations");
|
|
int first_iter = true;
|
|
// NOTE: Users should not write the for loop explicitly.
|
|
for (auto It = state.begin(), End = state.end(); It != End; ++It) {
|
|
if (state.range(0) == 1) {
|
|
assert(first_iter);
|
|
first_iter = false;
|
|
state.SkipWithError("error message");
|
|
// Test the unfortunate but documented behavior that the ranged-for loop
|
|
// doesn't automatically terminate when SkipWithError is set.
|
|
assert(++It != End);
|
|
break; // Required behavior
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(BM_error_during_running_ranged_for)->Arg(1)->Arg(2)->Iterations(5);
|
|
ADD_CASES("BM_error_during_running_ranged_for",
|
|
{{"/1/iterations:5", true, "error message"},
|
|
{"/2/iterations:5", false, ""}});
|
|
|
|
|
|
|
|
void BM_error_after_running(benchmark::State& state) {
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(state.iterations());
|
|
}
|
|
if (state.thread_index <= (state.threads / 2))
|
|
state.SkipWithError("error message");
|
|
}
|
|
BENCHMARK(BM_error_after_running)->ThreadRange(1, 8);
|
|
ADD_CASES("BM_error_after_running", {{"/threads:1", true, "error message"},
|
|
{"/threads:2", true, "error message"},
|
|
{"/threads:4", true, "error message"},
|
|
{"/threads:8", true, "error message"}});
|
|
|
|
void BM_error_while_paused(benchmark::State& state) {
|
|
bool first_iter = true;
|
|
while (state.KeepRunning()) {
|
|
if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
|
|
assert(first_iter);
|
|
first_iter = false;
|
|
state.PauseTiming();
|
|
state.SkipWithError("error message");
|
|
} else {
|
|
state.PauseTiming();
|
|
state.ResumeTiming();
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(BM_error_while_paused)->Arg(1)->Arg(2)->ThreadRange(1, 8);
|
|
ADD_CASES("BM_error_while_paused", {{"/1/threads:1", true, "error message"},
|
|
{"/1/threads:2", true, "error message"},
|
|
{"/1/threads:4", true, "error message"},
|
|
{"/1/threads:8", true, "error message"},
|
|
{"/2/threads:1", false, ""},
|
|
{"/2/threads:2", false, ""},
|
|
{"/2/threads:4", false, ""},
|
|
{"/2/threads:8", false, ""}});
|
|
|
|
int main(int argc, char* argv[]) {
|
|
benchmark::Initialize(&argc, argv);
|
|
|
|
TestReporter test_reporter;
|
|
benchmark::RunSpecifiedBenchmarks(&test_reporter);
|
|
|
|
typedef benchmark::BenchmarkReporter::Run Run;
|
|
auto EB = ExpectedResults.begin();
|
|
|
|
for (Run const& run : test_reporter.all_runs_) {
|
|
assert(EB != ExpectedResults.end());
|
|
EB->CheckRun(run);
|
|
++EB;
|
|
}
|
|
assert(EB == ExpectedResults.end());
|
|
|
|
return 0;
|
|
}
|