From 22fd1a556eadce4f314a9819ed29cc886e9ba552 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Tue, 17 Oct 2017 10:24:13 -0600 Subject: [PATCH] Fix and document SkipWithError(...) using ranged-for loop. --- include/benchmark/benchmark.h | 12 ++++++++---- test/skip_with_error_test.cc | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/include/benchmark/benchmark.h b/include/benchmark/benchmark.h index 2b745d7b..ead93b6c 100644 --- a/include/benchmark/benchmark.h +++ b/include/benchmark/benchmark.h @@ -467,9 +467,13 @@ class State { // REQUIRES: 'SkipWithError(...)' has not been called previously by the // current thread. - // Skip any future iterations of the 'KeepRunning()' loop in the current - // thread and report an error with the specified 'msg'. After this call - // the user may explicitly 'return' from the benchmark. + // Report the benchmark as resulting in an error with the specified 'msg'. + // After this call the user may explicitly 'return' from the benchmark. + // + // If the ranged-for style of benchmark loop is used, the user must explicitly + // break from the loop, otherwise all future iterations will be run. + // If the 'KeepRunning()' loop is used the current thread will automatically + // exit the loop at the end of the current iteration. // // For threaded benchmarks only the current thread stops executing and future // calls to `KeepRunning()` will block until all threads have completed @@ -611,7 +615,7 @@ struct State::StateIterator { BENCHMARK_ALWAYS_INLINE explicit StateIterator(State* st) - : cached_(st->max_iterations), parent_(st) {} + : cached_(st->error_occurred_ ? 0 : st->max_iterations), parent_(st) {} public: BENCHMARK_ALWAYS_INLINE diff --git a/test/skip_with_error_test.cc b/test/skip_with_error_test.cc index b74d33c5..0c2f3481 100644 --- a/test/skip_with_error_test.cc +++ b/test/skip_with_error_test.cc @@ -70,6 +70,15 @@ void BM_error_before_running(benchmark::State& state) { BENCHMARK(BM_error_before_running); ADD_CASES("BM_error_before_running", {{"", 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()) { @@ -93,8 +102,31 @@ ADD_CASES("BM_error_during_running", {{"/1/threads:1", true, "error message"}, {"/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) { - while (state.KeepRunning()) { + for (auto _ : state) { benchmark::DoNotOptimize(state.iterations()); } if (state.thread_index <= (state.threads / 2))