xref: /aosp_15_r20/external/abseil-cpp/absl/base/no_destructor_benchmark.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 // Copyright 2023 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <cstdint>
16 
17 #include "absl/base/internal/raw_logging.h"
18 #include "absl/base/no_destructor.h"
19 #include "benchmark/benchmark.h"
20 
21 namespace {
22 
23 // Number of static-NoDestructor-in-a-function to exercise.
24 // This must be low enough not to hit template instantiation limits
25 // (happens around 1000).
26 constexpr int kNumObjects = 1;  // set to 512 when doing benchmarks
27                                 // 1 is faster to compile: just one templated
28                                 // function instantiation
29 
30 // Size of individual objects to benchmark static-NoDestructor-in-a-function
31 // usage with.
32 constexpr int kObjSize = sizeof(void*)*1;
33 
34 // Simple object of kObjSize bytes (rounded to int).
35 // We benchmark complete reading of its state via Verify().
36 class BM_Blob {
37  public:
BM_Blob(int val)38   BM_Blob(int val) { for (auto& d : data_) d = val; }
BM_Blob()39   BM_Blob() : BM_Blob(-1) {}
Verify(int val) const40   void Verify(int val) const {  // val must be the c-tor argument
41     for (auto& d : data_) ABSL_INTERNAL_CHECK(d == val, "");
42   }
43  private:
44   int data_[kObjSize / sizeof(int) > 0 ? kObjSize / sizeof(int) : 1];
45 };
46 
47 // static-NoDestructor-in-a-function pattern instances.
48 // We'll instantiate kNumObjects of them.
49 template<int i>
NoDestrBlobFunc()50 const BM_Blob& NoDestrBlobFunc() {
51   static absl::NoDestructor<BM_Blob> x(i);
52   return *x;
53 }
54 
55 // static-heap-ptr-in-a-function pattern instances
56 // We'll instantiate kNumObjects of them.
57 template<int i>
OnHeapBlobFunc()58 const BM_Blob& OnHeapBlobFunc() {
59   static BM_Blob* x = new BM_Blob(i);
60   return *x;
61 }
62 
63 // Type for NoDestrBlobFunc or OnHeapBlobFunc.
64 typedef const BM_Blob& (*FuncType)();
65 
66 // ========================================================================= //
67 // Simple benchmarks that read a single BM_Blob over and over, hence
68 // all they touch fits into L1 CPU cache:
69 
70 // Direct non-POD global variable (style guide violation) as a baseline.
71 static BM_Blob direct_blob(0);
72 
BM_Direct(benchmark::State & state)73 void BM_Direct(benchmark::State& state) {
74   for (auto s : state) {
75     direct_blob.Verify(0);
76   }
77 }
78 BENCHMARK(BM_Direct);
79 
BM_NoDestr(benchmark::State & state)80 void BM_NoDestr(benchmark::State& state) {
81   for (auto s : state) {
82     NoDestrBlobFunc<0>().Verify(0);
83   }
84 }
85 BENCHMARK(BM_NoDestr);
86 
BM_OnHeap(benchmark::State & state)87 void BM_OnHeap(benchmark::State& state) {
88   for (auto s : state) {
89     OnHeapBlobFunc<0>().Verify(0);
90   }
91 }
92 BENCHMARK(BM_OnHeap);
93 
94 // ========================================================================= //
95 // Benchmarks that read kNumObjects of BM_Blob over and over, hence with
96 // appropriate values of sizeof(BM_Blob) and kNumObjects their working set
97 // can exceed a given layer of CPU cache.
98 
99 // Type of benchmark to select between NoDestrBlobFunc and OnHeapBlobFunc.
100 enum BM_Type { kNoDestr, kOnHeap, kDirect };
101 
102 // BlobFunc<n>(t, i) returns the i-th function of type t.
103 // n must be larger than i (we'll use kNumObjects for n).
104 template<int n>
BlobFunc(BM_Type t,int i)105 FuncType BlobFunc(BM_Type t, int i) {
106   if (i == n) {
107     switch (t) {
108       case kNoDestr:  return &NoDestrBlobFunc<n>;
109       case kOnHeap:   return &OnHeapBlobFunc<n>;
110       case kDirect:   return nullptr;
111     }
112   }
113   return BlobFunc<n-1>(t, i);
114 }
115 
116 template<>
BlobFunc(BM_Type t,int i)117 FuncType BlobFunc<0>(BM_Type t, int i) {
118   ABSL_INTERNAL_CHECK(i == 0, "");
119   switch (t) {
120     case kNoDestr:  return &NoDestrBlobFunc<0>;
121     case kOnHeap:   return &OnHeapBlobFunc<0>;
122     case kDirect:   return nullptr;
123   }
124   return nullptr;
125 }
126 
127 // Direct non-POD global variables (style guide violation) as a baseline.
128 static BM_Blob direct_blobs[kNumObjects];
129 
130 // Helper that cheaply maps benchmark iteration to randomish index in
131 // [0, kNumObjects).
RandIdx(int i)132 int RandIdx(int i) {
133   // int64 is to avoid overflow and generating negative return values:
134   return (static_cast<int64_t>(i) * 13) % kNumObjects;
135 }
136 
137 // Generic benchmark working with kNumObjects for any of the possible BM_Type.
138 template <BM_Type t>
BM_Many(benchmark::State & state)139 void BM_Many(benchmark::State& state) {
140   FuncType funcs[kNumObjects];
141   for (int i = 0; i < kNumObjects; ++i) {
142     funcs[i] = BlobFunc<kNumObjects-1>(t, i);
143   }
144   if (t == kDirect) {
145     for (auto s : state) {
146       int idx = RandIdx(state.iterations());
147       direct_blobs[idx].Verify(-1);
148     }
149   } else {
150     for (auto s : state) {
151       int idx = RandIdx(state.iterations());
152       funcs[idx]().Verify(idx);
153     }
154   }
155 }
156 
BM_DirectMany(benchmark::State & state)157 void BM_DirectMany(benchmark::State& state) { BM_Many<kDirect>(state); }
BM_NoDestrMany(benchmark::State & state)158 void BM_NoDestrMany(benchmark::State& state) { BM_Many<kNoDestr>(state); }
BM_OnHeapMany(benchmark::State & state)159 void BM_OnHeapMany(benchmark::State& state) { BM_Many<kOnHeap>(state); }
160 
161 BENCHMARK(BM_DirectMany);
162 BENCHMARK(BM_NoDestrMany);
163 BENCHMARK(BM_OnHeapMany);
164 
165 }  // namespace
166