Update in-tree Google Benchmark to current ToT.
I've put some work into the Google Benchmark library in order to make it easier to benchmark libc++. These changes have already been upstreamed into Google Benchmark and this patch applies the changes to the in-tree version. The main improvement in the addition of a 'compare_bench.py' script which makes it very easy to compare benchmarks. For example to compare the native STL to libc++ you would run: `$ compare_bench.py ./util_smartptr.native.out ./util_smartptr.libcxx.out` And the output would look like: RUNNING: ./util_smartptr.native.out Benchmark Time CPU Iterations ---------------------------------------------------------------- BM_SharedPtrCreateDestroy 62 ns 62 ns 10937500 BM_SharedPtrIncDecRef 31 ns 31 ns 23972603 BM_WeakPtrIncDecRef 28 ns 28 ns 23648649 RUNNING: ./util_smartptr.libcxx.out Benchmark Time CPU Iterations ---------------------------------------------------------------- BM_SharedPtrCreateDestroy 46 ns 46 ns 14957265 BM_SharedPtrIncDecRef 31 ns 31 ns 22435897 BM_WeakPtrIncDecRef 34 ns 34 ns 21084337 Comparing ./util_smartptr.native.out to ./util_smartptr.libcxx.out Benchmark Time CPU ----------------------------------------------------- BM_SharedPtrCreateDestroy -0.26 -0.26 BM_SharedPtrIncDecRef +0.00 +0.00 BM_WeakPtrIncDecRef +0.21 +0.21 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@278147 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -87,7 +87,12 @@ macro(add_benchmark_test name source_file)
|
|||||||
add_executable(${libcxx_target} EXCLUDE_FROM_ALL ${source_file})
|
add_executable(${libcxx_target} EXCLUDE_FROM_ALL ${source_file})
|
||||||
add_dependencies(${libcxx_target} cxx google-benchmark-libcxx)
|
add_dependencies(${libcxx_target} cxx google-benchmark-libcxx)
|
||||||
add_dependencies(libcxx-benchmarks ${libcxx_target})
|
add_dependencies(libcxx-benchmarks ${libcxx_target})
|
||||||
target_link_libraries(${libcxx_target} cxx -lbenchmark)
|
if (LIBCXX_ENABLE_SHARED)
|
||||||
|
target_link_libraries(${libcxx_target} cxx_shared)
|
||||||
|
else()
|
||||||
|
target_link_libraries(${libcxx_target} cxx_static)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(${libcxx_target} -lbenchmark)
|
||||||
set_target_properties(${libcxx_target}
|
set_target_properties(${libcxx_target}
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "${name}.libcxx.out"
|
OUTPUT_NAME "${name}.libcxx.out"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace ContainerBenchmarks {
|
|||||||
|
|
||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
void BM_ConstructIterIter(benchmark::State& st, Container, GenInputs gen) {
|
void BM_ConstructIterIter(benchmark::State& st, Container, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
const auto end = in.end();
|
const auto end = in.end();
|
||||||
benchmark::DoNotOptimize(&in);
|
benchmark::DoNotOptimize(&in);
|
||||||
while (st.KeepRunning()) {
|
while (st.KeepRunning()) {
|
||||||
@@ -21,7 +21,7 @@ void BM_ConstructIterIter(benchmark::State& st, Container, GenInputs gen) {
|
|||||||
|
|
||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
void BM_InsertValue(benchmark::State& st, Container c, GenInputs gen) {
|
void BM_InsertValue(benchmark::State& st, Container c, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
const auto end = in.end();
|
const auto end = in.end();
|
||||||
while (st.KeepRunning()) {
|
while (st.KeepRunning()) {
|
||||||
c.clear();
|
c.clear();
|
||||||
@@ -34,7 +34,7 @@ void BM_InsertValue(benchmark::State& st, Container c, GenInputs gen) {
|
|||||||
|
|
||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) {
|
void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
const auto end = in.end();
|
const auto end = in.end();
|
||||||
while (st.KeepRunning()) {
|
while (st.KeepRunning()) {
|
||||||
c.clear();
|
c.clear();
|
||||||
@@ -49,7 +49,7 @@ void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) {
|
|||||||
|
|
||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) {
|
void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
const auto end = in.end();
|
const auto end = in.end();
|
||||||
c.insert(in.begin(), in.end());
|
c.insert(in.begin(), in.end());
|
||||||
benchmark::DoNotOptimize(&c);
|
benchmark::DoNotOptimize(&c);
|
||||||
@@ -65,7 +65,7 @@ void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) {
|
|||||||
|
|
||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
void BM_EmplaceDuplicate(benchmark::State& st, Container c, GenInputs gen) {
|
void BM_EmplaceDuplicate(benchmark::State& st, Container c, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
const auto end = in.end();
|
const auto end = in.end();
|
||||||
c.insert(in.begin(), in.end());
|
c.insert(in.begin(), in.end());
|
||||||
benchmark::DoNotOptimize(&c);
|
benchmark::DoNotOptimize(&c);
|
||||||
@@ -80,7 +80,7 @@ void BM_EmplaceDuplicate(benchmark::State& st, Container c, GenInputs gen) {
|
|||||||
|
|
||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
static void BM_Find(benchmark::State& st, Container c, GenInputs gen) {
|
static void BM_Find(benchmark::State& st, Container c, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
c.insert(in.begin(), in.end());
|
c.insert(in.begin(), in.end());
|
||||||
benchmark::DoNotOptimize(&(*c.begin()));
|
benchmark::DoNotOptimize(&(*c.begin()));
|
||||||
const auto end = in.data() + in.size();
|
const auto end = in.data() + in.size();
|
||||||
@@ -95,7 +95,7 @@ static void BM_Find(benchmark::State& st, Container c, GenInputs gen) {
|
|||||||
template <class Container, class GenInputs>
|
template <class Container, class GenInputs>
|
||||||
static void BM_FindRehash(benchmark::State& st, Container c, GenInputs gen) {
|
static void BM_FindRehash(benchmark::State& st, Container c, GenInputs gen) {
|
||||||
c.rehash(8);
|
c.rehash(8);
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
c.insert(in.begin(), in.end());
|
c.insert(in.begin(), in.end());
|
||||||
benchmark::DoNotOptimize(&(*c.begin()));
|
benchmark::DoNotOptimize(&(*c.begin()));
|
||||||
const auto end = in.data() + in.size();
|
const auto end = in.data() + in.size();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ constexpr std::size_t TestNumInputs = 1024;
|
|||||||
template <class GenInputs>
|
template <class GenInputs>
|
||||||
void BM_Sort(benchmark::State& st, GenInputs gen) {
|
void BM_Sort(benchmark::State& st, GenInputs gen) {
|
||||||
using ValueType = typename decltype(gen(0))::value_type;
|
using ValueType = typename decltype(gen(0))::value_type;
|
||||||
const auto in = gen(st.range_x());
|
const auto in = gen(st.range(0));
|
||||||
std::vector<ValueType> inputs[5];
|
std::vector<ValueType> inputs[5];
|
||||||
auto reset_inputs = [&]() {
|
auto reset_inputs = [&]() {
|
||||||
for (auto& C : inputs) {
|
for (auto& C : inputs) {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ struct UInt64Hash2 {
|
|||||||
|
|
||||||
template <class HashFn, class GenInputs>
|
template <class HashFn, class GenInputs>
|
||||||
void BM_Hash(benchmark::State& st, HashFn fn, GenInputs gen) {
|
void BM_Hash(benchmark::State& st, HashFn fn, GenInputs gen) {
|
||||||
auto in = gen(st.range_x());
|
auto in = gen(st.range(0));
|
||||||
const auto end = in.data() + in.size();
|
const auto end = in.data() + in.size();
|
||||||
std::size_t last_hash = 0;
|
std::size_t last_hash = 0;
|
||||||
benchmark::DoNotOptimize(&last_hash);
|
benchmark::DoNotOptimize(&last_hash);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Arne Beer <arne@twobeer.de>
|
|||||||
Christopher Seymour <chris.j.seymour@hotmail.com>
|
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||||
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||||
Dominic Hamon <dma@stripysock.com>
|
Dominic Hamon <dma@stripysock.com>
|
||||||
|
Eric Fiselier <eric@efcs.ca>
|
||||||
Eugene Zhuk <eugene.zhuk@gmail.com>
|
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||||
Evgeny Safronov <division494@gmail.com>
|
Evgeny Safronov <division494@gmail.com>
|
||||||
Felix Homann <linuxaudio@showlabor.de>
|
Felix Homann <linuxaudio@showlabor.de>
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ measuring the speed of `memcpy()` calls of different lengths:
|
|||||||
|
|
||||||
```c++
|
```c++
|
||||||
static void BM_memcpy(benchmark::State& state) {
|
static void BM_memcpy(benchmark::State& state) {
|
||||||
char* src = new char[state.range_x()];
|
char* src = new char[state.range(0)];
|
||||||
char* dst = new char[state.range_x()];
|
char* dst = new char[state.range(0)];
|
||||||
memset(src, 'x', state.range_x());
|
memset(src, 'x', state.range(0));
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
memcpy(dst, src, state.range_x());
|
memcpy(dst, src, state.range(0));
|
||||||
state.SetBytesProcessed(int64_t(state.iterations()) *
|
state.SetBytesProcessed(int64_t(state.iterations()) *
|
||||||
int64_t(state.range_x()));
|
int64_t(state.range(0)));
|
||||||
delete[] src;
|
delete[] src;
|
||||||
delete[] dst;
|
delete[] dst;
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ BENCHMARK(BM_memcpy)->RangeMultiplier(2)->Range(8, 8<<10);
|
|||||||
```
|
```
|
||||||
Now arguments generated are [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ].
|
Now arguments generated are [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ].
|
||||||
|
|
||||||
You might have a benchmark that depends on two inputs. For example, the
|
You might have a benchmark that depends on two or more inputs. For example, the
|
||||||
following code defines a family of benchmarks for measuring the speed of set
|
following code defines a family of benchmarks for measuring the speed of set
|
||||||
insertion.
|
insertion.
|
||||||
|
|
||||||
@@ -78,21 +78,21 @@ insertion.
|
|||||||
static void BM_SetInsert(benchmark::State& state) {
|
static void BM_SetInsert(benchmark::State& state) {
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
std::set<int> data = ConstructRandomSet(state.range_x());
|
std::set<int> data = ConstructRandomSet(state.range(0));
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
for (int j = 0; j < state.range_y(); ++j)
|
for (int j = 0; j < state.range(1); ++j)
|
||||||
data.insert(RandomNumber());
|
data.insert(RandomNumber());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_SetInsert)
|
BENCHMARK(BM_SetInsert)
|
||||||
->ArgPair(1<<10, 1)
|
->Args({1<<10, 1})
|
||||||
->ArgPair(1<<10, 8)
|
->Args({1<<10, 8})
|
||||||
->ArgPair(1<<10, 64)
|
->Args({1<<10, 64})
|
||||||
->ArgPair(1<<10, 512)
|
->Args({1<<10, 512})
|
||||||
->ArgPair(8<<10, 1)
|
->Args({8<<10, 1})
|
||||||
->ArgPair(8<<10, 8)
|
->Args({8<<10, 8})
|
||||||
->ArgPair(8<<10, 64)
|
->Args({8<<10, 64})
|
||||||
->ArgPair(8<<10, 512);
|
->Args({8<<10, 512});
|
||||||
```
|
```
|
||||||
|
|
||||||
The preceding code is quite repetitive, and can be replaced with the following
|
The preceding code is quite repetitive, and can be replaced with the following
|
||||||
@@ -101,7 +101,7 @@ product of the two specified ranges and will generate a benchmark for each such
|
|||||||
pair.
|
pair.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
BENCHMARK(BM_SetInsert)->RangePair(1<<10, 8<<10, 1, 512);
|
BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {1, 512}});
|
||||||
```
|
```
|
||||||
|
|
||||||
For more complex patterns of inputs, passing a custom function to `Apply` allows
|
For more complex patterns of inputs, passing a custom function to `Apply` allows
|
||||||
@@ -113,7 +113,7 @@ and a sparse range on the second.
|
|||||||
static void CustomArguments(benchmark::internal::Benchmark* b) {
|
static void CustomArguments(benchmark::internal::Benchmark* b) {
|
||||||
for (int i = 0; i <= 10; ++i)
|
for (int i = 0; i <= 10; ++i)
|
||||||
for (int j = 32; j <= 1024*1024; j *= 8)
|
for (int j = 32; j <= 1024*1024; j *= 8)
|
||||||
b->ArgPair(i, j);
|
b->Args({i, j});
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
|
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
|
||||||
```
|
```
|
||||||
@@ -125,12 +125,12 @@ running time and the normalized root-mean square error of string comparison.
|
|||||||
|
|
||||||
```c++
|
```c++
|
||||||
static void BM_StringCompare(benchmark::State& state) {
|
static void BM_StringCompare(benchmark::State& state) {
|
||||||
std::string s1(state.range_x(), '-');
|
std::string s1(state.range(0), '-');
|
||||||
std::string s2(state.range_x(), '-');
|
std::string s2(state.range(0), '-');
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
benchmark::DoNotOptimize(s1.compare(s2));
|
benchmark::DoNotOptimize(s1.compare(s2));
|
||||||
}
|
}
|
||||||
state.SetComplexityN(state.range_x());
|
state.SetComplexityN(state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_StringCompare)
|
BENCHMARK(BM_StringCompare)
|
||||||
->RangeMultiplier(2)->Range(1<<10, 1<<18)->Complexity(benchmark::oN);
|
->RangeMultiplier(2)->Range(1<<10, 1<<18)->Complexity(benchmark::oN);
|
||||||
@@ -162,14 +162,14 @@ template <class Q> int BM_Sequential(benchmark::State& state) {
|
|||||||
Q q;
|
Q q;
|
||||||
typename Q::value_type v;
|
typename Q::value_type v;
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
for (int i = state.range_x(); i--; )
|
for (int i = state.range(0); i--; )
|
||||||
q.push(v);
|
q.push(v);
|
||||||
for (int e = state.range_x(); e--; )
|
for (int e = state.range(0); e--; )
|
||||||
q.Wait(&v);
|
q.Wait(&v);
|
||||||
}
|
}
|
||||||
// actually messages, not bytes:
|
// actually messages, not bytes:
|
||||||
state.SetBytesProcessed(
|
state.SetBytesProcessed(
|
||||||
static_cast<int64_t>(state.iterations())*state.range_x());
|
static_cast<int64_t>(state.iterations())*state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
|
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
|
||||||
```
|
```
|
||||||
@@ -206,6 +206,34 @@ BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc"));
|
|||||||
Note that elements of `...args` may refer to global variables. Users should
|
Note that elements of `...args` may refer to global variables. Users should
|
||||||
avoid modifying global state inside of a benchmark.
|
avoid modifying global state inside of a benchmark.
|
||||||
|
|
||||||
|
## Using RegisterBenchmark(name, fn, args...)
|
||||||
|
|
||||||
|
The `RegisterBenchmark(name, func, args...)` function provides an alternative
|
||||||
|
way to create and register benchmarks.
|
||||||
|
`RegisterBenchmark(name, func, args...)` creates, registers, and returns a
|
||||||
|
pointer to a new benchmark with the specified `name` that invokes
|
||||||
|
`func(st, args...)` where `st` is a `benchmark::State` object.
|
||||||
|
|
||||||
|
Unlike the `BENCHMARK` registration macros, which can only be used at the global
|
||||||
|
scope, the `RegisterBenchmark` can be called anywhere. This allows for
|
||||||
|
benchmark tests to be registered programmatically.
|
||||||
|
|
||||||
|
Additionally `RegisterBenchmark` allows any callable object to be registered
|
||||||
|
as a benchmark. Including capturing lambdas and function objects. This
|
||||||
|
allows the creation
|
||||||
|
|
||||||
|
For Example:
|
||||||
|
```c++
|
||||||
|
auto BM_test = [](benchmark::State& st, auto Inputs) { /* ... */ };
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
for (auto& test_input : { /* ... */ })
|
||||||
|
benchmark::RegisterBenchmark(test_input.name(), BM_test, test_input);
|
||||||
|
benchmark::Initialize(&argc, argv);
|
||||||
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Multithreaded benchmarks
|
### Multithreaded benchmarks
|
||||||
In a multithreaded test (benchmark invoked by multiple threads simultaneously),
|
In a multithreaded test (benchmark invoked by multiple threads simultaneously),
|
||||||
it is guaranteed that none of the threads will start until all have called
|
it is guaranteed that none of the threads will start until all have called
|
||||||
@@ -256,7 +284,7 @@ can be reported back with `SetIterationTime`.
|
|||||||
|
|
||||||
```c++
|
```c++
|
||||||
static void BM_ManualTiming(benchmark::State& state) {
|
static void BM_ManualTiming(benchmark::State& state) {
|
||||||
int microseconds = state.range_x();
|
int microseconds = state.range(0);
|
||||||
std::chrono::duration<double, std::micro> sleep_duration {
|
std::chrono::duration<double, std::micro> sleep_duration {
|
||||||
static_cast<double>(microseconds)
|
static_cast<double>(microseconds)
|
||||||
};
|
};
|
||||||
@@ -427,10 +455,10 @@ static void BM_test(benchmark::State& state) {
|
|||||||
|
|
||||||
## Output Formats
|
## Output Formats
|
||||||
The library supports multiple output formats. Use the
|
The library supports multiple output formats. Use the
|
||||||
`--benchmark_format=<tabular|json|csv>` flag to set the format type. `tabular` is
|
`--benchmark_format=<console|json|csv>` flag to set the format type. `console`
|
||||||
the default format.
|
is the default format.
|
||||||
|
|
||||||
The Tabular format is intended to be a human readable format. By default
|
The Console format is intended to be a human readable format. By default
|
||||||
the format generates color output. Context is output on stderr and the
|
the format generates color output. Context is output on stderr and the
|
||||||
tabular data on stdout. Example tabular output looks like:
|
tabular data on stdout. Example tabular output looks like:
|
||||||
```
|
```
|
||||||
@@ -493,6 +521,12 @@ name,iterations,real_time,cpu_time,bytes_per_second,items_per_second,label
|
|||||||
"BM_SetInsert/1024/10",106365,17238.4,8421.53,4.74973e+06,1.18743e+06,
|
"BM_SetInsert/1024/10",106365,17238.4,8421.53,4.74973e+06,1.18743e+06,
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Output Files
|
||||||
|
The library supports writing the output of the benchmark to a file specified
|
||||||
|
by `--benchmark_out=<filename>`. The format of the output can be specified
|
||||||
|
using `--benchmark_out_format={json|console|csv}`. Specifying
|
||||||
|
`--benchmark_out` does not suppress the console output.
|
||||||
|
|
||||||
## Debug vs Release
|
## Debug vs Release
|
||||||
By default, benchmark builds as a debug library. You will see a warning in the output when this is the case. To build it as a release library instead, use:
|
By default, benchmark builds as a debug library. You will see a warning in the output when this is the case. To build it as a release library instead, use:
|
||||||
|
|
||||||
@@ -507,4 +541,22 @@ cmake -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_LTO=true
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Linking against the library
|
## Linking against the library
|
||||||
When using gcc, it is necessary to link against pthread to avoid runtime exceptions. This is due to how gcc implements std::thread. See [issue #67](https://github.com/google/benchmark/issues/67) for more details.
|
When using gcc, it is necessary to link against pthread to avoid runtime exceptions.
|
||||||
|
This is due to how gcc implements std::thread.
|
||||||
|
See [issue #67](https://github.com/google/benchmark/issues/67) for more details.
|
||||||
|
|
||||||
|
## Compiler Support
|
||||||
|
|
||||||
|
Google Benchmark uses C++11 when building the library. As such we require
|
||||||
|
a modern C++ toolchain, both compiler and standard library.
|
||||||
|
|
||||||
|
The following minimum versions are strongly recommended build the library:
|
||||||
|
|
||||||
|
* GCC 4.8
|
||||||
|
* Clang 3.4
|
||||||
|
* Visual Studio 2013
|
||||||
|
|
||||||
|
Anything older *may* work.
|
||||||
|
|
||||||
|
Note: Using the library and its headers in C++03 is supported. C++11 is only
|
||||||
|
required to build the library.
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ int main(int argc, char** argv) {
|
|||||||
// of memcpy() calls of different lengths:
|
// of memcpy() calls of different lengths:
|
||||||
|
|
||||||
static void BM_memcpy(benchmark::State& state) {
|
static void BM_memcpy(benchmark::State& state) {
|
||||||
char* src = new char[state.range_x()]; char* dst = new char[state.range_x()];
|
char* src = new char[state.range(0)]; char* dst = new char[state.range(0)];
|
||||||
memset(src, 'x', state.range_x());
|
memset(src, 'x', state.range(0));
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
memcpy(dst, src, state.range_x());
|
memcpy(dst, src, state.range(0));
|
||||||
state.SetBytesProcessed(int64_t(state.iterations()) *
|
state.SetBytesProcessed(int64_t(state.iterations()) *
|
||||||
int64_t(state.range_x()));
|
int64_t(state.range(0)));
|
||||||
delete[] src; delete[] dst;
|
delete[] src; delete[] dst;
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1<<10)->Arg(8<<10);
|
BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1<<10)->Arg(8<<10);
|
||||||
@@ -60,27 +60,27 @@ BENCHMARK(BM_memcpy)->Range(8, 8<<10);
|
|||||||
static void BM_SetInsert(benchmark::State& state) {
|
static void BM_SetInsert(benchmark::State& state) {
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
set<int> data = ConstructRandomSet(state.range_x());
|
set<int> data = ConstructRandomSet(state.range(0));
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
for (int j = 0; j < state.range_y(); ++j)
|
for (int j = 0; j < state.range(1); ++j)
|
||||||
data.insert(RandomNumber());
|
data.insert(RandomNumber());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_SetInsert)
|
BENCHMARK(BM_SetInsert)
|
||||||
->ArgPair(1<<10, 1)
|
->Args({1<<10, 1})
|
||||||
->ArgPair(1<<10, 8)
|
->Args({1<<10, 8})
|
||||||
->ArgPair(1<<10, 64)
|
->Args({1<<10, 64})
|
||||||
->ArgPair(1<<10, 512)
|
->Args({1<<10, 512})
|
||||||
->ArgPair(8<<10, 1)
|
->Args({8<<10, 1})
|
||||||
->ArgPair(8<<10, 8)
|
->Args({8<<10, 8})
|
||||||
->ArgPair(8<<10, 64)
|
->Args({8<<10, 64})
|
||||||
->ArgPair(8<<10, 512);
|
->Args({8<<10, 512});
|
||||||
|
|
||||||
// The preceding code is quite repetitive, and can be replaced with
|
// The preceding code is quite repetitive, and can be replaced with
|
||||||
// the following short-hand. The following macro will pick a few
|
// the following short-hand. The following macro will pick a few
|
||||||
// appropriate arguments in the product of the two specified ranges
|
// appropriate arguments in the product of the two specified ranges
|
||||||
// and will generate a microbenchmark for each such pair.
|
// and will generate a microbenchmark for each such pair.
|
||||||
BENCHMARK(BM_SetInsert)->RangePair(1<<10, 8<<10, 1, 512);
|
BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {1, 512}});
|
||||||
|
|
||||||
// For more complex patterns of inputs, passing a custom function
|
// For more complex patterns of inputs, passing a custom function
|
||||||
// to Apply allows programmatic specification of an
|
// to Apply allows programmatic specification of an
|
||||||
@@ -90,7 +90,7 @@ BENCHMARK(BM_SetInsert)->RangePair(1<<10, 8<<10, 1, 512);
|
|||||||
static void CustomArguments(benchmark::internal::Benchmark* b) {
|
static void CustomArguments(benchmark::internal::Benchmark* b) {
|
||||||
for (int i = 0; i <= 10; ++i)
|
for (int i = 0; i <= 10; ++i)
|
||||||
for (int j = 32; j <= 1024*1024; j *= 8)
|
for (int j = 32; j <= 1024*1024; j *= 8)
|
||||||
b->ArgPair(i, j);
|
b->Args({i, j});
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
|
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
|
||||||
|
|
||||||
@@ -101,14 +101,14 @@ template <class Q> int BM_Sequential(benchmark::State& state) {
|
|||||||
Q q;
|
Q q;
|
||||||
typename Q::value_type v;
|
typename Q::value_type v;
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
for (int i = state.range_x(); i--; )
|
for (int i = state.range(0); i--; )
|
||||||
q.push(v);
|
q.push(v);
|
||||||
for (int e = state.range_x(); e--; )
|
for (int e = state.range(0); e--; )
|
||||||
q.Wait(&v);
|
q.Wait(&v);
|
||||||
}
|
}
|
||||||
// actually messages, not bytes:
|
// actually messages, not bytes:
|
||||||
state.SetBytesProcessed(
|
state.SetBytesProcessed(
|
||||||
static_cast<int64_t>(state.iterations())*state.range_x());
|
static_cast<int64_t>(state.iterations())*state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
|
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
|
||||||
|
|
||||||
@@ -153,8 +153,15 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
|
||||||
|
#if defined(BENCHMARK_HAS_CXX11)
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace benchmark {
|
namespace benchmark {
|
||||||
class BenchmarkReporter;
|
class BenchmarkReporter;
|
||||||
|
|
||||||
@@ -165,11 +172,16 @@ void Initialize(int* argc, char** argv);
|
|||||||
// of each matching benchmark. Otherwise run each matching benchmark and
|
// of each matching benchmark. Otherwise run each matching benchmark and
|
||||||
// report the results.
|
// report the results.
|
||||||
//
|
//
|
||||||
// The second overload reports the results using the specified 'reporter'.
|
// The second and third overload use the specified 'console_reporter' and
|
||||||
|
// 'file_reporter' respectively. 'file_reporter' will write to the file specified
|
||||||
|
// by '--benchmark_output'. If '--benchmark_output' is not given the
|
||||||
|
// 'file_reporter' is ignored.
|
||||||
//
|
//
|
||||||
// RETURNS: The number of matching benchmarks.
|
// RETURNS: The number of matching benchmarks.
|
||||||
size_t RunSpecifiedBenchmarks();
|
size_t RunSpecifiedBenchmarks();
|
||||||
size_t RunSpecifiedBenchmarks(BenchmarkReporter* reporter);
|
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
|
// If this routine is called, peak memory allocation past this point in the
|
||||||
@@ -258,7 +270,7 @@ typedef double(BigOFunc)(int);
|
|||||||
// benchmark to use.
|
// benchmark to use.
|
||||||
class State {
|
class State {
|
||||||
public:
|
public:
|
||||||
State(size_t max_iters, bool has_x, int x, bool has_y, int y,
|
State(size_t max_iters, const std::vector<int>& ranges,
|
||||||
int thread_i, int n_threads);
|
int thread_i, int n_threads);
|
||||||
|
|
||||||
// Returns true if the benchmark should continue through another iteration.
|
// Returns true if the benchmark should continue through another iteration.
|
||||||
@@ -367,7 +379,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK_ALWAYS_INLINE
|
BENCHMARK_ALWAYS_INLINE
|
||||||
size_t complexity_length_n() {
|
int complexity_length_n() {
|
||||||
return complexity_n_;
|
return complexity_n_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,17 +425,9 @@ public:
|
|||||||
|
|
||||||
// Range arguments for this run. CHECKs if the argument has been set.
|
// Range arguments for this run. CHECKs if the argument has been set.
|
||||||
BENCHMARK_ALWAYS_INLINE
|
BENCHMARK_ALWAYS_INLINE
|
||||||
int range_x() const {
|
int range(std::size_t pos) const {
|
||||||
assert(has_range_x_);
|
assert(range_.size() > pos);
|
||||||
((void)has_range_x_); // Prevent unused warning.
|
return range_[pos];
|
||||||
return range_x_;
|
|
||||||
}
|
|
||||||
|
|
||||||
BENCHMARK_ALWAYS_INLINE
|
|
||||||
int range_y() const {
|
|
||||||
assert(has_range_y_);
|
|
||||||
((void)has_range_y_); // Prevent unused warning.
|
|
||||||
return range_y_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK_ALWAYS_INLINE
|
BENCHMARK_ALWAYS_INLINE
|
||||||
@@ -434,11 +438,7 @@ private:
|
|||||||
bool finished_;
|
bool finished_;
|
||||||
size_t total_iterations_;
|
size_t total_iterations_;
|
||||||
|
|
||||||
bool has_range_x_;
|
std::vector<int> range_;
|
||||||
int range_x_;
|
|
||||||
|
|
||||||
bool has_range_y_;
|
|
||||||
int range_y_;
|
|
||||||
|
|
||||||
size_t bytes_processed_;
|
size_t bytes_processed_;
|
||||||
size_t items_processed_;
|
size_t items_processed_;
|
||||||
@@ -489,24 +489,22 @@ public:
|
|||||||
// REQUIRES: The function passed to the constructor must accept an arg1.
|
// REQUIRES: The function passed to the constructor must accept an arg1.
|
||||||
Benchmark* Range(int start, int limit);
|
Benchmark* Range(int start, int limit);
|
||||||
|
|
||||||
// Run this benchmark once for every value in the range [start..limit]
|
// Run this benchmark once for all values in the range [start..limit] with specific step
|
||||||
// REQUIRES: The function passed to the constructor must accept an arg1.
|
// REQUIRES: The function passed to the constructor must accept an arg1.
|
||||||
Benchmark* DenseRange(int start, int limit);
|
Benchmark* DenseRange(int start, int limit, int step = 1);
|
||||||
|
|
||||||
// Run this benchmark once with "x,y" as the extra arguments passed
|
// Run this benchmark once with "args" as the extra arguments passed
|
||||||
// to the function.
|
// to the function.
|
||||||
// REQUIRES: The function passed to the constructor must accept arg1,arg2.
|
// REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
|
||||||
Benchmark* ArgPair(int x, int y);
|
Benchmark* Args(const std::vector<int>& args);
|
||||||
|
|
||||||
// Pick a set of values A from the range [lo1..hi1] and a set
|
// Run this benchmark once for a number of values picked from the
|
||||||
// of values B from the range [lo2..hi2]. Run the benchmark for
|
// ranges [start..limit]. (starts and limits are always picked.)
|
||||||
// every pair of values in the cartesian product of A and B
|
// REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
|
||||||
// (i.e., for all combinations of the values in A and B).
|
Benchmark* Ranges(const std::vector<std::pair<int, int> >& ranges);
|
||||||
// REQUIRES: The function passed to the constructor must accept arg1,arg2.
|
|
||||||
Benchmark* RangePair(int lo1, int hi1, int lo2, int hi2);
|
|
||||||
|
|
||||||
// Pass this benchmark object to *func, which can customize
|
// Pass this benchmark object to *func, which can customize
|
||||||
// the benchmark by calling various methods like Arg, ArgPair,
|
// the benchmark by calling various methods like Arg, Args,
|
||||||
// Threads, etc.
|
// Threads, etc.
|
||||||
Benchmark* Apply(void (*func)(Benchmark* benchmark));
|
Benchmark* Apply(void (*func)(Benchmark* benchmark));
|
||||||
|
|
||||||
@@ -587,6 +585,20 @@ private:
|
|||||||
Benchmark& operator=(Benchmark const&);
|
Benchmark& operator=(Benchmark const&);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Create and register a benchmark with the specified 'name' that invokes
|
||||||
|
// the specified functor 'fn'.
|
||||||
|
//
|
||||||
|
// RETURNS: A pointer to the registered benchmark.
|
||||||
|
internal::Benchmark* RegisterBenchmark(const char* name, internal::Function* fn);
|
||||||
|
|
||||||
|
#if defined(BENCHMARK_HAS_CXX11)
|
||||||
|
template <class Lambda>
|
||||||
|
internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
// The class used to hold all Benchmarks created from static function.
|
// The class used to hold all Benchmarks created from static function.
|
||||||
// (ie those created using the BENCHMARK(...) macros.
|
// (ie those created using the BENCHMARK(...) macros.
|
||||||
class FunctionBenchmark : public Benchmark {
|
class FunctionBenchmark : public Benchmark {
|
||||||
@@ -600,8 +612,57 @@ private:
|
|||||||
Function* func_;
|
Function* func_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_HAS_CXX11
|
||||||
|
template <class Lambda>
|
||||||
|
class LambdaBenchmark : public Benchmark {
|
||||||
|
public:
|
||||||
|
virtual void Run(State& st) { lambda_(st); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class OLambda>
|
||||||
|
LambdaBenchmark(const char* name, OLambda&& lam)
|
||||||
|
: Benchmark(name), lambda_(std::forward<OLambda>(lam)) {}
|
||||||
|
|
||||||
|
LambdaBenchmark(LambdaBenchmark const&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Lam>
|
||||||
|
friend Benchmark* ::benchmark::RegisterBenchmark(const char*, Lam&&);
|
||||||
|
|
||||||
|
Lambda lambda_;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // end namespace internal
|
} // end namespace internal
|
||||||
|
|
||||||
|
inline internal::Benchmark*
|
||||||
|
RegisterBenchmark(const char* name, internal::Function* fn) {
|
||||||
|
return internal::RegisterBenchmarkInternal(
|
||||||
|
::new internal::FunctionBenchmark(name, fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_HAS_CXX11
|
||||||
|
template <class Lambda>
|
||||||
|
internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn) {
|
||||||
|
using BenchType = internal::LambdaBenchmark<typename std::decay<Lambda>::type>;
|
||||||
|
return internal::RegisterBenchmarkInternal(
|
||||||
|
::new BenchType(name, std::forward<Lambda>(fn)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BENCHMARK_HAS_CXX11) && \
|
||||||
|
(!defined(BENCHMARK_GCC_VERSION) || BENCHMARK_GCC_VERSION >= 409)
|
||||||
|
template <class Lambda, class ...Args>
|
||||||
|
internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn,
|
||||||
|
Args&&... args) {
|
||||||
|
return benchmark::RegisterBenchmark(name,
|
||||||
|
[=](benchmark::State& st) { fn(st, args...); });
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// The base class for all fixture tests.
|
// The base class for all fixture tests.
|
||||||
class Fixture: public internal::Benchmark {
|
class Fixture: public internal::Benchmark {
|
||||||
public:
|
public:
|
||||||
@@ -652,11 +713,11 @@ protected:
|
|||||||
|
|
||||||
// Old-style macros
|
// Old-style macros
|
||||||
#define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a))
|
#define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a))
|
||||||
#define BENCHMARK_WITH_ARG2(n, a1, a2) BENCHMARK(n)->ArgPair((a1), (a2))
|
#define BENCHMARK_WITH_ARG2(n, a1, a2) BENCHMARK(n)->Args({(a1), (a2)})
|
||||||
#define BENCHMARK_WITH_UNIT(n, t) BENCHMARK(n)->Unit((t))
|
#define BENCHMARK_WITH_UNIT(n, t) BENCHMARK(n)->Unit((t))
|
||||||
#define BENCHMARK_RANGE(n, lo, hi) BENCHMARK(n)->Range((lo), (hi))
|
#define BENCHMARK_RANGE(n, lo, hi) BENCHMARK(n)->Range((lo), (hi))
|
||||||
#define BENCHMARK_RANGE2(n, l1, h1, l2, h2) \
|
#define BENCHMARK_RANGE2(n, l1, h1, l2, h2) \
|
||||||
BENCHMARK(n)->RangePair((l1), (h1), (l2), (h2))
|
BENCHMARK(n)->RangePair({{(l1), (h1)}, {(l2), (h2)}})
|
||||||
|
|
||||||
#if __cplusplus >= 201103L
|
#if __cplusplus >= 201103L
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
#ifndef BENCHMARK_MACROS_H_
|
#ifndef BENCHMARK_MACROS_H_
|
||||||
#define BENCHMARK_MACROS_H_
|
#define BENCHMARK_MACROS_H_
|
||||||
|
|
||||||
#if __cplusplus < 201103L
|
#if __cplusplus >= 201103L
|
||||||
|
#define BENCHMARK_HAS_CXX11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_HAS_CXX11
|
||||||
# define BENCHMARK_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
# define BENCHMARK_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||||
TypeName(const TypeName&); \
|
TypeName(const TypeName&); \
|
||||||
TypeName& operator=(const TypeName&)
|
TypeName& operator=(const TypeName&)
|
||||||
@@ -53,4 +57,8 @@
|
|||||||
# define BENCHMARK_BUILTIN_EXPECT(x, y) x
|
# define BENCHMARK_BUILTIN_EXPECT(x, y) x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#define BENCHMARK_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // BENCHMARK_MACROS_H_
|
#endif // BENCHMARK_MACROS_H_
|
||||||
|
|||||||
@@ -156,14 +156,23 @@ class BenchmarkReporter {
|
|||||||
// Simple reporter that outputs benchmark data to the console. This is the
|
// Simple reporter that outputs benchmark data to the console. This is the
|
||||||
// default reporter used by RunSpecifiedBenchmarks().
|
// default reporter used by RunSpecifiedBenchmarks().
|
||||||
class ConsoleReporter : public BenchmarkReporter {
|
class ConsoleReporter : public BenchmarkReporter {
|
||||||
public:
|
public:
|
||||||
|
enum OutputOptions {
|
||||||
|
OO_None,
|
||||||
|
OO_Color
|
||||||
|
};
|
||||||
|
explicit ConsoleReporter(OutputOptions color_output = OO_Color)
|
||||||
|
: color_output_(color_output == OO_Color) {}
|
||||||
|
|
||||||
virtual bool ReportContext(const Context& context);
|
virtual bool ReportContext(const Context& context);
|
||||||
virtual void ReportRuns(const std::vector<Run>& reports);
|
virtual void ReportRuns(const std::vector<Run>& reports);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void PrintRunData(const Run& report);
|
virtual void PrintRunData(const Run& report);
|
||||||
|
|
||||||
size_t name_field_width_;
|
size_t name_field_width_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool color_output_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JSONReporter : public BenchmarkReporter {
|
class JSONReporter : public BenchmarkReporter {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -69,6 +70,12 @@ DEFINE_string(benchmark_format, "console",
|
|||||||
"The format to use for console output. Valid values are "
|
"The format to use for console output. Valid values are "
|
||||||
"'console', 'json', or 'csv'.");
|
"'console', 'json', or 'csv'.");
|
||||||
|
|
||||||
|
DEFINE_string(benchmark_out_format, "json",
|
||||||
|
"The format to use for file output. Valid values are "
|
||||||
|
"'console', 'json', or 'csv'.");
|
||||||
|
|
||||||
|
DEFINE_string(benchmark_out, "", "The file to write additonal output to");
|
||||||
|
|
||||||
DEFINE_bool(color_print, true, "Enables colorized logging.");
|
DEFINE_bool(color_print, true, "Enables colorized logging.");
|
||||||
|
|
||||||
DEFINE_int32(v, 0, "The level of verbose logging to output");
|
DEFINE_int32(v, 0, "The level of verbose logging to output");
|
||||||
@@ -306,23 +313,20 @@ namespace internal {
|
|||||||
|
|
||||||
// Information kept per benchmark we may want to run
|
// Information kept per benchmark we may want to run
|
||||||
struct Benchmark::Instance {
|
struct Benchmark::Instance {
|
||||||
std::string name;
|
std::string name;
|
||||||
Benchmark* benchmark;
|
Benchmark* benchmark;
|
||||||
bool has_arg1;
|
std::vector<int> arg;
|
||||||
int arg1;
|
TimeUnit time_unit;
|
||||||
bool has_arg2;
|
int range_multiplier;
|
||||||
int arg2;
|
bool use_real_time;
|
||||||
TimeUnit time_unit;
|
bool use_manual_time;
|
||||||
int range_multiplier;
|
BigO complexity;
|
||||||
bool use_real_time;
|
BigOFunc* complexity_lambda;
|
||||||
bool use_manual_time;
|
bool last_benchmark_instance;
|
||||||
BigO complexity;
|
int repetitions;
|
||||||
BigOFunc* complexity_lambda;
|
double min_time;
|
||||||
bool last_benchmark_instance;
|
int threads; // Number of concurrent threads to use
|
||||||
int repetitions;
|
bool multithreaded; // Is benchmark multi-threaded?
|
||||||
double min_time;
|
|
||||||
int threads; // Number of concurrent threads to use
|
|
||||||
bool multithreaded; // Is benchmark multi-threaded?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class for managing registered benchmarks. Note that each registered
|
// Class for managing registered benchmarks. Note that each registered
|
||||||
@@ -354,9 +358,9 @@ public:
|
|||||||
void Arg(int x);
|
void Arg(int x);
|
||||||
void Unit(TimeUnit unit);
|
void Unit(TimeUnit unit);
|
||||||
void Range(int start, int limit);
|
void Range(int start, int limit);
|
||||||
void DenseRange(int start, int limit);
|
void DenseRange(int start, int limit, int step = 1);
|
||||||
void ArgPair(int start, int limit);
|
void Args(const std::vector<int>& args);
|
||||||
void RangePair(int lo1, int hi1, int lo2, int hi2);
|
void Ranges(const std::vector<std::pair<int, int>>& ranges);
|
||||||
void RangeMultiplier(int multiplier);
|
void RangeMultiplier(int multiplier);
|
||||||
void MinTime(double n);
|
void MinTime(double n);
|
||||||
void Repetitions(int n);
|
void Repetitions(int n);
|
||||||
@@ -371,12 +375,13 @@ public:
|
|||||||
|
|
||||||
static void AddRange(std::vector<int>* dst, int lo, int hi, int mult);
|
static void AddRange(std::vector<int>* dst, int lo, int hi, int mult);
|
||||||
|
|
||||||
|
int ArgsCnt() const { return args_.empty() ? -1 : static_cast<int>(args_.front().size()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class BenchmarkFamilies;
|
friend class BenchmarkFamilies;
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
int arg_count_;
|
std::vector< std::vector<int> > args_; // Args for all benchmark runs
|
||||||
std::vector< std::pair<int, int> > args_; // Args for all benchmark runs
|
|
||||||
TimeUnit time_unit_;
|
TimeUnit time_unit_;
|
||||||
int range_multiplier_;
|
int range_multiplier_;
|
||||||
double min_time_;
|
double min_time_;
|
||||||
@@ -424,10 +429,10 @@ bool BenchmarkFamilies::FindBenchmarks(
|
|||||||
if (!bench_family) continue;
|
if (!bench_family) continue;
|
||||||
BenchmarkImp* family = bench_family->imp_;
|
BenchmarkImp* family = bench_family->imp_;
|
||||||
|
|
||||||
if (family->arg_count_ == -1) {
|
if (family->ArgsCnt() == -1) {
|
||||||
family->arg_count_ = 0;
|
family->Args({});
|
||||||
family->args_.emplace_back(-1, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& args : family->args_) {
|
for (auto const& args : family->args_) {
|
||||||
const std::vector<int>* thread_counts =
|
const std::vector<int>* thread_counts =
|
||||||
(family->thread_counts_.empty()
|
(family->thread_counts_.empty()
|
||||||
@@ -438,10 +443,7 @@ bool BenchmarkFamilies::FindBenchmarks(
|
|||||||
Benchmark::Instance instance;
|
Benchmark::Instance instance;
|
||||||
instance.name = family->name_;
|
instance.name = family->name_;
|
||||||
instance.benchmark = bench_family.get();
|
instance.benchmark = bench_family.get();
|
||||||
instance.has_arg1 = family->arg_count_ >= 1;
|
instance.arg = args;
|
||||||
instance.arg1 = args.first;
|
|
||||||
instance.has_arg2 = family->arg_count_ == 2;
|
|
||||||
instance.arg2 = args.second;
|
|
||||||
instance.time_unit = family->time_unit_;
|
instance.time_unit = family->time_unit_;
|
||||||
instance.range_multiplier = family->range_multiplier_;
|
instance.range_multiplier = family->range_multiplier_;
|
||||||
instance.min_time = family->min_time_;
|
instance.min_time = family->min_time_;
|
||||||
@@ -454,12 +456,10 @@ bool BenchmarkFamilies::FindBenchmarks(
|
|||||||
instance.multithreaded = !(family->thread_counts_.empty());
|
instance.multithreaded = !(family->thread_counts_.empty());
|
||||||
|
|
||||||
// Add arguments to instance name
|
// Add arguments to instance name
|
||||||
if (family->arg_count_ >= 1) {
|
for (auto const& arg : args) {
|
||||||
AppendHumanReadable(instance.arg1, &instance.name);
|
AppendHumanReadable(arg, &instance.name);
|
||||||
}
|
|
||||||
if (family->arg_count_ >= 2) {
|
|
||||||
AppendHumanReadable(instance.arg2, &instance.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsZero(family->min_time_)) {
|
if (!IsZero(family->min_time_)) {
|
||||||
instance.name += StringPrintF("/min_time:%0.3f", family->min_time_);
|
instance.name += StringPrintF("/min_time:%0.3f", family->min_time_);
|
||||||
}
|
}
|
||||||
@@ -488,7 +488,7 @@ bool BenchmarkFamilies::FindBenchmarks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BenchmarkImp::BenchmarkImp(const char* name)
|
BenchmarkImp::BenchmarkImp(const char* name)
|
||||||
: name_(name), arg_count_(-1), time_unit_(kNanosecond),
|
: name_(name), time_unit_(kNanosecond),
|
||||||
range_multiplier_(kRangeMultiplier), min_time_(0.0), repetitions_(0),
|
range_multiplier_(kRangeMultiplier), min_time_(0.0), repetitions_(0),
|
||||||
use_real_time_(false), use_manual_time_(false),
|
use_real_time_(false), use_manual_time_(false),
|
||||||
complexity_(oNone) {
|
complexity_(oNone) {
|
||||||
@@ -498,9 +498,8 @@ BenchmarkImp::~BenchmarkImp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BenchmarkImp::Arg(int x) {
|
void BenchmarkImp::Arg(int x) {
|
||||||
CHECK(arg_count_ == -1 || arg_count_ == 1);
|
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
arg_count_ = 1;
|
args_.push_back({x});
|
||||||
args_.emplace_back(x, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BenchmarkImp::Unit(TimeUnit unit) {
|
void BenchmarkImp::Unit(TimeUnit unit) {
|
||||||
@@ -508,42 +507,54 @@ void BenchmarkImp::Unit(TimeUnit unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BenchmarkImp::Range(int start, int limit) {
|
void BenchmarkImp::Range(int start, int limit) {
|
||||||
CHECK(arg_count_ == -1 || arg_count_ == 1);
|
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
arg_count_ = 1;
|
|
||||||
std::vector<int> arglist;
|
std::vector<int> arglist;
|
||||||
AddRange(&arglist, start, limit, range_multiplier_);
|
AddRange(&arglist, start, limit, range_multiplier_);
|
||||||
|
|
||||||
for (int i : arglist) {
|
for (int i : arglist) {
|
||||||
args_.emplace_back(i, -1);
|
args_.push_back({i});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BenchmarkImp::DenseRange(int start, int limit) {
|
void BenchmarkImp::DenseRange(int start, int limit, int step) {
|
||||||
CHECK(arg_count_ == -1 || arg_count_ == 1);
|
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
arg_count_ = 1;
|
|
||||||
CHECK_GE(start, 0);
|
CHECK_GE(start, 0);
|
||||||
CHECK_LE(start, limit);
|
CHECK_LE(start, limit);
|
||||||
for (int arg = start; arg <= limit; arg++) {
|
for (int arg = start; arg <= limit; arg+= step) {
|
||||||
args_.emplace_back(arg, -1);
|
args_.push_back({arg});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BenchmarkImp::ArgPair(int x, int y) {
|
void BenchmarkImp::Args(const std::vector<int>& args)
|
||||||
CHECK(arg_count_ == -1 || arg_count_ == 2);
|
{
|
||||||
arg_count_ = 2;
|
args_.push_back(args);
|
||||||
args_.emplace_back(x, y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BenchmarkImp::RangePair(int lo1, int hi1, int lo2, int hi2) {
|
void BenchmarkImp::Ranges(const std::vector<std::pair<int, int>>& ranges) {
|
||||||
CHECK(arg_count_ == -1 || arg_count_ == 2);
|
std::vector<std::vector<int>> arglists(ranges.size());
|
||||||
arg_count_ = 2;
|
int total = 1;
|
||||||
std::vector<int> arglist1, arglist2;
|
for (std::size_t i = 0; i < ranges.size(); i++) {
|
||||||
AddRange(&arglist1, lo1, hi1, range_multiplier_);
|
AddRange(&arglists[i], ranges[i].first, ranges[i].second, range_multiplier_);
|
||||||
AddRange(&arglist2, lo2, hi2, range_multiplier_);
|
total *= arglists[i].size();
|
||||||
|
}
|
||||||
|
|
||||||
for (int i : arglist1) {
|
std::vector<std::size_t> ctr(total, 0);
|
||||||
for (int j : arglist2) {
|
|
||||||
args_.emplace_back(i, j);
|
for (int i = 0; i < total; i++) {
|
||||||
|
std::vector<int> tmp;
|
||||||
|
|
||||||
|
for (std::size_t j = 0; j < arglists.size(); j++) {
|
||||||
|
tmp.push_back(arglists[j][ctr[j]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
args_.push_back(tmp);
|
||||||
|
|
||||||
|
for (std::size_t j = 0; j < arglists.size(); j++) {
|
||||||
|
if (ctr[j] + 1 < arglists[j].size()) {
|
||||||
|
++ctr[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctr[j] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,6 +652,7 @@ Benchmark::Benchmark(Benchmark const& other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Benchmark* Benchmark::Arg(int x) {
|
Benchmark* Benchmark::Arg(int x) {
|
||||||
|
CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == 1);
|
||||||
imp_->Arg(x);
|
imp_->Arg(x);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -651,22 +663,27 @@ Benchmark* Benchmark::Unit(TimeUnit unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Benchmark* Benchmark::Range(int start, int limit) {
|
Benchmark* Benchmark::Range(int start, int limit) {
|
||||||
|
CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == 1);
|
||||||
imp_->Range(start, limit);
|
imp_->Range(start, limit);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Benchmark* Benchmark::DenseRange(int start, int limit) {
|
Benchmark* Benchmark::Ranges(const std::vector<std::pair<int, int>>& ranges)
|
||||||
imp_->DenseRange(start, limit);
|
{
|
||||||
|
CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == static_cast<int>(ranges.size()));
|
||||||
|
imp_->Ranges(ranges);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Benchmark* Benchmark::ArgPair(int x, int y) {
|
Benchmark* Benchmark::DenseRange(int start, int limit, int step) {
|
||||||
imp_->ArgPair(x, y);
|
CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == 1);
|
||||||
|
imp_->DenseRange(start, limit, step);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Benchmark* Benchmark::RangePair(int lo1, int hi1, int lo2, int hi2) {
|
Benchmark* Benchmark::Args(const std::vector<int>& args) {
|
||||||
imp_->RangePair(lo1, hi1, lo2, hi2);
|
CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == static_cast<int>(args.size()));
|
||||||
|
imp_->Args(args);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,7 +761,7 @@ namespace {
|
|||||||
void RunInThread(const benchmark::internal::Benchmark::Instance* b,
|
void RunInThread(const benchmark::internal::Benchmark::Instance* b,
|
||||||
size_t iters, int thread_id,
|
size_t iters, int thread_id,
|
||||||
ThreadStats* total) EXCLUDES(GetBenchmarkLock()) {
|
ThreadStats* total) EXCLUDES(GetBenchmarkLock()) {
|
||||||
State st(iters, b->has_arg1, b->arg1, b->has_arg2, b->arg2, thread_id, b->threads);
|
State st(iters, b->arg, thread_id, b->threads);
|
||||||
b->benchmark->Run(st);
|
b->benchmark->Run(st);
|
||||||
CHECK(st.iterations() == st.max_iterations) <<
|
CHECK(st.iterations() == st.max_iterations) <<
|
||||||
"Benchmark returned before State::KeepRunning() returned false!";
|
"Benchmark returned before State::KeepRunning() returned false!";
|
||||||
@@ -758,14 +775,13 @@ void RunInThread(const benchmark::internal::Benchmark::Instance* b,
|
|||||||
timer_manager->Finalize();
|
timer_manager->Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunBenchmark(const benchmark::internal::Benchmark::Instance& b,
|
std::vector<BenchmarkReporter::Run>
|
||||||
BenchmarkReporter* br,
|
RunBenchmark(const benchmark::internal::Benchmark::Instance& b,
|
||||||
std::vector<BenchmarkReporter::Run>& complexity_reports)
|
std::vector<BenchmarkReporter::Run>* complexity_reports)
|
||||||
EXCLUDES(GetBenchmarkLock()) {
|
EXCLUDES(GetBenchmarkLock()) {
|
||||||
|
std::vector<BenchmarkReporter::Run> reports; // return value
|
||||||
size_t iters = 1;
|
size_t iters = 1;
|
||||||
|
|
||||||
std::vector<BenchmarkReporter::Run> reports;
|
|
||||||
|
|
||||||
std::vector<std::thread> pool;
|
std::vector<std::thread> pool;
|
||||||
if (b.multithreaded)
|
if (b.multithreaded)
|
||||||
pool.resize(b.threads);
|
pool.resize(b.threads);
|
||||||
@@ -872,7 +888,7 @@ void RunBenchmark(const benchmark::internal::Benchmark::Instance& b,
|
|||||||
report.complexity = b.complexity;
|
report.complexity = b.complexity;
|
||||||
report.complexity_lambda = b.complexity_lambda;
|
report.complexity_lambda = b.complexity_lambda;
|
||||||
if(report.complexity != oNone)
|
if(report.complexity != oNone)
|
||||||
complexity_reports.push_back(report);
|
complexity_reports->push_back(report);
|
||||||
}
|
}
|
||||||
|
|
||||||
reports.push_back(report);
|
reports.push_back(report);
|
||||||
@@ -903,27 +919,26 @@ void RunBenchmark(const benchmark::internal::Benchmark::Instance& b,
|
|||||||
additional_run_stats.end());
|
additional_run_stats.end());
|
||||||
|
|
||||||
if((b.complexity != oNone) && b.last_benchmark_instance) {
|
if((b.complexity != oNone) && b.last_benchmark_instance) {
|
||||||
additional_run_stats = ComputeBigO(complexity_reports);
|
additional_run_stats = ComputeBigO(*complexity_reports);
|
||||||
reports.insert(reports.end(), additional_run_stats.begin(),
|
reports.insert(reports.end(), additional_run_stats.begin(),
|
||||||
additional_run_stats.end());
|
additional_run_stats.end());
|
||||||
complexity_reports.clear();
|
complexity_reports->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
br->ReportRuns(reports);
|
|
||||||
|
|
||||||
if (b.multithreaded) {
|
if (b.multithreaded) {
|
||||||
for (std::thread& thread : pool)
|
for (std::thread& thread : pool)
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return reports;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
State::State(size_t max_iters, bool has_x, int x, bool has_y, int y,
|
State::State(size_t max_iters, const std::vector<int>& ranges,
|
||||||
int thread_i, int n_threads)
|
int thread_i, int n_threads)
|
||||||
: started_(false), finished_(false), total_iterations_(0),
|
: started_(false), finished_(false), total_iterations_(0),
|
||||||
has_range_x_(has_x), range_x_(x),
|
range_(ranges),
|
||||||
has_range_y_(has_y), range_y_(y),
|
|
||||||
bytes_processed_(0), items_processed_(0),
|
bytes_processed_(0), items_processed_(0),
|
||||||
complexity_n_(0),
|
complexity_n_(0),
|
||||||
error_occurred_(false),
|
error_occurred_(false),
|
||||||
@@ -975,8 +990,10 @@ namespace internal {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void RunMatchingBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
|
void RunMatchingBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
|
||||||
BenchmarkReporter* reporter) {
|
BenchmarkReporter* console_reporter,
|
||||||
CHECK(reporter != nullptr);
|
BenchmarkReporter* file_reporter) {
|
||||||
|
// Note the file_reporter can be null.
|
||||||
|
CHECK(console_reporter != nullptr);
|
||||||
|
|
||||||
// Determine the width of the name field using a minimum width of 10.
|
// Determine the width of the name field using a minimum width of 10.
|
||||||
bool has_repetitions = FLAGS_benchmark_repetitions > 1;
|
bool has_repetitions = FLAGS_benchmark_repetitions > 1;
|
||||||
@@ -1000,23 +1017,30 @@ void RunMatchingBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
|
|||||||
// Keep track of runing times of all instances of current benchmark
|
// Keep track of runing times of all instances of current benchmark
|
||||||
std::vector<BenchmarkReporter::Run> complexity_reports;
|
std::vector<BenchmarkReporter::Run> complexity_reports;
|
||||||
|
|
||||||
if (reporter->ReportContext(context)) {
|
if (console_reporter->ReportContext(context)
|
||||||
|
&& (!file_reporter || file_reporter->ReportContext(context))) {
|
||||||
for (const auto& benchmark : benchmarks) {
|
for (const auto& benchmark : benchmarks) {
|
||||||
RunBenchmark(benchmark, reporter, complexity_reports);
|
std::vector<BenchmarkReporter::Run> reports =
|
||||||
|
RunBenchmark(benchmark, &complexity_reports);
|
||||||
|
console_reporter->ReportRuns(reports);
|
||||||
|
if (file_reporter) file_reporter->ReportRuns(reports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console_reporter->Finalize();
|
||||||
|
if (file_reporter) file_reporter->Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<BenchmarkReporter> GetDefaultReporter() {
|
std::unique_ptr<BenchmarkReporter>
|
||||||
|
CreateReporter(std::string const& name, ConsoleReporter::OutputOptions allow_color) {
|
||||||
typedef std::unique_ptr<BenchmarkReporter> PtrType;
|
typedef std::unique_ptr<BenchmarkReporter> PtrType;
|
||||||
if (FLAGS_benchmark_format == "console") {
|
if (name == "console") {
|
||||||
return PtrType(new ConsoleReporter);
|
return PtrType(new ConsoleReporter(allow_color));
|
||||||
} else if (FLAGS_benchmark_format == "json") {
|
} else if (name == "json") {
|
||||||
return PtrType(new JSONReporter);
|
return PtrType(new JSONReporter);
|
||||||
} else if (FLAGS_benchmark_format == "csv") {
|
} else if (name == "csv") {
|
||||||
return PtrType(new CSVReporter);
|
return PtrType(new CSVReporter);
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Unexpected format: '" << FLAGS_benchmark_format << "'\n";
|
std::cerr << "Unexpected format: '" << name << "'\n";
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1025,10 +1049,17 @@ std::unique_ptr<BenchmarkReporter> GetDefaultReporter() {
|
|||||||
} // end namespace internal
|
} // end namespace internal
|
||||||
|
|
||||||
size_t RunSpecifiedBenchmarks() {
|
size_t RunSpecifiedBenchmarks() {
|
||||||
return RunSpecifiedBenchmarks(nullptr);
|
return RunSpecifiedBenchmarks(nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RunSpecifiedBenchmarks(BenchmarkReporter* reporter) {
|
|
||||||
|
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter) {
|
||||||
|
return RunSpecifiedBenchmarks(console_reporter, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter,
|
||||||
|
BenchmarkReporter* file_reporter) {
|
||||||
std::string spec = FLAGS_benchmark_filter;
|
std::string spec = FLAGS_benchmark_filter;
|
||||||
if (spec.empty() || spec == "all")
|
if (spec.empty() || spec == "all")
|
||||||
spec = "."; // Regexp that matches all benchmarks
|
spec = "."; // Regexp that matches all benchmarks
|
||||||
@@ -1041,13 +1072,38 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* reporter) {
|
|||||||
for (auto const& benchmark : benchmarks)
|
for (auto const& benchmark : benchmarks)
|
||||||
std::cout << benchmark.name << "\n";
|
std::cout << benchmark.name << "\n";
|
||||||
} else {
|
} else {
|
||||||
std::unique_ptr<BenchmarkReporter> default_reporter;
|
// Setup the reporters
|
||||||
if (!reporter) {
|
std::ofstream output_file;
|
||||||
default_reporter = internal::GetDefaultReporter();
|
std::unique_ptr<BenchmarkReporter> default_console_reporter;
|
||||||
reporter = default_reporter.get();
|
std::unique_ptr<BenchmarkReporter> default_file_reporter;
|
||||||
|
if (!console_reporter) {
|
||||||
|
auto output_opts = FLAGS_color_print ? ConsoleReporter::OO_Color
|
||||||
|
: ConsoleReporter::OO_None;
|
||||||
|
default_console_reporter = internal::CreateReporter(
|
||||||
|
FLAGS_benchmark_format, output_opts);
|
||||||
|
console_reporter = default_console_reporter.get();
|
||||||
}
|
}
|
||||||
internal::RunMatchingBenchmarks(benchmarks, reporter);
|
std::string const& fname = FLAGS_benchmark_out;
|
||||||
reporter->Finalize();
|
if (fname == "" && file_reporter) {
|
||||||
|
std::cerr << "A custom file reporter was provided but "
|
||||||
|
"--benchmark_out=<file> was not specified." << std::endl;
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
if (fname != "") {
|
||||||
|
output_file.open(fname);
|
||||||
|
if (!output_file.is_open()) {
|
||||||
|
std::cerr << "invalid file name: '" << fname << std::endl;
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
if (!file_reporter) {
|
||||||
|
default_file_reporter = internal::CreateReporter(
|
||||||
|
FLAGS_benchmark_out_format, ConsoleReporter::OO_None);
|
||||||
|
file_reporter = default_file_reporter.get();
|
||||||
|
}
|
||||||
|
file_reporter->SetOutputStream(&output_file);
|
||||||
|
file_reporter->SetErrorStream(&output_file);
|
||||||
|
}
|
||||||
|
internal::RunMatchingBenchmarks(benchmarks, console_reporter, file_reporter);
|
||||||
}
|
}
|
||||||
return benchmarks.size();
|
return benchmarks.size();
|
||||||
}
|
}
|
||||||
@@ -1062,6 +1118,8 @@ void PrintUsageAndExit() {
|
|||||||
" [--benchmark_min_time=<min_time>]\n"
|
" [--benchmark_min_time=<min_time>]\n"
|
||||||
" [--benchmark_repetitions=<num_repetitions>]\n"
|
" [--benchmark_repetitions=<num_repetitions>]\n"
|
||||||
" [--benchmark_format=<console|json|csv>]\n"
|
" [--benchmark_format=<console|json|csv>]\n"
|
||||||
|
" [--benchmark_out=<filename>]\n"
|
||||||
|
" [--benchmark_out_format=<json|console|csv>]\n"
|
||||||
" [--color_print={true|false}]\n"
|
" [--color_print={true|false}]\n"
|
||||||
" [--v=<verbosity>]\n");
|
" [--v=<verbosity>]\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -1081,6 +1139,10 @@ void ParseCommandLineFlags(int* argc, char** argv) {
|
|||||||
&FLAGS_benchmark_repetitions) ||
|
&FLAGS_benchmark_repetitions) ||
|
||||||
ParseStringFlag(argv[i], "benchmark_format",
|
ParseStringFlag(argv[i], "benchmark_format",
|
||||||
&FLAGS_benchmark_format) ||
|
&FLAGS_benchmark_format) ||
|
||||||
|
ParseStringFlag(argv[i], "benchmark_out",
|
||||||
|
&FLAGS_benchmark_out) ||
|
||||||
|
ParseStringFlag(argv[i], "benchmark_out_format",
|
||||||
|
&FLAGS_benchmark_out_format) ||
|
||||||
ParseBoolFlag(argv[i], "color_print",
|
ParseBoolFlag(argv[i], "color_print",
|
||||||
&FLAGS_color_print) ||
|
&FLAGS_color_print) ||
|
||||||
ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
|
ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
|
||||||
@@ -1092,10 +1154,9 @@ void ParseCommandLineFlags(int* argc, char** argv) {
|
|||||||
PrintUsageAndExit();
|
PrintUsageAndExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto const* flag : {&FLAGS_benchmark_format,
|
||||||
if (FLAGS_benchmark_format != "console" &&
|
&FLAGS_benchmark_out_format})
|
||||||
FLAGS_benchmark_format != "json" &&
|
if (*flag != "console" && *flag != "json" && *flag != "csv") {
|
||||||
FLAGS_benchmark_format != "csv") {
|
|
||||||
PrintUsageAndExit();
|
PrintUsageAndExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "commandlineflags.h"
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "internal_macros.h"
|
#include "internal_macros.h"
|
||||||
|
|
||||||
@@ -28,8 +27,6 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DECLARE_bool(color_print);
|
|
||||||
|
|
||||||
namespace benchmark {
|
namespace benchmark {
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef BENCHMARK_OS_WINDOWS
|
#ifdef BENCHMARK_OS_WINDOWS
|
||||||
@@ -120,14 +117,14 @@ std::string FormatString(const char *msg, ...) {
|
|||||||
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
|
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
ColorPrintf(out, color, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
if (!FLAGS_color_print) {
|
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, va_list args) {
|
||||||
out << FormatString(fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BENCHMARK_OS_WINDOWS
|
#ifdef BENCHMARK_OS_WINDOWS
|
||||||
|
((void)out); // suppress unused warning
|
||||||
|
|
||||||
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
|
||||||
// Gets the current text color.
|
// Gets the current text color.
|
||||||
@@ -152,7 +149,6 @@ void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
|
|||||||
out << FormatString(fmt, args) << "\033[m";
|
out << FormatString(fmt, args) << "\033[m";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace benchmark
|
} // end namespace benchmark
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ enum LogColor {
|
|||||||
std::string FormatString(const char* msg, va_list args);
|
std::string FormatString(const char* msg, va_list args);
|
||||||
std::string FormatString(const char* msg, ...);
|
std::string FormatString(const char* msg, ...);
|
||||||
|
|
||||||
|
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, va_list args);
|
||||||
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...);
|
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...);
|
||||||
|
|
||||||
} // end namespace benchmark
|
} // end namespace benchmark
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ BigOFunc* FittingCurve(BigO complexity) {
|
|||||||
case oN:
|
case oN:
|
||||||
return [](int n) -> double { return n; };
|
return [](int n) -> double { return n; };
|
||||||
case oNSquared:
|
case oNSquared:
|
||||||
return [](int n) -> double { return n * n; };
|
return [](int n) -> double { return std::pow(n, 2); };
|
||||||
case oNCubed:
|
case oNCubed:
|
||||||
return [](int n) -> double { return n * n * n; };
|
return [](int n) -> double { return std::pow(n, 3); };
|
||||||
case oLogN:
|
case oLogN:
|
||||||
return [](int n) { return std::log2(n); };
|
return [](int n) { return std::log2(n); };
|
||||||
case oNLogN:
|
case oNLogN:
|
||||||
|
|||||||
@@ -30,8 +30,6 @@
|
|||||||
#include "string_util.h"
|
#include "string_util.h"
|
||||||
#include "walltime.h"
|
#include "walltime.h"
|
||||||
|
|
||||||
DECLARE_bool(color_print);
|
|
||||||
|
|
||||||
namespace benchmark {
|
namespace benchmark {
|
||||||
|
|
||||||
bool ConsoleReporter::ReportContext(const Context& context) {
|
bool ConsoleReporter::ReportContext(const Context& context) {
|
||||||
@@ -40,10 +38,10 @@ bool ConsoleReporter::ReportContext(const Context& context) {
|
|||||||
PrintBasicContext(&GetErrorStream(), context);
|
PrintBasicContext(&GetErrorStream(), context);
|
||||||
|
|
||||||
#ifdef BENCHMARK_OS_WINDOWS
|
#ifdef BENCHMARK_OS_WINDOWS
|
||||||
if (FLAGS_color_print && &std::cout != &GetOutputStream()) {
|
if (color_output_ && &std::cout != &GetOutputStream()) {
|
||||||
GetErrorStream() << "Color printing is only supported for stdout on windows."
|
GetErrorStream() << "Color printing is only supported for stdout on windows."
|
||||||
" Disabling color printing\n";
|
" Disabling color printing\n";
|
||||||
FLAGS_color_print = false;
|
color_output_ = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::string str = FormatString("%-*s %13s %13s %10s\n",
|
std::string str = FormatString("%-*s %13s %13s %10s\n",
|
||||||
@@ -59,18 +57,29 @@ void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) {
|
|||||||
PrintRunData(run);
|
PrintRunData(run);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleReporter::PrintRunData(const Run& result) {
|
static void IgnoreColorPrint(std::ostream& out, LogColor,
|
||||||
auto& Out = GetOutputStream();
|
const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
out << FormatString(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleReporter::PrintRunData(const Run& result) {
|
||||||
|
typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...);
|
||||||
|
auto& Out = GetOutputStream();
|
||||||
|
PrinterFn* printer = color_output_ ? (PrinterFn*)ColorPrintf
|
||||||
|
: IgnoreColorPrint;
|
||||||
auto name_color =
|
auto name_color =
|
||||||
(result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN;
|
(result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN;
|
||||||
ColorPrintf(Out, name_color, "%-*s ", name_field_width_,
|
printer(Out, name_color, "%-*s ", name_field_width_,
|
||||||
result.benchmark_name.c_str());
|
result.benchmark_name.c_str());
|
||||||
|
|
||||||
if (result.error_occurred) {
|
if (result.error_occurred) {
|
||||||
ColorPrintf(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'",
|
printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'",
|
||||||
result.error_message.c_str());
|
result.error_message.c_str());
|
||||||
ColorPrintf(Out, COLOR_DEFAULT, "\n");
|
printer(Out, COLOR_DEFAULT, "\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Format bytes per second
|
// Format bytes per second
|
||||||
@@ -91,34 +100,34 @@ void ConsoleReporter::PrintRunData(const Run& result) {
|
|||||||
|
|
||||||
if (result.report_big_o) {
|
if (result.report_big_o) {
|
||||||
std::string big_o = GetBigOString(result.complexity);
|
std::string big_o = GetBigOString(result.complexity);
|
||||||
ColorPrintf(Out, COLOR_YELLOW, "%10.2f %s %10.2f %s ", real_time,
|
printer(Out, COLOR_YELLOW, "%10.2f %s %10.2f %s ", real_time,
|
||||||
big_o.c_str(), cpu_time, big_o.c_str());
|
big_o.c_str(), cpu_time, big_o.c_str());
|
||||||
} else if (result.report_rms) {
|
} else if (result.report_rms) {
|
||||||
ColorPrintf(Out, COLOR_YELLOW, "%10.0f %% %10.0f %% ", real_time * 100,
|
printer(Out, COLOR_YELLOW, "%10.0f %% %10.0f %% ", real_time * 100,
|
||||||
cpu_time * 100);
|
cpu_time * 100);
|
||||||
} else {
|
} else {
|
||||||
const char* timeLabel = GetTimeUnitString(result.time_unit);
|
const char* timeLabel = GetTimeUnitString(result.time_unit);
|
||||||
ColorPrintf(Out, COLOR_YELLOW, "%10.0f %s %10.0f %s ", real_time, timeLabel,
|
printer(Out, COLOR_YELLOW, "%10.0f %s %10.0f %s ", real_time, timeLabel,
|
||||||
cpu_time, timeLabel);
|
cpu_time, timeLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.report_big_o && !result.report_rms) {
|
if (!result.report_big_o && !result.report_rms) {
|
||||||
ColorPrintf(Out, COLOR_CYAN, "%10lld", result.iterations);
|
printer(Out, COLOR_CYAN, "%10lld", result.iterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rate.empty()) {
|
if (!rate.empty()) {
|
||||||
ColorPrintf(Out, COLOR_DEFAULT, " %*s", 13, rate.c_str());
|
printer(Out, COLOR_DEFAULT, " %*s", 13, rate.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!items.empty()) {
|
if (!items.empty()) {
|
||||||
ColorPrintf(Out, COLOR_DEFAULT, " %*s", 18, items.c_str());
|
printer(Out, COLOR_DEFAULT, " %*s", 18, items.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.report_label.empty()) {
|
if (!result.report_label.empty()) {
|
||||||
ColorPrintf(Out, COLOR_DEFAULT, " %s", result.report_label.c_str());
|
printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorPrintf(Out, COLOR_DEFAULT, "\n");
|
printer(Out, COLOR_DEFAULT, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace benchmark
|
} // end namespace benchmark
|
||||||
|
|||||||
@@ -113,11 +113,11 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() {
|
|||||||
uint32_t pmuseren;
|
uint32_t pmuseren;
|
||||||
uint32_t pmcntenset;
|
uint32_t pmcntenset;
|
||||||
// Read the user mode perf monitor counter access permissions.
|
// Read the user mode perf monitor counter access permissions.
|
||||||
asm("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
|
asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
|
||||||
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
|
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
|
||||||
asm("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
|
asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
|
||||||
if (pmcntenset & 0x80000000ul) { // Is it counting?
|
if (pmcntenset & 0x80000000ul) { // Is it counting?
|
||||||
asm("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
|
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
|
||||||
// The counter is set up to count every 64th cycle
|
// The counter is set up to count every 64th cycle
|
||||||
return static_cast<int64_t>(pmccntr) * 64; // Should optimize to << 6
|
return static_cast<int64_t>(pmccntr) * 64; // Should optimize to << 6
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ void InitializeSystemInfo() {
|
|||||||
}
|
}
|
||||||
// TODO: also figure out cpuinfo_num_cpus
|
// TODO: also figure out cpuinfo_num_cpus
|
||||||
|
|
||||||
|
|
||||||
#elif defined BENCHMARK_OS_WINDOWS
|
#elif defined BENCHMARK_OS_WINDOWS
|
||||||
// In NT, read MHz from the registry. If we fail to do so or we're in win9x
|
// In NT, read MHz from the registry. If we fail to do so or we're in win9x
|
||||||
// then make a crude estimate.
|
// then make a crude estimate.
|
||||||
@@ -251,7 +252,12 @@ void InitializeSystemInfo() {
|
|||||||
cpuinfo_cycles_per_second = static_cast<double>((int64_t)data * (int64_t)(1000 * 1000)); // was mhz
|
cpuinfo_cycles_per_second = static_cast<double>((int64_t)data * (int64_t)(1000 * 1000)); // was mhz
|
||||||
else
|
else
|
||||||
cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
|
cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
|
||||||
// TODO: also figure out cpuinfo_num_cpus
|
|
||||||
|
SYSTEM_INFO sysinfo;
|
||||||
|
// Use memset as opposed to = {} to avoid GCC missing initializer false positives.
|
||||||
|
std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
|
||||||
|
GetSystemInfo(&sysinfo);
|
||||||
|
cpuinfo_num_cpus = sysinfo.dwNumberOfProcessors; // number of logical processors in the current group
|
||||||
|
|
||||||
#elif defined BENCHMARK_OS_MACOSX
|
#elif defined BENCHMARK_OS_MACOSX
|
||||||
// returning "mach time units" per second. the current number of elapsed
|
// returning "mach time units" per second. the current number of elapsed
|
||||||
|
|||||||
@@ -45,9 +45,15 @@ add_test(donotoptimize_test donotoptimize_test --benchmark_min_time=0.01)
|
|||||||
compile_benchmark_test(fixture_test)
|
compile_benchmark_test(fixture_test)
|
||||||
add_test(fixture_test fixture_test --benchmark_min_time=0.01)
|
add_test(fixture_test fixture_test --benchmark_min_time=0.01)
|
||||||
|
|
||||||
|
compile_benchmark_test(register_benchmark_test)
|
||||||
|
add_test(register_benchmark_test register_benchmark_test --benchmark_min_time=0.01)
|
||||||
|
|
||||||
compile_benchmark_test(map_test)
|
compile_benchmark_test(map_test)
|
||||||
add_test(map_test map_test --benchmark_min_time=0.01)
|
add_test(map_test map_test --benchmark_min_time=0.01)
|
||||||
|
|
||||||
|
compile_benchmark_test(multiple_ranges_test)
|
||||||
|
add_test(multiple_ranges_test multiple_ranges_test --benchmark_min_time=0.01)
|
||||||
|
|
||||||
compile_benchmark_test(reporter_output_test)
|
compile_benchmark_test(reporter_output_test)
|
||||||
add_test(reporter_output_test reporter_output_test --benchmark_min_time=0.01)
|
add_test(reporter_output_test reporter_output_test --benchmark_min_time=0.01)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ BENCHMARK(BM_empty)->ThreadPerCpu();
|
|||||||
|
|
||||||
void BM_spin_empty(benchmark::State& state) {
|
void BM_spin_empty(benchmark::State& state) {
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
for (int x = 0; x < state.range_x(); ++x) {
|
for (int x = 0; x < state.range(0); ++x) {
|
||||||
benchmark::DoNotOptimize(x);
|
benchmark::DoNotOptimize(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,11 +23,11 @@ BASIC_BENCHMARK_TEST(BM_spin_empty);
|
|||||||
BASIC_BENCHMARK_TEST(BM_spin_empty)->ThreadPerCpu();
|
BASIC_BENCHMARK_TEST(BM_spin_empty)->ThreadPerCpu();
|
||||||
|
|
||||||
void BM_spin_pause_before(benchmark::State& state) {
|
void BM_spin_pause_before(benchmark::State& state) {
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
while(state.KeepRunning()) {
|
while(state.KeepRunning()) {
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,11 +39,11 @@ BASIC_BENCHMARK_TEST(BM_spin_pause_before)->ThreadPerCpu();
|
|||||||
void BM_spin_pause_during(benchmark::State& state) {
|
void BM_spin_pause_during(benchmark::State& state) {
|
||||||
while(state.KeepRunning()) {
|
while(state.KeepRunning()) {
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,11 +64,11 @@ BENCHMARK(BM_pause_during)->UseRealTime()->ThreadPerCpu();
|
|||||||
|
|
||||||
void BM_spin_pause_after(benchmark::State& state) {
|
void BM_spin_pause_after(benchmark::State& state) {
|
||||||
while(state.KeepRunning()) {
|
while(state.KeepRunning()) {
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,15 +77,15 @@ BASIC_BENCHMARK_TEST(BM_spin_pause_after)->ThreadPerCpu();
|
|||||||
|
|
||||||
|
|
||||||
void BM_spin_pause_before_and_after(benchmark::State& state) {
|
void BM_spin_pause_before_and_after(benchmark::State& state) {
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
while(state.KeepRunning()) {
|
while(state.KeepRunning()) {
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < state.range_x(); ++i) {
|
for (int i = 0; i < state.range(0); ++i) {
|
||||||
benchmark::DoNotOptimize(i);
|
benchmark::DoNotOptimize(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ BENCHMARK(BM_Factorial)->UseRealTime();
|
|||||||
static void BM_CalculatePiRange(benchmark::State& state) {
|
static void BM_CalculatePiRange(benchmark::State& state) {
|
||||||
double pi = 0.0;
|
double pi = 0.0;
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
pi = CalculatePi(state.range_x());
|
pi = CalculatePi(state.range(0));
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << pi;
|
ss << pi;
|
||||||
state.SetLabel(ss.str());
|
state.SetLabel(ss.str());
|
||||||
@@ -87,25 +87,25 @@ BENCHMARK(BM_CalculatePi)->ThreadPerCpu();
|
|||||||
static void BM_SetInsert(benchmark::State& state) {
|
static void BM_SetInsert(benchmark::State& state) {
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
std::set<int> data = ConstructRandomSet(state.range_x());
|
std::set<int> data = ConstructRandomSet(state.range(0));
|
||||||
state.ResumeTiming();
|
state.ResumeTiming();
|
||||||
for (int j = 0; j < state.range_y(); ++j)
|
for (int j = 0; j < state.range(1); ++j)
|
||||||
data.insert(rand());
|
data.insert(rand());
|
||||||
}
|
}
|
||||||
state.SetItemsProcessed(state.iterations() * state.range_y());
|
state.SetItemsProcessed(state.iterations() * state.range(1));
|
||||||
state.SetBytesProcessed(state.iterations() * state.range_y() * sizeof(int));
|
state.SetBytesProcessed(state.iterations() * state.range(1) * sizeof(int));
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_SetInsert)->RangePair(1<<10,8<<10, 1,10);
|
BENCHMARK(BM_SetInsert)->Ranges({{1<<10,8<<10}, {1,10}});
|
||||||
|
|
||||||
template<typename Container, typename ValueType = typename Container::value_type>
|
template<typename Container, typename ValueType = typename Container::value_type>
|
||||||
static void BM_Sequential(benchmark::State& state) {
|
static void BM_Sequential(benchmark::State& state) {
|
||||||
ValueType v = 42;
|
ValueType v = 42;
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
Container c;
|
Container c;
|
||||||
for (int i = state.range_x(); --i; )
|
for (int i = state.range(0); --i; )
|
||||||
c.push_back(v);
|
c.push_back(v);
|
||||||
}
|
}
|
||||||
const size_t items_processed = state.iterations() * state.range_x();
|
const size_t items_processed = state.iterations() * state.range(0);
|
||||||
state.SetItemsProcessed(items_processed);
|
state.SetItemsProcessed(items_processed);
|
||||||
state.SetBytesProcessed(items_processed * sizeof(v));
|
state.SetBytesProcessed(items_processed * sizeof(v));
|
||||||
}
|
}
|
||||||
@@ -117,8 +117,8 @@ BENCHMARK_TEMPLATE(BM_Sequential, std::vector<int>, int)->Arg(512);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void BM_StringCompare(benchmark::State& state) {
|
static void BM_StringCompare(benchmark::State& state) {
|
||||||
std::string s1(state.range_x(), '-');
|
std::string s1(state.range(0), '-');
|
||||||
std::string s2(state.range_x(), '-');
|
std::string s2(state.range(0), '-');
|
||||||
while (state.KeepRunning())
|
while (state.KeepRunning())
|
||||||
benchmark::DoNotOptimize(s1.compare(s2));
|
benchmark::DoNotOptimize(s1.compare(s2));
|
||||||
}
|
}
|
||||||
@@ -147,14 +147,14 @@ BENCHMARK(BM_SetupTeardown)->ThreadPerCpu();
|
|||||||
static void BM_LongTest(benchmark::State& state) {
|
static void BM_LongTest(benchmark::State& state) {
|
||||||
double tracker = 0.0;
|
double tracker = 0.0;
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
for (int i = 0; i < state.range_x(); ++i)
|
for (int i = 0; i < state.range(0); ++i)
|
||||||
benchmark::DoNotOptimize(tracker += i);
|
benchmark::DoNotOptimize(tracker += i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_LongTest)->Range(1<<16,1<<28);
|
BENCHMARK(BM_LongTest)->Range(1<<16,1<<28);
|
||||||
|
|
||||||
static void BM_ParallelMemset(benchmark::State& state) {
|
static void BM_ParallelMemset(benchmark::State& state) {
|
||||||
int size = state.range_x() / sizeof(int);
|
int size = state.range(0) / sizeof(int);
|
||||||
int thread_size = size / state.threads;
|
int thread_size = size / state.threads;
|
||||||
int from = thread_size * state.thread_index;
|
int from = thread_size * state.thread_index;
|
||||||
int to = from + thread_size;
|
int to = from + thread_size;
|
||||||
@@ -179,7 +179,7 @@ BENCHMARK(BM_ParallelMemset)->Arg(10 << 20)->ThreadRange(1, 4);
|
|||||||
|
|
||||||
static void BM_ManualTiming(benchmark::State& state) {
|
static void BM_ManualTiming(benchmark::State& state) {
|
||||||
size_t slept_for = 0;
|
size_t slept_for = 0;
|
||||||
int microseconds = state.range_x();
|
int microseconds = state.range(0);
|
||||||
std::chrono::duration<double, std::micro> sleep_duration {
|
std::chrono::duration<double, std::micro> sleep_duration {
|
||||||
static_cast<double>(microseconds)
|
static_cast<double>(microseconds)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,18 +36,27 @@ struct TestCase {
|
|||||||
CHECK(err_str.empty()) << "Could not construct regex \"" << regex << "\""
|
CHECK(err_str.empty()) << "Could not construct regex \"" << regex << "\""
|
||||||
<< " got Error: " << err_str;
|
<< " got Error: " << err_str;
|
||||||
|
|
||||||
|
std::string near = "<EOF>";
|
||||||
std::string line;
|
std::string line;
|
||||||
|
bool first = true;
|
||||||
while (remaining_output.eof() == false) {
|
while (remaining_output.eof() == false) {
|
||||||
CHECK(remaining_output.good());
|
CHECK(remaining_output.good());
|
||||||
std::getline(remaining_output, line);
|
std::getline(remaining_output, line);
|
||||||
|
// Keep the first line as context.
|
||||||
|
if (first) {
|
||||||
|
near = line;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
if (r.Match(line)) return;
|
if (r.Match(line)) return;
|
||||||
CHECK(match_rule != MR_Next) << "Expected line \"" << line
|
CHECK(match_rule != MR_Next) << "Expected line \"" << line
|
||||||
<< "\" to match regex \"" << regex << "\"";
|
<< "\" to match regex \"" << regex << "\""
|
||||||
|
<< "\nstarted matching at line: \"" << near << "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK(remaining_output.eof() == false)
|
CHECK(remaining_output.eof() == false)
|
||||||
<< "End of output reached before match for regex \"" << regex
|
<< "End of output reached before match for regex \"" << regex
|
||||||
<< "\" was found";
|
<< "\" was found"
|
||||||
|
<< "\nstarted matching at line: \"" << near << "\"";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,7 +121,7 @@ std::string join(First f, Args&&... args) {
|
|||||||
return std::string(std::move(f)) + "[ ]+" + join(std::forward<Args>(args)...);
|
return std::string(std::move(f)) + "[ ]+" + join(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string dec_re = "[0-9]+\\.[0-9]+";
|
std::string dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
|
||||||
|
|
||||||
#define ADD_COMPLEXITY_CASES(...) \
|
#define ADD_COMPLEXITY_CASES(...) \
|
||||||
int CONCAT(dummy, __LINE__) = AddComplexityTest(__VA_ARGS__)
|
int CONCAT(dummy, __LINE__) = AddComplexityTest(__VA_ARGS__)
|
||||||
@@ -138,7 +147,7 @@ int AddComplexityTest(std::vector<TestCase>* console_out, std::vector<TestCase>*
|
|||||||
});
|
});
|
||||||
AddCases(csv_out, {
|
AddCases(csv_out, {
|
||||||
{"^\"" + big_o_test_name + "\",," + dec_re + "," + dec_re + "," + big_o + ",,,,,$"},
|
{"^\"" + big_o_test_name + "\",," + dec_re + "," + dec_re + "," + big_o + ",,,,,$"},
|
||||||
{"^\"" + rms_test_name + "\",," + dec_re + "," + dec_re + ",,,,,,$"}
|
{"^\"" + rms_test_name + "\",," + dec_re + "," + dec_re + ",,,,,,$", MR_Next}
|
||||||
});
|
});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -151,12 +160,15 @@ int AddComplexityTest(std::vector<TestCase>* console_out, std::vector<TestCase>*
|
|||||||
|
|
||||||
void BM_Complexity_O1(benchmark::State& state) {
|
void BM_Complexity_O1(benchmark::State& state) {
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
|
for (int i=0; i < 1024; ++i) {
|
||||||
|
benchmark::DoNotOptimize(&i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state.SetComplexityN(state.range_x());
|
state.SetComplexityN(state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity(benchmark::o1);
|
BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity(benchmark::o1);
|
||||||
BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity([](int){return 1.0; });
|
|
||||||
BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity();
|
BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity();
|
||||||
|
BENCHMARK(BM_Complexity_O1) -> Range(1, 1<<18) -> Complexity([](int){return 1.0; });
|
||||||
|
|
||||||
const char* big_o_1_test_name = "BM_Complexity_O1_BigO";
|
const char* big_o_1_test_name = "BM_Complexity_O1_BigO";
|
||||||
const char* rms_o_1_test_name = "BM_Complexity_O1_RMS";
|
const char* rms_o_1_test_name = "BM_Complexity_O1_RMS";
|
||||||
@@ -167,6 +179,10 @@ const char* lambda_big_o_1 = "f\\(N\\)";
|
|||||||
ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
||||||
big_o_1_test_name, rms_o_1_test_name, enum_auto_big_o_1);
|
big_o_1_test_name, rms_o_1_test_name, enum_auto_big_o_1);
|
||||||
|
|
||||||
|
// Add auto enum tests
|
||||||
|
ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
||||||
|
big_o_1_test_name, rms_o_1_test_name, enum_auto_big_o_1);
|
||||||
|
|
||||||
// Add lambda tests
|
// Add lambda tests
|
||||||
ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
||||||
big_o_1_test_name, rms_o_1_test_name, lambda_big_o_1);
|
big_o_1_test_name, rms_o_1_test_name, lambda_big_o_1);
|
||||||
@@ -185,12 +201,12 @@ std::vector<int> ConstructRandomVector(int size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BM_Complexity_O_N(benchmark::State& state) {
|
void BM_Complexity_O_N(benchmark::State& state) {
|
||||||
auto v = ConstructRandomVector(state.range_x());
|
auto v = ConstructRandomVector(state.range(0));
|
||||||
const int item_not_in_vector = state.range_x()*2; // Test worst case scenario (item not in vector)
|
const int item_not_in_vector = state.range(0)*2; // Test worst case scenario (item not in vector)
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
benchmark::DoNotOptimize(std::find(v.begin(), v.end(), item_not_in_vector));
|
benchmark::DoNotOptimize(std::find(v.begin(), v.end(), item_not_in_vector));
|
||||||
}
|
}
|
||||||
state.SetComplexityN(state.range_x());
|
state.SetComplexityN(state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity(benchmark::oN);
|
BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity(benchmark::oN);
|
||||||
BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity([](int n) -> double{return n; });
|
BENCHMARK(BM_Complexity_O_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity([](int n) -> double{return n; });
|
||||||
@@ -214,11 +230,11 @@ ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
|||||||
// ========================================================================= //
|
// ========================================================================= //
|
||||||
|
|
||||||
static void BM_Complexity_O_N_log_N(benchmark::State& state) {
|
static void BM_Complexity_O_N_log_N(benchmark::State& state) {
|
||||||
auto v = ConstructRandomVector(state.range_x());
|
auto v = ConstructRandomVector(state.range(0));
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
std::sort(v.begin(), v.end());
|
std::sort(v.begin(), v.end());
|
||||||
}
|
}
|
||||||
state.SetComplexityN(state.range_x());
|
state.SetComplexityN(state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity(benchmark::oNLogN);
|
BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity(benchmark::oNLogN);
|
||||||
BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity([](int n) {return n * std::log2(n); });
|
BENCHMARK(BM_Complexity_O_N_log_N) -> RangeMultiplier(2) -> Range(1<<10, 1<<16) -> Complexity([](int n) {return n * std::log2(n); });
|
||||||
@@ -244,14 +260,8 @@ ADD_COMPLEXITY_CASES(&ConsoleOutputTests, &JSONOutputTests, &CSVOutputTests,
|
|||||||
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
// Add --color_print=false to argv since we don't want to match color codes.
|
benchmark::Initialize(&argc, argv);
|
||||||
char new_arg[64];
|
benchmark::ConsoleReporter CR(benchmark::ConsoleReporter::OO_None);
|
||||||
char* new_argv[64];
|
|
||||||
std::copy(argv, argv + argc, new_argv);
|
|
||||||
new_argv[argc++] = std::strcpy(new_arg, "--color_print=false");
|
|
||||||
benchmark::Initialize(&argc, new_argv);
|
|
||||||
|
|
||||||
benchmark::ConsoleReporter CR;
|
|
||||||
benchmark::JSONReporter JR;
|
benchmark::JSONReporter JR;
|
||||||
benchmark::CSVReporter CSVR;
|
benchmark::CSVReporter CSVR;
|
||||||
struct ReporterTest {
|
struct ReporterTest {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ BENCHMARK_DEFINE_F(MyFixture, Bar)(benchmark::State& st) {
|
|||||||
assert(data.get() != nullptr);
|
assert(data.get() != nullptr);
|
||||||
assert(*data == 42);
|
assert(*data == 42);
|
||||||
}
|
}
|
||||||
st.SetItemsProcessed(st.range_x());
|
st.SetItemsProcessed(st.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK_REGISTER_F(MyFixture, Bar)->Arg(42);
|
BENCHMARK_REGISTER_F(MyFixture, Bar)->Arg(42);
|
||||||
BENCHMARK_REGISTER_F(MyFixture, Bar)->Arg(42)->ThreadPerCpu();
|
BENCHMARK_REGISTER_F(MyFixture, Bar)->Arg(42)->ThreadPerCpu();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ std::map<int, int> ConstructRandomMap(int size) {
|
|||||||
|
|
||||||
// Basic version.
|
// Basic version.
|
||||||
static void BM_MapLookup(benchmark::State& state) {
|
static void BM_MapLookup(benchmark::State& state) {
|
||||||
const int size = state.range_x();
|
const int size = state.range(0);
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
std::map<int, int> m = ConstructRandomMap(size);
|
std::map<int, int> m = ConstructRandomMap(size);
|
||||||
@@ -34,7 +34,7 @@ BENCHMARK(BM_MapLookup)->Range(1 << 3, 1 << 12);
|
|||||||
class MapFixture : public ::benchmark::Fixture {
|
class MapFixture : public ::benchmark::Fixture {
|
||||||
public:
|
public:
|
||||||
void SetUp(const ::benchmark::State& st) {
|
void SetUp(const ::benchmark::State& st) {
|
||||||
m = ConstructRandomMap(st.range_x());
|
m = ConstructRandomMap(st.range(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown(const ::benchmark::State&) {
|
void TearDown(const ::benchmark::State&) {
|
||||||
@@ -45,7 +45,7 @@ class MapFixture : public ::benchmark::Fixture {
|
|||||||
};
|
};
|
||||||
|
|
||||||
BENCHMARK_DEFINE_F(MapFixture, Lookup)(benchmark::State& state) {
|
BENCHMARK_DEFINE_F(MapFixture, Lookup)(benchmark::State& state) {
|
||||||
const int size = state.range_x();
|
const int size = state.range(0);
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
benchmark::DoNotOptimize(m.find(rand() % size));
|
benchmark::DoNotOptimize(m.find(rand() % size));
|
||||||
|
|||||||
46
utils/google-benchmark/test/multiple_ranges_test.cc
Normal file
46
utils/google-benchmark/test/multiple_ranges_test.cc
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
class MultipleRangesFixture : public ::benchmark::Fixture {
|
||||||
|
public:
|
||||||
|
MultipleRangesFixture()
|
||||||
|
: expectedValues({
|
||||||
|
{1, 3, 5}, {1, 3, 8}, {1, 3, 15}, {2, 3, 5}, {2, 3, 8}, {2, 3, 15},
|
||||||
|
{1, 4, 5}, {1, 4, 8}, {1, 4, 15}, {2, 4, 5}, {2, 4, 8}, {2, 4, 15},
|
||||||
|
{1, 7, 5}, {1, 7, 8}, {1, 7, 15}, {2, 7, 5}, {2, 7, 8}, {2, 7, 15},
|
||||||
|
{7, 6, 3}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp(const ::benchmark::State& state) {
|
||||||
|
std::vector<int> ranges = {state.range(0), state.range(1), state.range(2)};
|
||||||
|
|
||||||
|
assert(expectedValues.find(ranges) != expectedValues.end());
|
||||||
|
|
||||||
|
actualValues.insert(ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~MultipleRangesFixture() {
|
||||||
|
assert(actualValues.size() == expectedValues.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::vector<int>> expectedValues;
|
||||||
|
std::set<std::vector<int>> actualValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
BENCHMARK_DEFINE_F(MultipleRangesFixture, Empty)(benchmark::State& state) {
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
int product = state.range(0) * state.range(1) * state.range(2);
|
||||||
|
for (int x = 0; x < product; x++) {
|
||||||
|
benchmark::DoNotOptimize(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_REGISTER_F(MultipleRangesFixture, Empty)->RangeMultiplier(2)->Ranges({{1, 2}, {3, 7}, {5, 15}})->Args({7, 6, 3});
|
||||||
|
|
||||||
|
BENCHMARK_MAIN()
|
||||||
@@ -9,7 +9,7 @@ void BM_basic(benchmark::State& state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BM_basic_slow(benchmark::State& state) {
|
void BM_basic_slow(benchmark::State& state) {
|
||||||
std::chrono::milliseconds sleep_duration(state.range_x());
|
std::chrono::milliseconds sleep_duration(state.range(0));
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
std::this_thread::sleep_for(
|
std::this_thread::sleep_for(
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration)
|
std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration)
|
||||||
@@ -25,8 +25,8 @@ BENCHMARK(BM_basic_slow)->Arg(1000)->Unit(benchmark::kMillisecond);
|
|||||||
BENCHMARK(BM_basic)->Range(1, 8);
|
BENCHMARK(BM_basic)->Range(1, 8);
|
||||||
BENCHMARK(BM_basic)->RangeMultiplier(2)->Range(1, 8);
|
BENCHMARK(BM_basic)->RangeMultiplier(2)->Range(1, 8);
|
||||||
BENCHMARK(BM_basic)->DenseRange(10, 15);
|
BENCHMARK(BM_basic)->DenseRange(10, 15);
|
||||||
BENCHMARK(BM_basic)->ArgPair(42, 42);
|
BENCHMARK(BM_basic)->Args({42, 42});
|
||||||
BENCHMARK(BM_basic)->RangePair(64, 512, 64, 512);
|
BENCHMARK(BM_basic)->Ranges({{64, 512}, {64, 512}});
|
||||||
BENCHMARK(BM_basic)->MinTime(0.7);
|
BENCHMARK(BM_basic)->MinTime(0.7);
|
||||||
BENCHMARK(BM_basic)->UseRealTime();
|
BENCHMARK(BM_basic)->UseRealTime();
|
||||||
BENCHMARK(BM_basic)->ThreadRange(2, 4);
|
BENCHMARK(BM_basic)->ThreadRange(2, 4);
|
||||||
|
|||||||
149
utils/google-benchmark/test/register_benchmark_test.cc
Normal file
149
utils/google-benchmark/test/register_benchmark_test.cc
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
|
||||||
|
#undef NDEBUG
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "../src/check.h" // NOTE: check.h is for internal use only!
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TestReporter : public benchmark::ConsoleReporter {
|
||||||
|
public:
|
||||||
|
virtual void ReportRuns(const std::vector<Run>& report) {
|
||||||
|
all_runs_.insert(all_runs_.end(), begin(report), end(report));
|
||||||
|
ConsoleReporter::ReportRuns(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Run> all_runs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
std::string name;
|
||||||
|
const char* label;
|
||||||
|
TestCase(const char* xname) : name(xname), label(nullptr) {}
|
||||||
|
TestCase(const char* xname, const char* xlabel)
|
||||||
|
: name(xname), label(xlabel) {}
|
||||||
|
|
||||||
|
typedef benchmark::BenchmarkReporter::Run Run;
|
||||||
|
|
||||||
|
void CheckRun(Run const& run) const {
|
||||||
|
CHECK(name == run.benchmark_name) << "expected " << name
|
||||||
|
<< " got " << run.benchmark_name;
|
||||||
|
if (label) {
|
||||||
|
CHECK(run.report_label == label) << "expected " << label
|
||||||
|
<< " got " << run.report_label;
|
||||||
|
} else {
|
||||||
|
CHECK(run.report_label == "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TestCase> ExpectedResults;
|
||||||
|
|
||||||
|
int AddCases(std::initializer_list<TestCase> const& v) {
|
||||||
|
for (auto N : v) {
|
||||||
|
ExpectedResults.push_back(N);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
|
typedef benchmark::internal::Benchmark* ReturnVal;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------//
|
||||||
|
// Test RegisterBenchmark with no additional arguments
|
||||||
|
//----------------------------------------------------------------------------//
|
||||||
|
void BM_function(benchmark::State& state) { while (state.KeepRunning()) {} }
|
||||||
|
BENCHMARK(BM_function);
|
||||||
|
ReturnVal dummy = benchmark::RegisterBenchmark(
|
||||||
|
"BM_function_manual_registration",
|
||||||
|
BM_function);
|
||||||
|
ADD_CASES({"BM_function"}, {"BM_function_manual_registration"});
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------//
|
||||||
|
// Test RegisterBenchmark with additional arguments
|
||||||
|
// Note: GCC <= 4.8 do not support this form of RegisterBenchmark because they
|
||||||
|
// reject the variadic pack expansion of lambda captures.
|
||||||
|
//----------------------------------------------------------------------------//
|
||||||
|
#ifndef BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||||
|
|
||||||
|
void BM_extra_args(benchmark::State& st, const char* label) {
|
||||||
|
while (st.KeepRunning()) {}
|
||||||
|
st.SetLabel(label);
|
||||||
|
}
|
||||||
|
int RegisterFromFunction() {
|
||||||
|
std::pair<const char*, const char*> cases[] = {
|
||||||
|
{"test1", "One"},
|
||||||
|
{"test2", "Two"},
|
||||||
|
{"test3", "Three"}
|
||||||
|
};
|
||||||
|
for (auto& c : cases)
|
||||||
|
benchmark::RegisterBenchmark(c.first, &BM_extra_args, c.second);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int dummy2 = RegisterFromFunction();
|
||||||
|
ADD_CASES(
|
||||||
|
{"test1", "One"},
|
||||||
|
{"test2", "Two"},
|
||||||
|
{"test3", "Three"}
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------//
|
||||||
|
// Test RegisterBenchmark with different callable types
|
||||||
|
//----------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
struct CustomFixture {
|
||||||
|
void operator()(benchmark::State& st) {
|
||||||
|
while (st.KeepRunning()) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestRegistrationAtRuntime() {
|
||||||
|
#ifdef BENCHMARK_HAS_CXX11
|
||||||
|
{
|
||||||
|
CustomFixture fx;
|
||||||
|
benchmark::RegisterBenchmark("custom_fixture", fx);
|
||||||
|
AddCases({"custom_fixture"});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||||
|
{
|
||||||
|
int x = 42;
|
||||||
|
auto capturing_lam = [=](benchmark::State& st) {
|
||||||
|
while (st.KeepRunning()) {}
|
||||||
|
st.SetLabel(std::to_string(x));
|
||||||
|
};
|
||||||
|
benchmark::RegisterBenchmark("lambda_benchmark", capturing_lam);
|
||||||
|
AddCases({{"lambda_benchmark", "42"}});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
TestRegistrationAtRuntime();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -114,7 +114,9 @@ std::string join(First f, Args&&... args) {
|
|||||||
return std::string(std::move(f)) + "[ ]+" + join(std::forward<Args>(args)...);
|
return std::string(std::move(f)) + "[ ]+" + join(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string dec_re = "[0-9]+\\.[0-9]+";
|
|
||||||
|
|
||||||
|
std::string dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
||||||
@@ -185,7 +187,7 @@ ADD_CASES(&CSVOutputTests, {
|
|||||||
void BM_Complexity_O1(benchmark::State& state) {
|
void BM_Complexity_O1(benchmark::State& state) {
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
}
|
}
|
||||||
state.SetComplexityN(state.range_x());
|
state.SetComplexityN(state.range(0));
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Complexity_O1)->Range(1, 1<<18)->Complexity(benchmark::o1);
|
BENCHMARK(BM_Complexity_O1)->Range(1, 1<<18)->Complexity(benchmark::o1);
|
||||||
|
|
||||||
@@ -203,14 +205,8 @@ ADD_CASES(&ConsoleOutputTests, {
|
|||||||
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
// Add --color_print=false to argv since we don't want to match color codes.
|
benchmark::Initialize(&argc, argv);
|
||||||
char new_arg[64];
|
benchmark::ConsoleReporter CR(benchmark::ConsoleReporter::OO_None);
|
||||||
char* new_argv[64];
|
|
||||||
std::copy(argv, argv + argc, new_argv);
|
|
||||||
new_argv[argc++] = std::strcpy(new_arg, "--color_print=false");
|
|
||||||
benchmark::Initialize(&argc, new_argv);
|
|
||||||
|
|
||||||
benchmark::ConsoleReporter CR;
|
|
||||||
benchmark::JSONReporter JR;
|
benchmark::JSONReporter JR;
|
||||||
benchmark::CSVReporter CSVR;
|
benchmark::CSVReporter CSVR;
|
||||||
struct ReporterTest {
|
struct ReporterTest {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ ADD_CASES("BM_error_before_running",
|
|||||||
void BM_error_during_running(benchmark::State& state) {
|
void BM_error_during_running(benchmark::State& state) {
|
||||||
int first_iter = true;
|
int first_iter = true;
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
if (state.range_x() == 1 && state.thread_index <= (state.threads / 2)) {
|
if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
|
||||||
assert(first_iter);
|
assert(first_iter);
|
||||||
first_iter = false;
|
first_iter = false;
|
||||||
state.SkipWithError("error message");
|
state.SkipWithError("error message");
|
||||||
@@ -116,7 +116,7 @@ ADD_CASES(
|
|||||||
void BM_error_while_paused(benchmark::State& state) {
|
void BM_error_while_paused(benchmark::State& state) {
|
||||||
bool first_iter = true;
|
bool first_iter = true;
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
if (state.range_x() == 1 && state.thread_index <= (state.threads / 2)) {
|
if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
|
||||||
assert(first_iter);
|
assert(first_iter);
|
||||||
first_iter = false;
|
first_iter = false;
|
||||||
state.PauseTiming();
|
state.PauseTiming();
|
||||||
|
|||||||
30
utils/google-benchmark/tools/compare_bench.py
Normal file
30
utils/google-benchmark/tools/compare_bench.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
compare_bench.py - Compare two benchmarks or their results and report the
|
||||||
|
difference.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import gbench
|
||||||
|
from gbench import util, report
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse the command line flags
|
||||||
|
def usage():
|
||||||
|
print('compare_bench.py <test1> <test2> [benchmark options]...')
|
||||||
|
exit(1)
|
||||||
|
if '--help' in sys.argv or len(sys.argv) < 3:
|
||||||
|
usage()
|
||||||
|
tests = sys.argv[1:3]
|
||||||
|
bench_opts = sys.argv[3:]
|
||||||
|
bench_opts = list(bench_opts)
|
||||||
|
# Run the benchmarks and report the results
|
||||||
|
json1 = gbench.util.run_or_load_benchmark(tests[0], bench_opts)
|
||||||
|
json2 = gbench.util.run_or_load_benchmark(tests[1], bench_opts)
|
||||||
|
output_lines = gbench.report.generate_difference_report(json1, json2)
|
||||||
|
print 'Comparing %s to %s' % (tests[0], tests[1])
|
||||||
|
for ln in output_lines:
|
||||||
|
print(ln)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
46
utils/google-benchmark/tools/gbench/Inputs/test1_run1.json
Normal file
46
utils/google-benchmark/tools/gbench/Inputs/test1_run1.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"date": "2016-08-02 17:44:46",
|
||||||
|
"num_cpus": 4,
|
||||||
|
"mhz_per_cpu": 4228,
|
||||||
|
"cpu_scaling_enabled": false,
|
||||||
|
"library_build_type": "release"
|
||||||
|
},
|
||||||
|
"benchmarks": [
|
||||||
|
{
|
||||||
|
"name": "BM_SameTimes",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 10,
|
||||||
|
"cpu_time": 10,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_2xFaster",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 50,
|
||||||
|
"cpu_time": 50,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_2xSlower",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 50,
|
||||||
|
"cpu_time": 50,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_10PercentFaster",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 100,
|
||||||
|
"cpu_time": 100,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_10PercentSlower",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 100,
|
||||||
|
"cpu_time": 100,
|
||||||
|
"time_unit": "ns"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
46
utils/google-benchmark/tools/gbench/Inputs/test1_run2.json
Normal file
46
utils/google-benchmark/tools/gbench/Inputs/test1_run2.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"date": "2016-08-02 17:44:46",
|
||||||
|
"num_cpus": 4,
|
||||||
|
"mhz_per_cpu": 4228,
|
||||||
|
"cpu_scaling_enabled": false,
|
||||||
|
"library_build_type": "release"
|
||||||
|
},
|
||||||
|
"benchmarks": [
|
||||||
|
{
|
||||||
|
"name": "BM_SameTimes",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 10,
|
||||||
|
"cpu_time": 10,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_2xFaster",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 25,
|
||||||
|
"cpu_time": 25,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_2xSlower",
|
||||||
|
"iterations": 20833333,
|
||||||
|
"real_time": 100,
|
||||||
|
"cpu_time": 100,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_10PercentFaster",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 90,
|
||||||
|
"cpu_time": 90,
|
||||||
|
"time_unit": "ns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BM_10PercentSlower",
|
||||||
|
"iterations": 1000,
|
||||||
|
"real_time": 110,
|
||||||
|
"cpu_time": 110,
|
||||||
|
"time_unit": "ns"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
8
utils/google-benchmark/tools/gbench/__init__.py
Normal file
8
utils/google-benchmark/tools/gbench/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
"""Google Benchmark tooling"""
|
||||||
|
|
||||||
|
__author__ = 'Eric Fiselier'
|
||||||
|
__email__ = 'eric@efcs.ca'
|
||||||
|
__versioninfo__ = (0, 5, 0)
|
||||||
|
__version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev'
|
||||||
|
|
||||||
|
__all__ = []
|
||||||
136
utils/google-benchmark/tools/gbench/report.py
Normal file
136
utils/google-benchmark/tools/gbench/report.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
"""report.py - Utilities for reporting statistics about benchmark results
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
class BenchmarkColor(object):
|
||||||
|
def __init__(self, name, code):
|
||||||
|
self.name = name
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s%r' % (self.__class__.__name__,
|
||||||
|
(self.name, self.code))
|
||||||
|
|
||||||
|
def __format__(self, format):
|
||||||
|
return self.code
|
||||||
|
|
||||||
|
# Benchmark Colors Enumeration
|
||||||
|
BC_NONE = BenchmarkColor('NONE', '')
|
||||||
|
BC_MAGENTA = BenchmarkColor('MAGENTA', '\033[95m')
|
||||||
|
BC_CYAN = BenchmarkColor('CYAN', '\033[96m')
|
||||||
|
BC_OKBLUE = BenchmarkColor('OKBLUE', '\033[94m')
|
||||||
|
BC_HEADER = BenchmarkColor('HEADER', '\033[92m')
|
||||||
|
BC_WARNING = BenchmarkColor('WARNING', '\033[93m')
|
||||||
|
BC_WHITE = BenchmarkColor('WHITE', '\033[97m')
|
||||||
|
BC_FAIL = BenchmarkColor('FAIL', '\033[91m')
|
||||||
|
BC_ENDC = BenchmarkColor('ENDC', '\033[0m')
|
||||||
|
BC_BOLD = BenchmarkColor('BOLD', '\033[1m')
|
||||||
|
BC_UNDERLINE = BenchmarkColor('UNDERLINE', '\033[4m')
|
||||||
|
|
||||||
|
def color_format(use_color, fmt_str, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Return the result of 'fmt_str.format(*args, **kwargs)' after transforming
|
||||||
|
'args' and 'kwargs' according to the value of 'use_color'. If 'use_color'
|
||||||
|
is False then all color codes in 'args' and 'kwargs' are replaced with
|
||||||
|
the empty string.
|
||||||
|
"""
|
||||||
|
assert use_color is True or use_color is False
|
||||||
|
if not use_color:
|
||||||
|
args = [arg if not isinstance(arg, BenchmarkColor) else BC_NONE
|
||||||
|
for arg in args]
|
||||||
|
kwargs = {key: arg if not isinstance(arg, BenchmarkColor) else BC_NONE
|
||||||
|
for key, arg in kwargs.items()}
|
||||||
|
return fmt_str.format(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def find_longest_name(benchmark_list):
|
||||||
|
"""
|
||||||
|
Return the length of the longest benchmark name in a given list of
|
||||||
|
benchmark JSON objects
|
||||||
|
"""
|
||||||
|
longest_name = 1
|
||||||
|
for bc in benchmark_list:
|
||||||
|
if len(bc['name']) > longest_name:
|
||||||
|
longest_name = len(bc['name'])
|
||||||
|
return longest_name
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_change(old_val, new_val):
|
||||||
|
"""
|
||||||
|
Return a float representing the decimal change between old_val and new_val.
|
||||||
|
"""
|
||||||
|
return float(new_val - old_val) / abs(old_val)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_difference_report(json1, json2, use_color=True):
|
||||||
|
"""
|
||||||
|
Calculate and report the difference between each test of two benchmarks
|
||||||
|
runs specified as 'json1' and 'json2'.
|
||||||
|
"""
|
||||||
|
first_col_width = find_longest_name(json1['benchmarks']) + 5
|
||||||
|
def find_test(name):
|
||||||
|
for b in json2['benchmarks']:
|
||||||
|
if b['name'] == name:
|
||||||
|
return b
|
||||||
|
return None
|
||||||
|
first_line = "{:<{}s} Time CPU".format(
|
||||||
|
'Benchmark', first_col_width)
|
||||||
|
output_strs = [first_line, '-' * len(first_line)]
|
||||||
|
for bn in json1['benchmarks']:
|
||||||
|
other_bench = find_test(bn['name'])
|
||||||
|
if not other_bench:
|
||||||
|
continue
|
||||||
|
|
||||||
|
def get_color(res):
|
||||||
|
if res > 0.05:
|
||||||
|
return BC_FAIL
|
||||||
|
elif res > -0.07:
|
||||||
|
return BC_WHITE
|
||||||
|
else:
|
||||||
|
return BC_CYAN
|
||||||
|
fmt_str = "{}{:<{}s}{endc} {}{:+.2f}{endc} {}{:+.2f}{endc}"
|
||||||
|
tres = calculate_change(bn['real_time'], other_bench['real_time'])
|
||||||
|
cpures = calculate_change(bn['cpu_time'], other_bench['cpu_time'])
|
||||||
|
output_strs += [color_format(use_color, fmt_str,
|
||||||
|
BC_HEADER, bn['name'], first_col_width,
|
||||||
|
get_color(tres), tres, get_color(cpures), cpures,
|
||||||
|
endc=BC_ENDC)]
|
||||||
|
return output_strs
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Unit tests
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestReportDifference(unittest.TestCase):
|
||||||
|
def load_results(self):
|
||||||
|
import json
|
||||||
|
testInputs = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Inputs')
|
||||||
|
testOutput1 = os.path.join(testInputs, 'test1_run1.json')
|
||||||
|
testOutput2 = os.path.join(testInputs, 'test1_run2.json')
|
||||||
|
with open(testOutput1, 'r') as f:
|
||||||
|
json1 = json.load(f)
|
||||||
|
with open(testOutput2, 'r') as f:
|
||||||
|
json2 = json.load(f)
|
||||||
|
return json1, json2
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
expect_lines = [
|
||||||
|
['BM_SameTimes', '+0.00', '+0.00'],
|
||||||
|
['BM_2xFaster', '-0.50', '-0.50'],
|
||||||
|
['BM_2xSlower', '+1.00', '+1.00'],
|
||||||
|
['BM_10PercentFaster', '-0.10', '-0.10'],
|
||||||
|
['BM_10PercentSlower', '+0.10', '+0.10']
|
||||||
|
]
|
||||||
|
json1, json2 = self.load_results()
|
||||||
|
output_lines = generate_difference_report(json1, json2, use_color=False)
|
||||||
|
print output_lines
|
||||||
|
self.assertEqual(len(output_lines), len(expect_lines))
|
||||||
|
for i in xrange(0, len(output_lines)):
|
||||||
|
parts = [x for x in output_lines[i].split(' ') if x]
|
||||||
|
self.assertEqual(len(parts), 3)
|
||||||
|
self.assertEqual(parts, expect_lines[i])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
130
utils/google-benchmark/tools/gbench/util.py
Normal file
130
utils/google-benchmark/tools/gbench/util.py
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
"""util.py - General utilities for running, loading, and processing benchmarks
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Input file type enumeration
|
||||||
|
IT_Invalid = 0
|
||||||
|
IT_JSON = 1
|
||||||
|
IT_Executable = 2
|
||||||
|
|
||||||
|
_num_magic_bytes = 2 if sys.platform.startswith('win') else 4
|
||||||
|
def is_executable_file(filename):
|
||||||
|
"""
|
||||||
|
Return 'True' if 'filename' names a valid file which is likely
|
||||||
|
an executable. A file is considered an executable if it starts with the
|
||||||
|
magic bytes for a EXE, Mach O, or ELF file.
|
||||||
|
"""
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return False
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
magic_bytes = f.read(_num_magic_bytes)
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
return magic_bytes in [
|
||||||
|
'\xfe\xed\xfa\xce', # MH_MAGIC
|
||||||
|
'\xce\xfa\xed\xfe', # MH_CIGAM
|
||||||
|
'\xfe\xed\xfa\xcf', # MH_MAGIC_64
|
||||||
|
'\xcf\xfa\xed\xfe', # MH_CIGAM_64
|
||||||
|
'\xca\xfe\xba\xbe', # FAT_MAGIC
|
||||||
|
'\xbe\xba\xfe\xca' # FAT_CIGAM
|
||||||
|
]
|
||||||
|
elif sys.platform.startswith('win'):
|
||||||
|
return magic_bytes == 'MZ'
|
||||||
|
else:
|
||||||
|
return magic_bytes == '\x7FELF'
|
||||||
|
|
||||||
|
|
||||||
|
def is_json_file(filename):
|
||||||
|
"""
|
||||||
|
Returns 'True' if 'filename' names a valid JSON output file.
|
||||||
|
'False' otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
json.load(f)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def classify_input_file(filename):
|
||||||
|
"""
|
||||||
|
Return a tuple (type, msg) where 'type' specifies the classified type
|
||||||
|
of 'filename'. If 'type' is 'IT_Invalid' then 'msg' is a human readable
|
||||||
|
string represeting the error.
|
||||||
|
"""
|
||||||
|
ftype = IT_Invalid
|
||||||
|
err_msg = None
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
err_msg = "'%s' does not exist" % filename
|
||||||
|
elif not os.path.isfile(filename):
|
||||||
|
err_msg = "'%s' does not name a file" % filename
|
||||||
|
elif is_executable_file(filename):
|
||||||
|
ftype = IT_Executable
|
||||||
|
elif is_json_file(filename):
|
||||||
|
ftype = IT_JSON
|
||||||
|
else:
|
||||||
|
err_msg = "'%s' does not name a valid benchmark executable or JSON file"
|
||||||
|
return ftype, err_msg
|
||||||
|
|
||||||
|
|
||||||
|
def check_input_file(filename):
|
||||||
|
"""
|
||||||
|
Classify the file named by 'filename' and return the classification.
|
||||||
|
If the file is classified as 'IT_Invalid' print an error message and exit
|
||||||
|
the program.
|
||||||
|
"""
|
||||||
|
ftype, msg = classify_input_file(filename)
|
||||||
|
if ftype == IT_Invalid:
|
||||||
|
print "Invalid input file: %s" % msg
|
||||||
|
sys.exit(1)
|
||||||
|
return ftype
|
||||||
|
|
||||||
|
|
||||||
|
def load_benchmark_results(fname):
|
||||||
|
"""
|
||||||
|
Read benchmark output from a file and return the JSON object.
|
||||||
|
REQUIRES: 'fname' names a file containing JSON benchmark output.
|
||||||
|
"""
|
||||||
|
with open(fname, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def run_benchmark(exe_name, benchmark_flags):
|
||||||
|
"""
|
||||||
|
Run a benchmark specified by 'exe_name' with the specified
|
||||||
|
'benchmark_flags'. The benchmark is run directly as a subprocess to preserve
|
||||||
|
real time console output.
|
||||||
|
RETURNS: A JSON object representing the benchmark output
|
||||||
|
"""
|
||||||
|
thandle, tname = tempfile.mkstemp()
|
||||||
|
os.close(thandle)
|
||||||
|
cmd = [exe_name] + benchmark_flags
|
||||||
|
print("RUNNING: %s" % ' '.join(cmd))
|
||||||
|
exitCode = subprocess.call(cmd + ['--benchmark_out=%s' % tname])
|
||||||
|
if exitCode != 0:
|
||||||
|
print('TEST FAILED...')
|
||||||
|
sys.exit(exitCode)
|
||||||
|
json_res = load_benchmark_results(tname)
|
||||||
|
os.unlink(tname)
|
||||||
|
return json_res
|
||||||
|
|
||||||
|
|
||||||
|
def run_or_load_benchmark(filename, benchmark_flags):
|
||||||
|
"""
|
||||||
|
Get the results for a specified benchmark. If 'filename' specifies
|
||||||
|
an executable benchmark then the results are generated by running the
|
||||||
|
benchmark. Otherwise 'filename' must name a valid JSON output file,
|
||||||
|
which is loaded and the result returned.
|
||||||
|
"""
|
||||||
|
ftype = check_input_file(filename)
|
||||||
|
if ftype == IT_JSON:
|
||||||
|
return load_benchmark_results(filename)
|
||||||
|
elif ftype == IT_Executable:
|
||||||
|
return run_benchmark(filename, benchmark_flags)
|
||||||
|
else:
|
||||||
|
assert False # This branch is unreachable
|
||||||
Reference in New Issue
Block a user