Skip to content

Commit de38336

Browse files
authored
Merge e288bb8 into df415ad
2 parents df415ad + e288bb8 commit de38336

File tree

3 files changed

+76
-20
lines changed

3 files changed

+76
-20
lines changed

include/benchmark/benchmark.h

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
172172

173173
#include <stdint.h>
174174

175+
#include <algorithm>
175176
#include <cassert>
176177
#include <cstddef>
177178
#include <iosfwd>
@@ -429,16 +430,18 @@ class State {
429430
// Returns true if the benchmark should continue through another iteration.
430431
// NOTE: A benchmark may not return from the test until KeepRunning() has
431432
// returned false.
432-
bool KeepRunning() {
433-
if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
434-
StartKeepRunning();
435-
}
436-
bool const res = (--total_iterations_ != 0);
437-
if (BENCHMARK_BUILTIN_EXPECT(!res, false)) {
438-
FinishKeepRunning();
439-
}
440-
return res;
441-
}
433+
bool KeepRunning();
434+
435+
// Returns true iff the benchmark should run n more iterations.
436+
// NOTE: A benchmark must not return from the test until KeepRunningBatch()
437+
// has returned false.
438+
// NOTE: KeepRunningBatch() may overshoot by up to 'n' iterations.
439+
//
440+
// Intended usage:
441+
// while (state.KeepRunningBatch(1000)) {
442+
// // process 1000 elements
443+
// }
444+
bool KeepRunningBatch(int n);
442445

443446
// REQUIRES: timer is running and 'SkipWithError(...)' has not been called
444447
// by the current thread.
@@ -565,12 +568,15 @@ class State {
565568
int range_y() const { return range(1); }
566569

567570
BENCHMARK_ALWAYS_INLINE
568-
size_t iterations() const { return (max_iterations - total_iterations_) + 1; }
571+
size_t iterations() const { return (completed_iterations_ - total_iterations_); }
569572

570573
private:
571574
bool started_;
572575
bool finished_;
576+
// When total_iterations_ is 0, KeepRunning() and friends will return false.
573577
size_t total_iterations_;
578+
// May be larger than max_iterations.
579+
size_t completed_iterations_;
574580

575581
std::vector<int> range_;
576582

@@ -581,6 +587,11 @@ class State {
581587

582588
bool error_occurred_;
583589

590+
// When using KeepRunningBatch(), batch_leftover_ holds the number of
591+
// iterations beyond max_iters that were run. Used to track
592+
// completed_iterations_ accurately.
593+
size_t batch_leftover_;
594+
584595
public:
585596
// Container for user-defined counters.
586597
UserCounters counters;
@@ -597,12 +608,43 @@ class State {
597608

598609
private:
599610
void StartKeepRunning();
600-
void FinishKeepRunning();
611+
void FinishKeepRunning(size_t n);
601612
internal::ThreadTimer* timer_;
602613
internal::ThreadManager* manager_;
603614
BENCHMARK_DISALLOW_COPY_AND_ASSIGN(State);
604615
};
605616

617+
BENCHMARK_ALWAYS_INLINE
618+
inline bool State::KeepRunningBatch(int n) {
619+
if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
620+
StartKeepRunning();
621+
}
622+
// leftover only has a sensible value when n > total_iterations.
623+
size_t leftover = n - total_iterations_;
624+
// total_iterations_ is a size_t. Avoid wrapping around past 0.
625+
bool const res = (total_iterations_ > 0);
626+
total_iterations_ -= std::min<size_t>(n, total_iterations_);
627+
if (BENCHMARK_BUILTIN_EXPECT(!res, false)) {
628+
FinishKeepRunning(batch_leftover_);
629+
}
630+
// If this batch has 0 < n < total_iterations iterations, (i.e. it's the last
631+
// batch), track how many extra iterations past max_iters we ran.
632+
batch_leftover_ = leftover;
633+
return res;
634+
}
635+
636+
BENCHMARK_ALWAYS_INLINE
637+
inline bool State::KeepRunning() {
638+
if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
639+
StartKeepRunning();
640+
}
641+
bool const res = (total_iterations_-- != 0);
642+
if (BENCHMARK_BUILTIN_EXPECT(!res, false)) {
643+
FinishKeepRunning(0);
644+
}
645+
return res;
646+
}
647+
606648
struct State::StateIterator {
607649
struct BENCHMARK_UNUSED Value {};
608650
typedef std::forward_iterator_tag iterator_category;
@@ -633,7 +675,7 @@ struct State::StateIterator {
633675
BENCHMARK_ALWAYS_INLINE
634676
bool operator!=(StateIterator const&) const {
635677
if (BENCHMARK_BUILTIN_EXPECT(cached_ != 0, true)) return true;
636-
parent_->FinishKeepRunning();
678+
parent_->FinishKeepRunning(0);
637679
return false;
638680
}
639681

src/benchmark.cc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ void RunInThread(const benchmark::internal::Benchmark::Instance* b,
268268
internal::ThreadTimer timer;
269269
State st(iters, b->arg, thread_id, b->threads, &timer, manager);
270270
b->benchmark->Run(st);
271-
CHECK(st.iterations() == st.max_iterations)
271+
CHECK(st.iterations() >= st.max_iterations)
272272
<< "Benchmark returned before State::KeepRunning() returned false!";
273273
{
274274
MutexLock l(manager->GetBenchmarkMutex());
@@ -399,12 +399,13 @@ State::State(size_t max_iters, const std::vector<int>& ranges, int thread_i,
399399
internal::ThreadManager* manager)
400400
: started_(false),
401401
finished_(false),
402-
total_iterations_(max_iters + 1),
402+
total_iterations_(max_iters),
403403
range_(ranges),
404404
bytes_processed_(0),
405405
items_processed_(0),
406406
complexity_n_(0),
407407
error_occurred_(false),
408+
batch_leftover_(0),
408409
counters(),
409410
thread_index(thread_i),
410411
threads(n_threads),
@@ -437,7 +438,7 @@ void State::SkipWithError(const char* msg) {
437438
manager_->results.has_error_ = true;
438439
}
439440
}
440-
total_iterations_ = 1;
441+
total_iterations_ = 0;
441442
if (timer_->running()) timer_->StopTimer();
442443
}
443444

@@ -453,17 +454,20 @@ void State::SetLabel(const char* label) {
453454
void State::StartKeepRunning() {
454455
CHECK(!started_ && !finished_);
455456
started_ = true;
457+
completed_iterations_ = max_iterations;
456458
manager_->StartStopBarrier();
457459
if (!error_occurred_) ResumeTiming();
458460
}
459461

460-
void State::FinishKeepRunning() {
462+
void State::FinishKeepRunning(size_t n) {
461463
CHECK(started_ && (!finished_ || error_occurred_));
462464
if (!error_occurred_) {
463465
PauseTiming();
464466
}
465-
// Total iterations has now wrapped around zero. Fix this.
466-
total_iterations_ = 1;
467+
// Batch size overshot total_iterations_. Account for it.
468+
completed_iterations_ += n;
469+
// Total iterations has now wrapped around past 0. Fix this.
470+
total_iterations_ = 0;
467471
finished_ = true;
468472
manager_->StartStopBarrier();
469473
}

test/basic_test.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,20 @@ void BM_KeepRunning(benchmark::State& state) {
102102
while (state.KeepRunning()) {
103103
++iter_count;
104104
}
105-
assert(iter_count == state.max_iterations);
105+
assert(iter_count == state.iterations());
106106
}
107107
BENCHMARK(BM_KeepRunning);
108108

109+
void BM_KeepRunningBatch(benchmark::State& state) {
110+
const size_t batch_size = 100;
111+
size_t iter_count = 0;
112+
while (state.KeepRunningBatch(batch_size)) {
113+
iter_count += batch_size;
114+
}
115+
assert(state.iterations() == iter_count);
116+
}
117+
BENCHMARK(BM_KeepRunningBatch);
118+
109119
void BM_RangedFor(benchmark::State& state) {
110120
size_t iter_count = 0;
111121
for (auto _ : state) {

0 commit comments

Comments
 (0)