mirror of
https://github.com/google/benchmark.git
synced 2025-03-22 23:20:29 +08:00
Custom user counters: add invert modifier. (#850)
While current counters can e.g. answer the question "how many items is processed per second", it is impossible to get it to tell "how many seconds it takes to process a single item". The solution is to add a yet another modifier `kInvert`, that is *always* considered last, which simply inverts the answer. Fixes #781, #830, #848.
This commit is contained in:
parent
c408461983
commit
7d97a057e1
12
README.md
12
README.md
@ -662,9 +662,9 @@ the resulting sum is the value which will be shown for the benchmark.
|
|||||||
|
|
||||||
The `Counter` constructor accepts three parameters: the value as a `double`
|
The `Counter` constructor accepts three parameters: the value as a `double`
|
||||||
; a bit flag which allows you to show counters as rates, and/or as per-thread
|
; a bit flag which allows you to show counters as rates, and/or as per-thread
|
||||||
iteration, and/or as per-thread averages, and/or iteration invariants;
|
iteration, and/or as per-thread averages, and/or iteration invariants,
|
||||||
and a flag specifying the 'unit' - i.e. is 1k a 1000 (default,
|
and/or finally inverting the result; and a flag specifying the 'unit' - i.e.
|
||||||
`benchmark::Counter::OneK::kIs1000`), or 1024
|
is 1k a 1000 (default, `benchmark::Counter::OneK::kIs1000`), or 1024
|
||||||
(`benchmark::Counter::OneK::kIs1024`)?
|
(`benchmark::Counter::OneK::kIs1024`)?
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
@ -673,8 +673,14 @@ and a flag specifying the 'unit' - i.e. is 1k a 1000 (default,
|
|||||||
|
|
||||||
// Set the counter as a rate. It will be presented divided
|
// Set the counter as a rate. It will be presented divided
|
||||||
// by the duration of the benchmark.
|
// by the duration of the benchmark.
|
||||||
|
// Meaning: per one second, how many 'foo's are processed?
|
||||||
state.counters["FooRate"] = Counter(numFoos, benchmark::Counter::kIsRate);
|
state.counters["FooRate"] = Counter(numFoos, benchmark::Counter::kIsRate);
|
||||||
|
|
||||||
|
// Set the counter as a rate. It will be presented divided
|
||||||
|
// by the duration of the benchmark, and the result inverted.
|
||||||
|
// Meaning: how many seconds it takes to process one 'foo'?
|
||||||
|
state.counters["FooInvRate"] = Counter(numFoos, benchmark::Counter::kIsRate | benchmark::Counter::kInvert);
|
||||||
|
|
||||||
// Set the counter as a thread-average quantity. It will
|
// Set the counter as a thread-average quantity. It will
|
||||||
// be presented divided by the number of threads.
|
// be presented divided by the number of threads.
|
||||||
state.counters["FooAvg"] = Counter(numFoos, benchmark::Counter::kAvgThreads);
|
state.counters["FooAvg"] = Counter(numFoos, benchmark::Counter::kAvgThreads);
|
||||||
|
@ -368,7 +368,10 @@ class Counter {
|
|||||||
// It will be presented divided by the number of iterations.
|
// It will be presented divided by the number of iterations.
|
||||||
kAvgIterations = 1U << 3U,
|
kAvgIterations = 1U << 3U,
|
||||||
// Mark the counter as a iteration-average rate. See above.
|
// Mark the counter as a iteration-average rate. See above.
|
||||||
kAvgIterationsRate = kIsRate | kAvgIterations
|
kAvgIterationsRate = kIsRate | kAvgIterations,
|
||||||
|
|
||||||
|
// In the end, invert the result. This is always done last!
|
||||||
|
kInvert = 1U << 31U
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OneK {
|
enum OneK {
|
||||||
|
@ -12,21 +12,21 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "benchmark/benchmark.h"
|
|
||||||
#include "complexity.h"
|
|
||||||
#include "counter.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "colorprint.h"
|
#include "colorprint.h"
|
||||||
#include "commandlineflags.h"
|
#include "commandlineflags.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#include "counter.h"
|
||||||
#include "internal_macros.h"
|
#include "internal_macros.h"
|
||||||
#include "string_util.h"
|
#include "string_util.h"
|
||||||
#include "timers.h"
|
#include "timers.h"
|
||||||
@ -156,16 +156,14 @@ void ConsoleReporter::PrintRunData(const Run& result) {
|
|||||||
const std::size_t cNameLen = std::max(std::string::size_type(10),
|
const std::size_t cNameLen = std::max(std::string::size_type(10),
|
||||||
c.first.length());
|
c.first.length());
|
||||||
auto const& s = HumanReadableNumber(c.second.value, c.second.oneK);
|
auto const& s = HumanReadableNumber(c.second.value, c.second.oneK);
|
||||||
|
const char* unit = "";
|
||||||
|
if (c.second.flags & Counter::kIsRate)
|
||||||
|
unit = (c.second.flags & Counter::kInvert) ? "s" : "/s";
|
||||||
if (output_options_ & OO_Tabular) {
|
if (output_options_ & OO_Tabular) {
|
||||||
if (c.second.flags & Counter::kIsRate) {
|
printer(Out, COLOR_DEFAULT, " %*s%s", cNameLen - strlen(unit), s.c_str(),
|
||||||
printer(Out, COLOR_DEFAULT, " %*s/s", cNameLen - 2, s.c_str());
|
|
||||||
} else {
|
|
||||||
printer(Out, COLOR_DEFAULT, " %*s", cNameLen, s.c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const char* unit = (c.second.flags & Counter::kIsRate) ? "/s" : "";
|
|
||||||
printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(),
|
|
||||||
unit);
|
unit);
|
||||||
|
} else {
|
||||||
|
printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,10 @@ double Finish(Counter const& c, IterationCount iterations, double cpu_time,
|
|||||||
if (c.flags & Counter::kAvgIterations) {
|
if (c.flags & Counter::kAvgIterations) {
|
||||||
v /= iterations;
|
v /= iterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c.flags & Counter::kInvert) { // Invert is *always* last.
|
||||||
|
v = 1.0 / v;
|
||||||
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +148,89 @@ void CheckRate(Results const& e) {
|
|||||||
}
|
}
|
||||||
CHECK_BENCHMARK_RESULTS("BM_Counters_Rate", &CheckRate);
|
CHECK_BENCHMARK_RESULTS("BM_Counters_Rate", &CheckRate);
|
||||||
|
|
||||||
|
// ========================================================================= //
|
||||||
|
// ----------------------- Inverted Counters Output ------------------------ //
|
||||||
|
// ========================================================================= //
|
||||||
|
|
||||||
|
void BM_Invert(benchmark::State& state) {
|
||||||
|
for (auto _ : state) {
|
||||||
|
// This test requires a non-zero CPU time to avoid divide-by-zero
|
||||||
|
benchmark::DoNotOptimize(state.iterations());
|
||||||
|
}
|
||||||
|
namespace bm = benchmark;
|
||||||
|
state.counters["foo"] = bm::Counter{0.0001, bm::Counter::kInvert};
|
||||||
|
state.counters["bar"] = bm::Counter{10000, bm::Counter::kInvert};
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_Invert);
|
||||||
|
ADD_CASES(TC_ConsoleOut,
|
||||||
|
{{"^BM_Invert %console_report bar=%hrfloatu foo=%hrfloatk$"}});
|
||||||
|
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Invert\",$"},
|
||||||
|
{"\"run_name\": \"BM_Invert\",$", MR_Next},
|
||||||
|
{"\"run_type\": \"iteration\",$", MR_Next},
|
||||||
|
{"\"repetitions\": 0,$", MR_Next},
|
||||||
|
{"\"repetition_index\": 0,$", MR_Next},
|
||||||
|
{"\"threads\": 1,$", MR_Next},
|
||||||
|
{"\"iterations\": %int,$", MR_Next},
|
||||||
|
{"\"real_time\": %float,$", MR_Next},
|
||||||
|
{"\"cpu_time\": %float,$", MR_Next},
|
||||||
|
{"\"time_unit\": \"ns\",$", MR_Next},
|
||||||
|
{"\"bar\": %float,$", MR_Next},
|
||||||
|
{"\"foo\": %float$", MR_Next},
|
||||||
|
{"}", MR_Next}});
|
||||||
|
ADD_CASES(TC_CSVOut, {{"^\"BM_Invert\",%csv_report,%float,%float$"}});
|
||||||
|
// VS2013 does not allow this function to be passed as a lambda argument
|
||||||
|
// to CHECK_BENCHMARK_RESULTS()
|
||||||
|
void CheckInvert(Results const& e) {
|
||||||
|
CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, 10000, 0.0001);
|
||||||
|
CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 0.0001, 0.0001);
|
||||||
|
}
|
||||||
|
CHECK_BENCHMARK_RESULTS("BM_Invert", &CheckInvert);
|
||||||
|
|
||||||
|
// ========================================================================= //
|
||||||
|
// ------------------------- InvertedRate Counters Output
|
||||||
|
// -------------------------- //
|
||||||
|
// ========================================================================= //
|
||||||
|
|
||||||
|
void BM_Counters_InvertedRate(benchmark::State& state) {
|
||||||
|
for (auto _ : state) {
|
||||||
|
// This test requires a non-zero CPU time to avoid divide-by-zero
|
||||||
|
benchmark::DoNotOptimize(state.iterations());
|
||||||
|
}
|
||||||
|
namespace bm = benchmark;
|
||||||
|
state.counters["foo"] =
|
||||||
|
bm::Counter{1, bm::Counter::kIsRate | bm::Counter::kInvert};
|
||||||
|
state.counters["bar"] =
|
||||||
|
bm::Counter{8192, bm::Counter::kIsRate | bm::Counter::kInvert};
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_Counters_InvertedRate);
|
||||||
|
ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_InvertedRate %console_report "
|
||||||
|
"bar=%hrfloats foo=%hrfloats$"}});
|
||||||
|
ADD_CASES(TC_JSONOut,
|
||||||
|
{{"\"name\": \"BM_Counters_InvertedRate\",$"},
|
||||||
|
{"\"run_name\": \"BM_Counters_InvertedRate\",$", MR_Next},
|
||||||
|
{"\"run_type\": \"iteration\",$", MR_Next},
|
||||||
|
{"\"repetitions\": 0,$", MR_Next},
|
||||||
|
{"\"repetition_index\": 0,$", MR_Next},
|
||||||
|
{"\"threads\": 1,$", MR_Next},
|
||||||
|
{"\"iterations\": %int,$", MR_Next},
|
||||||
|
{"\"real_time\": %float,$", MR_Next},
|
||||||
|
{"\"cpu_time\": %float,$", MR_Next},
|
||||||
|
{"\"time_unit\": \"ns\",$", MR_Next},
|
||||||
|
{"\"bar\": %float,$", MR_Next},
|
||||||
|
{"\"foo\": %float$", MR_Next},
|
||||||
|
{"}", MR_Next}});
|
||||||
|
ADD_CASES(TC_CSVOut,
|
||||||
|
{{"^\"BM_Counters_InvertedRate\",%csv_report,%float,%float$"}});
|
||||||
|
// VS2013 does not allow this function to be passed as a lambda argument
|
||||||
|
// to CHECK_BENCHMARK_RESULTS()
|
||||||
|
void CheckInvertedRate(Results const& e) {
|
||||||
|
double t = e.DurationCPUTime(); // this (and not real time) is the time used
|
||||||
|
// check that the values are within 0.1% of the expected values
|
||||||
|
CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, t, 0.001);
|
||||||
|
CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, t / 8192.0, 0.001);
|
||||||
|
}
|
||||||
|
CHECK_BENCHMARK_RESULTS("BM_Counters_InvertedRate", &CheckInvertedRate);
|
||||||
|
|
||||||
// ========================================================================= //
|
// ========================================================================= //
|
||||||
// ------------------------- Thread Counters Output ------------------------ //
|
// ------------------------- Thread Counters Output ------------------------ //
|
||||||
// ========================================================================= //
|
// ========================================================================= //
|
||||||
|
Loading…
Reference in New Issue
Block a user