blob: c25774696196d56a5d3521edbdce45804a19d9e1 [file] [log] [blame]
// 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.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "base/types/expected_macros.h"
#include <stddef.h>
#include <string>
#include <utility>
#include "base/types/expected.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/base/attributes.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;
ABSL_ATTRIBUTE_NO_TAIL_CALL 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