diff options
Diffstat (limited to 'absl/base/no_destructor_benchmark.cc')
-rw-r--r-- | absl/base/no_destructor_benchmark.cc | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/absl/base/no_destructor_benchmark.cc b/absl/base/no_destructor_benchmark.cc new file mode 100644 index 00000000..5fc88f1d --- /dev/null +++ b/absl/base/no_destructor_benchmark.cc @@ -0,0 +1,165 @@ +// Copyright 2023 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstdint> + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/no_destructor.h" +#include "benchmark/benchmark.h" + +namespace { + +// Number of static-NoDestructor-in-a-function to exercise. +// This must be low enough not to hit template instantiation limits +// (happens around 1000). +constexpr int kNumObjects = 1; // set to 512 when doing benchmarks + // 1 is faster to compile: just one templated + // function instantiation + +// Size of individual objects to benchmark static-NoDestructor-in-a-function +// usage with. +constexpr int kObjSize = sizeof(void*)*1; + +// Simple object of kObjSize bytes (rounded to int). +// We benchmark complete reading of its state via Verify(). +class BM_Blob { + public: + BM_Blob(int val) { for (auto& d : data_) d = val; } + BM_Blob() : BM_Blob(-1) {} + void Verify(int val) const { // val must be the c-tor argument + for (auto& d : data_) ABSL_INTERNAL_CHECK(d == val, ""); + } + private: + int data_[kObjSize / sizeof(int) > 0 ? kObjSize / sizeof(int) : 1]; +}; + +// static-NoDestructor-in-a-function pattern instances. +// We'll instantiate kNumObjects of them. +template<int i> +const BM_Blob& NoDestrBlobFunc() { + static absl::NoDestructor<BM_Blob> x(i); + return *x; +} + +// static-heap-ptr-in-a-function pattern instances +// We'll instantiate kNumObjects of them. +template<int i> +const BM_Blob& OnHeapBlobFunc() { + static BM_Blob* x = new BM_Blob(i); + return *x; +} + +// Type for NoDestrBlobFunc or OnHeapBlobFunc. +typedef const BM_Blob& (*FuncType)(); + +// ========================================================================= // +// Simple benchmarks that read a single BM_Blob over and over, hence +// all they touch fits into L1 CPU cache: + +// Direct non-POD global variable (style guide violation) as a baseline. +static BM_Blob direct_blob(0); + +void BM_Direct(benchmark::State& state) { + for (auto s : state) { + direct_blob.Verify(0); + } +} +BENCHMARK(BM_Direct); + +void BM_NoDestr(benchmark::State& state) { + for (auto s : state) { + NoDestrBlobFunc<0>().Verify(0); + } +} +BENCHMARK(BM_NoDestr); + +void BM_OnHeap(benchmark::State& state) { + for (auto s : state) { + OnHeapBlobFunc<0>().Verify(0); + } +} +BENCHMARK(BM_OnHeap); + +// ========================================================================= // +// Benchmarks that read kNumObjects of BM_Blob over and over, hence with +// appropriate values of sizeof(BM_Blob) and kNumObjects their working set +// can exceed a given layer of CPU cache. + +// Type of benchmark to select between NoDestrBlobFunc and OnHeapBlobFunc. +enum BM_Type { kNoDestr, kOnHeap, kDirect }; + +// BlobFunc<n>(t, i) returns the i-th function of type t. +// n must be larger than i (we'll use kNumObjects for n). +template<int n> +FuncType BlobFunc(BM_Type t, int i) { + if (i == n) { + switch (t) { + case kNoDestr: return &NoDestrBlobFunc<n>; + case kOnHeap: return &OnHeapBlobFunc<n>; + case kDirect: return nullptr; + } + } + return BlobFunc<n-1>(t, i); +} + +template<> +FuncType BlobFunc<0>(BM_Type t, int i) { + ABSL_INTERNAL_CHECK(i == 0, ""); + switch (t) { + case kNoDestr: return &NoDestrBlobFunc<0>; + case kOnHeap: return &OnHeapBlobFunc<0>; + case kDirect: return nullptr; + } + return nullptr; +} + +// Direct non-POD global variables (style guide violation) as a baseline. +static BM_Blob direct_blobs[kNumObjects]; + +// Helper that cheaply maps benchmark iteration to randomish index in +// [0, kNumObjects). +int RandIdx(int i) { + // int64 is to avoid overflow and generating negative return values: + return (static_cast<int64_t>(i) * 13) % kNumObjects; +} + +// Generic benchmark working with kNumObjects for any of the possible BM_Type. +template <BM_Type t> +void BM_Many(benchmark::State& state) { + FuncType funcs[kNumObjects]; + for (int i = 0; i < kNumObjects; ++i) { + funcs[i] = BlobFunc<kNumObjects-1>(t, i); + } + if (t == kDirect) { + for (auto s : state) { + int idx = RandIdx(state.iterations()); + direct_blobs[idx].Verify(-1); + } + } else { + for (auto s : state) { + int idx = RandIdx(state.iterations()); + funcs[idx]().Verify(idx); + } + } +} + +void BM_DirectMany(benchmark::State& state) { BM_Many<kDirect>(state); } +void BM_NoDestrMany(benchmark::State& state) { BM_Many<kNoDestr>(state); } +void BM_OnHeapMany(benchmark::State& state) { BM_Many<kOnHeap>(state); } + +BENCHMARK(BM_DirectMany); +BENCHMARK(BM_NoDestrMany); +BENCHMARK(BM_OnHeapMany); + +} // namespace |