1 // Copyright 2022 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 <algorithm>
16 #include <atomic>
17 #include <cstddef>
18 #include <cstring>
19 #include <memory>
20 #include <new>
21 #include <random>
22 #include <string>
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
26
27 #include "absl/base/config.h"
28 #include "absl/container/internal/layout.h"
29 #include "absl/log/internal/vlog_config.h"
30 #include "absl/memory/memory.h"
31 #include "absl/random/distributions.h"
32 #include "absl/strings/str_cat.h"
33 #include "benchmark/benchmark.h"
34
35 namespace absl {
36 ABSL_NAMESPACE_BEGIN
37 namespace log_internal {
38 // Performance of `UpdateLogSites` depends upon the number and organization of
39 // `VLogSite`s in the program. We can synthesize some on the heap to mimic
40 // their layout and linkage in static data.
41 class SyntheticBinary {
42 public:
SyntheticBinary(const size_t num_tus,const size_t max_extra_static_data_bytes_per_tu,const size_t max_sites_per_tu,const int num_shuffles)43 explicit SyntheticBinary(const size_t num_tus,
44 const size_t max_extra_static_data_bytes_per_tu,
45 const size_t max_sites_per_tu,
46 const int num_shuffles) {
47 per_tu_data_.reserve(num_tus);
48 auto sites = absl::make_unique<VLogSite *[]>(num_tus * max_sites_per_tu);
49 for (size_t i = 0; i < num_tus; i++) {
50 const std::string filename =
51 absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10,
52 "/file-", i % 10, ".cc");
53 container_internal::Layout<char, VLogSite, char> layout(
54 filename.size() + 1,
55 absl::LogUniform<size_t>(bitgen_, 1, max_sites_per_tu),
56 absl::LogUniform<size_t>(bitgen_, 0,
57 max_extra_static_data_bytes_per_tu));
58 auto buf = absl::make_unique<char[]>(layout.AllocSize());
59 layout.PoisonPadding(buf.get());
60 memcpy(layout.Pointer<0>(buf.get()), filename.c_str(),
61 filename.size() + 1);
62 for (VLogSite &site : layout.Slice<1>(buf.get())) {
63 sites[num_sites_++] =
64 new (&site) VLogSite(layout.Pointer<0>(buf.get()));
65 // The last one is a dangling pointer but will be fixed below.
66 site.next_.store(&site + 1, std::memory_order_seq_cst);
67 }
68 num_extra_static_data_bytes_ += layout.Size<2>();
69 per_tu_data_.push_back(PerTU{layout, std::move(buf)});
70 }
71 // Now link the files together back-to-front into a circular list.
72 for (size_t i = 0; i < num_tus; i++) {
73 auto &tu = per_tu_data_[i];
74 auto &next_tu = per_tu_data_[(i + 1) % num_tus];
75 tu.layout.Slice<1>(tu.buf.get())
76 .back()
77 .next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()),
78 std::memory_order_seq_cst);
79 }
80 // Now do some shufflin'.
81 auto new_sites = absl::make_unique<VLogSite *[]>(num_sites_);
82 for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) {
83 // Each shuffle cuts the ring into three pieces and rearranges them.
84 const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_);
85 const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_);
86 const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_);
87 if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue;
88 // The same cuts, sorted:
89 const size_t cut_1 = std::min({cut_a, cut_b, cut_c});
90 const size_t cut_3 = std::max({cut_a, cut_b, cut_c});
91 const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3;
92 VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst);
93 sites[cut_1]->next_.store(
94 sites[cut_2]->next_.load(std::memory_order_seq_cst),
95 std::memory_order_seq_cst);
96 sites[cut_2]->next_.store(
97 sites[cut_3]->next_.load(std::memory_order_seq_cst),
98 std::memory_order_seq_cst);
99 sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst);
100 memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1));
101 memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1],
102 sizeof(VLogSite *) * (cut_3 - cut_2));
103 memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1],
104 sizeof(VLogSite *) * (cut_2 - cut_1));
105 memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1],
106 sizeof(VLogSite *) * (num_sites_ - cut_3 - 1));
107 sites.swap(new_sites);
108 }
109 const char *last_file = nullptr;
110 for (size_t i = 0; i < num_sites_; i++) {
111 if (sites[i]->file_ == last_file) continue;
112 last_file = sites[i]->file_;
113 num_new_files_++;
114 }
115 absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]);
116 sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst);
117 }
~SyntheticBinary()118 ~SyntheticBinary() {
119 static_assert(std::is_trivially_destructible<VLogSite>::value, "");
120 absl::log_internal::SetVModuleListHeadForTestOnly(nullptr);
121 }
122
num_sites() const123 size_t num_sites() const { return num_sites_; }
num_new_files() const124 size_t num_new_files() const { return num_new_files_; }
num_extra_static_data_bytes() const125 size_t num_extra_static_data_bytes() const {
126 return num_extra_static_data_bytes_;
127 }
128
129 private:
130 struct PerTU {
131 container_internal::Layout<char, VLogSite, char> layout;
132 std::unique_ptr<char[]> buf;
133 };
134
135 std::mt19937 bitgen_;
136 std::vector<PerTU> per_tu_data_;
137 size_t num_sites_ = 0;
138 size_t num_new_files_ = 0;
139 size_t num_extra_static_data_bytes_ = 0;
140 };
141
142 namespace {
BM_UpdateVModuleEmpty(benchmark::State & state)143 void BM_UpdateVModuleEmpty(benchmark::State& state) {
144 SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
145 256, state.range(1));
146 for (auto s : state) {
147 absl::log_internal::UpdateVModule("");
148 }
149 state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
150 }
151 BENCHMARK(BM_UpdateVModuleEmpty)
152 ->ArgPair(100, 200)
153 ->ArgPair(1000, 2000)
154 ->ArgPair(10000, 20000);
155
BM_UpdateVModuleShort(benchmark::State & state)156 void BM_UpdateVModuleShort(benchmark::State& state) {
157 SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
158 256, state.range(1));
159 for (auto s : state) {
160 absl::log_internal::UpdateVModule("directory2/*=4");
161 }
162 state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
163 }
164 BENCHMARK(BM_UpdateVModuleShort)
165 ->ArgPair(100, 200)
166 ->ArgPair(1000, 2000)
167 ->ArgPair(10000, 20000);
168
BM_UpdateVModuleLong(benchmark::State & state)169 void BM_UpdateVModuleLong(benchmark::State& state) {
170 SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
171 256, state.range(1));
172 for (auto s : state) {
173 absl::log_internal::UpdateVModule(
174 "d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,,"
175 "d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7,"
176 "d?*r?**o?*y1/aaa=6");
177 }
178 state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
179 }
180 BENCHMARK(BM_UpdateVModuleLong)
181 ->ArgPair(100, 200)
182 ->ArgPair(1000, 2000)
183 ->ArgPair(10000, 20000);
184 } // namespace
185 } // namespace log_internal
186 ABSL_NAMESPACE_END
187 } // namespace absl
188