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