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
  181
  182
  183
  184

base / types / expected_macros_perftest.cc [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/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