Skip to content

Commit 3bca691

Browse files
committed
Further cleaning of initial commit. Add test.
1 parent 55ff633 commit 3bca691

File tree

9 files changed

+150
-26
lines changed

9 files changed

+150
-26
lines changed

include/benchmark/benchmark.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,12 @@ template <class Q> int BM_Sequential(benchmark::State& state) {
126126
}
127127
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
128128
129-
Use `Benchmark::MinTime(double t)` to set the minimum time used to run the
130-
benchmark. This option overrides the `benchmark_min_time` flag.
129+
Use `Benchmark::MinTime(double t)` to set the minimum time used to determine how long
130+
to run the benchmark. This option overrides the `benchmark_min_time` flag.
131+
132+
If a benchmark measures time manually, use `Benchmark::MinRelAccuracy(double r)` to set
133+
the required minimum relative accuracy used to determine how long to run the benchmark.
134+
This option overrides the `benchmark_min_rel_accuracy` flag.
131135
132136
void BM_test(benchmark::State& state) {
133137
... body ...
@@ -1190,11 +1194,19 @@ class BENCHMARK_EXPORT Benchmark {
11901194
// multiplier kRangeMultiplier will be used.
11911195
Benchmark* RangeMultiplier(int multiplier);
11921196

1193-
// Set the minimum amount of time to use when running this benchmark. This
1194-
// option overrides the `benchmark_min_time` flag.
1197+
// Set the minimum amount of time to use to determine the required number
1198+
// of iterations when running this benchmark. This option overrides
1199+
// the `benchmark_min_time` flag.
11951200
// REQUIRES: `t > 0` and `Iterations` has not been called on this benchmark.
11961201
Benchmark* MinTime(double t);
11971202

1203+
// Set the minimum relative accuracy to use to determine the required number
1204+
// of iterations when running this benchmark. This option overrides
1205+
// the `benchmark_min_rel_accuracy` flag.
1206+
// REQUIRES: `t > 0`, `Iterations` has not been called on this benchmark, and
1207+
// time is measured manually, i.e., `UseManualTime` has been called on this
1208+
// benchmark and each benchmark iteration should call SetIterationTime(seconds)
1209+
// to report the measured time.
11981210
Benchmark* MinRelAccuracy(double r);
11991211

12001212
// Set the minimum amount of time to run the benchmark before taking runtimes
@@ -1766,6 +1778,7 @@ class BENCHMARK_EXPORT BenchmarkReporter {
17661778
threads(1),
17671779
time_unit(GetDefaultTimeUnit()),
17681780
real_accumulated_time(0),
1781+
manual_accumulated_time_pow2(0),
17691782
cpu_accumulated_time(0),
17701783
max_heapbytes_used(0),
17711784
complexity(oNone),
@@ -1793,6 +1806,7 @@ class BENCHMARK_EXPORT BenchmarkReporter {
17931806
int64_t repetitions;
17941807
TimeUnit time_unit;
17951808
double real_accumulated_time;
1809+
double manual_accumulated_time_pow2;
17961810
double cpu_accumulated_time;
17971811

17981812
// Return a value representing the real time per iteration in the unit

src/benchmark.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ BM_DEFINE_bool(benchmark_list_tests, false);
6565
// linked into the binary are run.
6666
BM_DEFINE_string(benchmark_filter, "");
6767

68-
// Specification of how long to run the benchmark.
68+
// Specification of either an exact number of iterations (specified as `<integer>x`)
69+
// or a minimum number of seconds (specified as `<float>s`) used to determine how
70+
// long to run the benchmark.
6971
//
70-
// It can be either an exact number of iterations (specified as `<integer>x`),
71-
// or a minimum number of seconds (specified as `<float>s`). If the latter
72-
// format (ie., min seconds) is used, the system may run the benchmark longer
72+
// If the latter format (ie., min seconds) is used, the system may run the benchmark longer
7373
// until the results are considered significant.
7474
//
7575
// For backward compatibility, the `s` suffix may be omitted, in which case,
@@ -81,6 +81,18 @@ BM_DEFINE_string(benchmark_filter, "");
8181
// benchmark execution, regardless of number of threads.
8282
BM_DEFINE_string(benchmark_min_time, kDefaultMinTimeStr);
8383

84+
// Specification of required relative accuracy used to determine how
85+
// long to run the benchmark.
86+
//
87+
// REQUIRES: time is measured manually.
88+
//
89+
// Manual timers provide per-iteration times. The relative accuracy is
90+
// measured as the standard deviation of these per-iteration times divided by
91+
// the mean and the square root of the number of iterations. The benchmark is
92+
// run until the specified minimum time or number of iterations is reached
93+
// and the measured relative accuracy meets the specified requirement.
94+
BM_DEFINE_double(benchmark_min_rel_accuracy, 0.0);
95+
8496
// Minimum number of seconds a benchmark should be run before results should be
8597
// taken into account. This e.g can be necessary for benchmarks of code which
8698
// needs to fill some form of cache before performance is of interest.
@@ -694,6 +706,8 @@ void ParseCommandLineFlags(int* argc, char** argv) {
694706
ParseStringFlag(argv[i], "benchmark_filter", &FLAGS_benchmark_filter) ||
695707
ParseStringFlag(argv[i], "benchmark_min_time",
696708
&FLAGS_benchmark_min_time) ||
709+
ParseDoubleFlag(argv[i], "benchmark_min_rel_accuracy",
710+
&FLAGS_benchmark_min_rel_accuracy) ||
697711
ParseDoubleFlag(argv[i], "benchmark_min_warmup_time",
698712
&FLAGS_benchmark_min_warmup_time) ||
699713
ParseInt32Flag(argv[i], "benchmark_repetitions",
@@ -753,7 +767,8 @@ void PrintDefaultHelp() {
753767
"benchmark"
754768
" [--benchmark_list_tests={true|false}]\n"
755769
" [--benchmark_filter=<regex>]\n"
756-
" [--benchmark_min_time=`<integer>x` OR `<float>s` ]\n"
770+
" [--benchmark_min_time=`<integer>x` OR `<float>s`]\n"
771+
" [--benchmark_min_rel_accuracy=<min_rel_accuracy>]\n"
757772
" [--benchmark_min_warmup_time=<min_warmup_time>]\n"
758773
" [--benchmark_repetitions=<num_repetitions>]\n"
759774
" [--benchmark_enable_random_interleaving={true|false}]\n"

src/benchmark_register.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ Benchmark* Benchmark::MinTime(double t) {
360360
Benchmark* Benchmark::MinRelAccuracy(double r) {
361361
BM_CHECK(r > 0.0);
362362
BM_CHECK(iterations_ == 0);
363+
BM_CHECK(use_manual_time_);
363364
min_rel_accuracy_ = r;
364365
return this;
365366
}

src/benchmark_runner.cc

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ BenchmarkReporter::Run CreateRunReport(
9393
if (!report.skipped) {
9494
if (b.use_manual_time()) {
9595
report.real_accumulated_time = results.manual_time_used;
96+
report.manual_accumulated_time_pow2 = results.manual_time_used_pow2;
9697
} else {
9798
report.real_accumulated_time = results.real_time_used;
9899
}
@@ -139,7 +140,7 @@ void RunInThread(const BenchmarkInstance* b, IterationCount iters,
139140
results.cpu_time_used += timer.cpu_time_used();
140141
results.real_time_used += timer.real_time_used();
141142
results.manual_time_used += timer.manual_time_used();
142-
results.manual_time_used2 += timer.manual_time_used2();
143+
results.manual_time_used_pow2 += timer.manual_time_used_pow2();
143144
results.complexity_n += st.complexity_length_n();
144145
internal::Increment(&results.counters, st.counters);
145146
}
@@ -225,8 +226,10 @@ BenchmarkRunner::BenchmarkRunner(
225226
: b(b_),
226227
reports_for_family(reports_for_family_),
227228
parsed_benchtime_flag(ParseBenchMinTime(FLAGS_benchmark_min_time)),
228-
min_time(ComputeMinTime(b_, parsed_benchtime_flag)),
229-
min_rel_accuracy(b_min_rel_accuracy()),
229+
min_time(ComputeMinTime(b, parsed_benchtime_flag)),
230+
min_rel_accuracy(!IsZero(b.min_rel_accuracy())
231+
? b.min_rel_accuracy()
232+
: FLAGS_benchmark_min_rel_accuracy),
230233
min_warmup_time((!IsZero(b.min_time()) && b.min_warmup_time() > 0.0)
231234
? b.min_warmup_time()
232235
: FLAGS_benchmark_min_warmup_time),
@@ -303,10 +306,10 @@ BenchmarkRunner::IterationResults BenchmarkRunner::DoNIterations() {
303306

304307
// Base decisions off of real time if requested by this benchmark.
305308
i.seconds = i.results.cpu_time_used;
306-
i.seconds2 = 0;
309+
i.seconds_pow2 = 0;
307310
if (b.use_manual_time()) {
308311
i.seconds = i.results.manual_time_used;
309-
i.seconds2 = i.results.manual_time_used2;
312+
i.seconds_pow2 = i.results.manual_time_used_pow2;
310313
} else if (b.use_real_time()) {
311314
i.seconds = i.results.real_time_used;
312315
}
@@ -333,8 +336,8 @@ IterationCount BenchmarkRunner::PredictNumItersNeeded(
333336

334337
// So what seems to be the sufficiently-large iteration count? Round up.
335338
const IterationCount max_next_iters = static_cast<IterationCount>(
336-
std::lround(std::max(multiplier * static_cast<double>(i.iters),
337-
static_cast<double>(i.iters) + 1.0)));
339+
std::llround(std::max(multiplier * static_cast<double>(i.iters),
340+
static_cast<double>(i.iters) + 1.0)));
338341
// But we do have *some* limits though..
339342
const IterationCount next_iters = std::min(max_next_iters, kMaxIterations);
340343

@@ -364,8 +367,8 @@ double BenchmarkRunner::GetMinTimeToApply() const {
364367
return warmup_done ? min_time : min_warmup_time;
365368
}
366369

367-
double GetRelAccuracy(const IterationResults& i) const {
368-
return std::sqrt(i.seconds2 / i.iters - std::pow(i.seconds / i.iters, 2.)) / (i.seconds / i.iters) / sqrt(i.iters);
370+
double BenchmarkRunner::GetRelAccuracy(const IterationResults& i) const {
371+
return std::sqrt(i.seconds_pow2 / i.iters - std::pow(i.seconds / i.iters, 2.)) / (i.seconds / i.iters) / sqrt(i.iters);
369372
}
370373

371374
bool BenchmarkRunner::HasSufficientTimeToApply(const IterationResults& i) const {
@@ -377,7 +380,7 @@ bool BenchmarkRunner::HasSufficientTimeToApply(const IterationResults& i) const
377380
}
378381

379382
bool BenchmarkRunner::HasSufficientRelAccuracy(const IterationResults& i) const {
380-
return (!IsZero(GetMinRelAccuracy()) && (GetRelAccuracy(i) <= GetMinRelAccuracy()));
383+
return (IsZero(GetMinRelAccuracy()) || (GetRelAccuracy(i) <= GetMinRelAccuracy()));
381384
}
382385

383386
void BenchmarkRunner::FinishWarmUp(const IterationCount& i) {

src/benchmark_runner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
namespace benchmark {
2727

2828
BM_DECLARE_string(benchmark_min_time);
29+
BM_DECLARE_double(benchmark_min_rel_accuracy);
2930
BM_DECLARE_double(benchmark_min_warmup_time);
3031
BM_DECLARE_int32(benchmark_repetitions);
3132
BM_DECLARE_bool(benchmark_report_aggregates_only);
@@ -113,7 +114,7 @@ class BenchmarkRunner {
113114
internal::ThreadManager::Result results;
114115
IterationCount iters;
115116
double seconds;
116-
double seconds2;
117+
double seconds_pow2;
117118
};
118119
IterationResults DoNIterations();
119120

src/thread_manager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ThreadManager {
4141
double real_time_used = 0;
4242
double cpu_time_used = 0;
4343
double manual_time_used = 0;
44-
double manual_time_used2 = 0;
44+
double manual_time_used_pow2 = 0;
4545
int64_t complexity_n = 0;
4646
std::string report_label_;
4747
std::string skip_message_;

src/thread_timer.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ThreadTimer {
4040
// Called by each thread
4141
void SetIterationTime(double seconds) {
4242
manual_time_used_ += seconds;
43-
manual_time_used2_ += std::pow(seconds, 2.);
43+
manual_time_used_pow2_ += std::pow(seconds, 2.);
4444
}
4545

4646
bool running() const { return running_; }
@@ -63,9 +63,9 @@ class ThreadTimer {
6363
return manual_time_used_;
6464
}
6565

66-
double manual_time_used2() const {
66+
double manual_time_used_pow2() const {
6767
BM_CHECK(!running_);
68-
return manual_time_used2_;
68+
return manual_time_used_pow2_;
6969
}
7070

7171
private:
@@ -83,11 +83,10 @@ class ThreadTimer {
8383

8484
// Accumulated time so far (does not contain current slice if running_)
8585
double real_time_used_ = 0;
86-
8786
double cpu_time_used_ = 0;
8887
// Manually set iteration time. User sets this with SetIterationTime(seconds).
8988
double manual_time_used_ = 0;
90-
double manual_time_used2_ = 0;
89+
double manual_time_used_pow2_ = 0;
9190
};
9291

9392
} // namespace internal

test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ benchmark_add_test(NAME min_time_flag_time COMMAND benchmark_min_time_flag_time_
9797
compile_benchmark_test(benchmark_min_time_flag_iters_test)
9898
benchmark_add_test(NAME min_time_flag_iters COMMAND benchmark_min_time_flag_iters_test)
9999

100+
compile_benchmark_test(benchmark_min_rel_accuracy_flag_test)
101+
benchmark_add_test(NAME min_rel_accuracy_flag_test COMMAND benchmark_min_rel_accuracy_flag_test)
102+
100103
add_filter_test(filter_simple "Foo" 3)
101104
add_filter_test(filter_simple_negative "-Foo" 2)
102105
add_filter_test(filter_suffix "BM_.*" 4)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#include <cassert>
2+
#include <cstdlib>
3+
#include <cstring>
4+
#include <iostream>
5+
#include <random>
6+
#include <string>
7+
#include <vector>
8+
9+
#include "benchmark/benchmark.h"
10+
11+
// Tests that if a benchmark measures time manually, we can specify the required relative accuracy with
12+
// --benchmark_min_rel_accuracy=<min_rel_accuracy>.
13+
namespace {
14+
15+
class TestReporter : public benchmark::ConsoleReporter {
16+
public:
17+
virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE {
18+
return ConsoleReporter::ReportContext(context);
19+
};
20+
21+
virtual void ReportRuns(const std::vector<Run>& report) BENCHMARK_OVERRIDE {
22+
assert(report.size() == 1);
23+
iters_.push_back(report[0].iterations);
24+
real_accumulated_times_.push_back(report[0].real_accumulated_time);
25+
manual_accumulated_time_pow2s_.push_back(report[0].manual_accumulated_time_pow2);
26+
ConsoleReporter::ReportRuns(report);
27+
};
28+
29+
TestReporter() {}
30+
31+
virtual ~TestReporter() {}
32+
33+
const std::vector<benchmark::IterationCount>& GetIters() const {
34+
return iters_;
35+
}
36+
37+
const std::vector<double>& GetRealAccumulatedTimes() const {
38+
return real_accumulated_times_;
39+
}
40+
41+
const std::vector<double>& GetManualAccumulatedTimePow2s() const {
42+
return manual_accumulated_time_pow2s_;
43+
}
44+
45+
private:
46+
std::vector<benchmark::IterationCount> iters_;
47+
std::vector<double> real_accumulated_times_;
48+
std::vector<double> manual_accumulated_time_pow2s_;
49+
};
50+
51+
} // end namespace
52+
53+
static void BM_MyBench(benchmark::State& state) {
54+
static std::mt19937 rd{std::random_device{}()};
55+
static std::uniform_real_distribution<double> mrand(0, 1);
56+
57+
for (auto s : state) {
58+
state.SetIterationTime(mrand(rd));
59+
}
60+
}
61+
BENCHMARK(BM_MyBench)->UseManualTime();
62+
63+
int main(int argc, char** argv) {
64+
// Make a fake argv and append the new --benchmark_min_rel_accuracy=<min_rel_accuracy> to it.
65+
int fake_argc = argc + 2;
66+
const char** fake_argv = new const char*[static_cast<size_t>(fake_argc)];
67+
for (int i = 0; i < argc; ++i) fake_argv[i] = argv[i];
68+
fake_argv[argc] = "--benchmark_min_time=10s";
69+
fake_argv[argc + 1] = "--benchmark_min_rel_accuracy=0.01";
70+
71+
benchmark::Initialize(&fake_argc, const_cast<char**>(fake_argv));
72+
73+
TestReporter test_reporter;
74+
const size_t returned_count =
75+
benchmark::RunSpecifiedBenchmarks(&test_reporter, "BM_MyBench");
76+
assert(returned_count == 1);
77+
78+
// Check the executed iters.
79+
const benchmark::IterationCount iters = test_reporter.GetIters()[0];
80+
const double real_accumulated_time = test_reporter.GetRealAccumulatedTimes()[0];
81+
const double manual_accumulated_time_pow2 = test_reporter.GetManualAccumulatedTimePow2s()[0];
82+
83+
const double rel_accuracy = std::sqrt(manual_accumulated_time_pow2 / iters - std::pow(real_accumulated_time / iters, 2.)) / (real_accumulated_time / iters) / sqrt(iters);
84+
assert(rel_accuracy <= 0.01);
85+
86+
delete[] fake_argv;
87+
return 0;
88+
}

0 commit comments

Comments
 (0)