1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/types/expected_macros.h"
#include <stddef.h>
#include <string>
#include <utility>
#include "base/compiler_specific.h"
#include "base/types/expected.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/google_benchmark/src/include/benchmark/benchmark.h"
namespace base {
namespace {
// Basis for `RETURN_IF_ERROR` and `ASSIGN_OR_RETURN` benchmarks. Derived
// classes override `LoopAgain` with the macro invocation(s).
class ReturnLoop {
public:
using ReturnType = expected<int, std::string>;
explicit ReturnLoop(ReturnType return_value)
: value_(std::move(return_value)) {}
virtual ~ReturnLoop() = default;
DISABLE_TAIL_CALLS ReturnType Loop(size_t* ops) {
if (!*ops) {
return value_;
}
return LoopAgain(ops);
}
ReturnType return_value() const { return value_; }
private:
virtual ReturnType LoopAgain(size_t* ops) = 0;
const ReturnType value_;
};
class ReturnIfErrorLoop : public ReturnLoop {
public:
using ReturnLoop::ReturnLoop;
private:
ReturnType LoopAgain(size_t* ops) override {
--*ops;
RETURN_IF_ERROR(Loop(ops));
return 0;
}
};
class ReturnIfErrorWithAnnotateLoop : public ReturnLoop {
public:
using ReturnLoop::ReturnLoop;
private:
ReturnType LoopAgain(size_t* ops) override {
--*ops;
RETURN_IF_ERROR(Loop(ops), [](std::string e) {
return e + "The quick brown fox jumped over the lazy dog.";
});
return 0;
}
};
class AssignOrReturnLoop : public ReturnLoop {
public:
using ReturnLoop::ReturnLoop;
private:
ReturnType LoopAgain(size_t* ops) override {
--*ops;
ASSIGN_OR_RETURN(const int result, Loop(ops));
return result;
}
};
class AssignOrReturnAnnotateLoop : public ReturnLoop {
public:
using ReturnLoop::ReturnLoop;
private:
ReturnType LoopAgain(size_t* ops) override {
--*ops;
ASSIGN_OR_RETURN(const int result, Loop(ops), [](std::string e) {
return e + "The quick brown fox jumped over the lazy dog.";
});
return result;
}
};
std::string BenchmarkError() {
// This error message is intended to be long enough to guarantee external
// memory allocation in `std::string`.
return "The quick brown fox jumped over the lazy dog.";
}
// Drive a benchmark loop. `T` is intended to be a `ReturnLoop` (above).
template <class T>
void BenchmarkLoop(T* driver, ::benchmark::State* state) {
// We benchmark 8 macro invocations (stack depth) per loop. This
// amortizes one time costs (e.g. building the initial error value)
// across what we actually care about.
constexpr int kMaxOps = 8;
while (state->KeepRunningBatch(kMaxOps)) {
size_t ops = kMaxOps;
auto ret = driver->Loop(&ops);
::benchmark::DoNotOptimize(ret);
}
}
// TODO(https://crbug.com/40251982): Update test-driving scripts to control
// google_benchmark correctly and parse its output, so that these benchmarks'
// results are included in bot output.
// Registers a benchmark as a GTest test. This allows using legacy
// --gtest_filter and --gtest_list_tests.
// TODO(https://crbug.com/40251982): Clean this up after transitioning to
// --benchmark_filter and --benchmark_list_tests.
#define BENCHMARK_WITH_TEST(benchmark_name) \
TEST(ExpectedMacrosPerfTest, benchmark_name) { \
BENCHMARK(benchmark_name); \
}
void BM_ReturnIfError_Ok(::benchmark::State& state) {
ReturnIfErrorLoop loop(1);
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_ReturnIfError_Ok)
void BM_ReturnIfError_Error(::benchmark::State& state) {
ReturnIfErrorLoop loop{unexpected(BenchmarkError())};
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_ReturnIfError_Error)
void BM_ReturnIfError_Annotate_Ok(::benchmark::State& state) {
ReturnIfErrorWithAnnotateLoop loop(1);
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_ReturnIfError_Annotate_Ok)
void BM_ReturnIfError_Annotate_Error(::benchmark::State& state) {
ReturnIfErrorWithAnnotateLoop loop{unexpected(BenchmarkError())};
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_ReturnIfError_Annotate_Error)
void BM_AssignOrReturn_Ok(::benchmark::State& state) {
AssignOrReturnLoop loop(1);
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_AssignOrReturn_Ok)
void BM_AssignOrReturn_Error(::benchmark::State& state) {
AssignOrReturnLoop loop{unexpected(BenchmarkError())};
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_AssignOrReturn_Error)
void BM_AssignOrReturn_Annotate_Ok(::benchmark::State& state) {
AssignOrReturnAnnotateLoop loop(1);
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_AssignOrReturn_Annotate_Ok)
void BM_AssignOrReturn_Annotate_Error(::benchmark::State& state) {
AssignOrReturnAnnotateLoop loop{unexpected(BenchmarkError())};
BenchmarkLoop(&loop, &state);
}
BENCHMARK_WITH_TEST(BM_AssignOrReturn_Annotate_Error)
} // namespace
} // namespace base
|