blob: 5fc88f1dfe88aad2638a8f41cf61acf5ee6b2dc7 [file] [log] [blame]
Derek Mauro8c488c42023-11-08 09:57:56 -08001// 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
21namespace {
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).
26constexpr 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.
32constexpr 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().
36class BM_Blob {
37 public:
38 BM_Blob(int val) { for (auto& d : data_) d = val; }
39 BM_Blob() : BM_Blob(-1) {}
40 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.
49template<int i>
50const 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.
57template<int i>
58const BM_Blob& OnHeapBlobFunc() {
59 static BM_Blob* x = new BM_Blob(i);
60 return *x;
61}
62
63// Type for NoDestrBlobFunc or OnHeapBlobFunc.
64typedef 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.
71static BM_Blob direct_blob(0);
72
73void BM_Direct(benchmark::State& state) {
74 for (auto s : state) {
75 direct_blob.Verify(0);
76 }
77}
78BENCHMARK(BM_Direct);
79
80void BM_NoDestr(benchmark::State& state) {
81 for (auto s : state) {
82 NoDestrBlobFunc<0>().Verify(0);
83 }
84}
85BENCHMARK(BM_NoDestr);
86
87void BM_OnHeap(benchmark::State& state) {
88 for (auto s : state) {
89 OnHeapBlobFunc<0>().Verify(0);
90 }
91}
92BENCHMARK(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.
100enum 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).
104template<int n>
105FuncType 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
116template<>
117FuncType 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.
128static BM_Blob direct_blobs[kNumObjects];
129
130// Helper that cheaply maps benchmark iteration to randomish index in
131// [0, kNumObjects).
132int 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.
138template <BM_Type t>
139void 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
157void BM_DirectMany(benchmark::State& state) { BM_Many<kDirect>(state); }
158void BM_NoDestrMany(benchmark::State& state) { BM_Many<kNoDestr>(state); }
159void BM_OnHeapMany(benchmark::State& state) { BM_Many<kOnHeap>(state); }
160
161BENCHMARK(BM_DirectMany);
162BENCHMARK(BM_NoDestrMany);
163BENCHMARK(BM_OnHeapMany);
164
165} // namespace