blob: 9004e2ee5e1c37ccc649ec8201f852c81e2cc7da [file] [log] [blame]
// Copyright 2022 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 <algorithm>
#include <atomic>
#include <cstddef>
#include <cstring>
#include <memory>
#include <new>
#include <random>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/base/config.h"
#include "absl/container/internal/layout.h"
#include "absl/log/internal/vlog_config.h"
#include "absl/memory/memory.h"
#include "absl/random/distributions.h"
#include "absl/strings/str_cat.h"
#include "benchmark/benchmark.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace log_internal {
// Performance of `UpdateLogSites` depends upon the number and organization of
// `VLogSite`s in the program. We can synthesize some on the heap to mimic
// their layout and linkage in static data.
class SyntheticBinary {
public:
explicit 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) {
per_tu_data_.reserve(num_tus);
auto sites = absl::make_unique<VLogSite *[]>(num_tus * max_sites_per_tu);
for (size_t i = 0; i < num_tus; i++) {
const std::string filename =
absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10,
"/file-", i % 10, ".cc");
container_internal::Layout<char, VLogSite, char> layout(
filename.size() + 1,
absl::LogUniform<size_t>(bitgen_, 1, max_sites_per_tu),
absl::LogUniform<size_t>(bitgen_, 0,
max_extra_static_data_bytes_per_tu));
auto buf = absl::make_unique<char[]>(layout.AllocSize());
layout.PoisonPadding(buf.get());
memcpy(layout.Pointer<0>(buf.get()), filename.c_str(),
filename.size() + 1);
for (VLogSite &site : layout.Slice<1>(buf.get())) {
sites[num_sites_++] =
new (&site) VLogSite(layout.Pointer<0>(buf.get()));
// The last one is a dangling pointer but will be fixed below.
site.next_.store(&site + 1, std::memory_order_seq_cst);
}
num_extra_static_data_bytes_ += layout.Size<2>();
per_tu_data_.push_back(PerTU{layout, std::move(buf)});
}
// Now link the files together back-to-front into a circular list.
for (size_t i = 0; i < num_tus; i++) {
auto &tu = per_tu_data_[i];
auto &next_tu = per_tu_data_[(i + 1) % num_tus];
tu.layout.Slice<1>(tu.buf.get())
.back()
.next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()),
std::memory_order_seq_cst);
}
// Now do some shufflin'.
auto new_sites = absl::make_unique<VLogSite *[]>(num_sites_);
for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) {
// Each shuffle cuts the ring into three pieces and rearranges them.
const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_);
const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_);
const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_);
if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue;
// The same cuts, sorted:
const size_t cut_1 = std::min({cut_a, cut_b, cut_c});
const size_t cut_3 = std::max({cut_a, cut_b, cut_c});
const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3;
VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst);
sites[cut_1]->next_.store(
sites[cut_2]->next_.load(std::memory_order_seq_cst),
std::memory_order_seq_cst);
sites[cut_2]->next_.store(
sites[cut_3]->next_.load(std::memory_order_seq_cst),
std::memory_order_seq_cst);
sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst);
memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1));
memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1],
sizeof(VLogSite *) * (cut_3 - cut_2));
memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1],
sizeof(VLogSite *) * (cut_2 - cut_1));
memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1],
sizeof(VLogSite *) * (num_sites_ - cut_3 - 1));
sites.swap(new_sites);
}
const char *last_file = nullptr;
for (size_t i = 0; i < num_sites_; i++) {
if (sites[i]->file_ == last_file) continue;
last_file = sites[i]->file_;
num_new_files_++;
}
absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]);
sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst);
}
~SyntheticBinary() {
static_assert(std::is_trivially_destructible<VLogSite>::value, "");
absl::log_internal::SetVModuleListHeadForTestOnly(nullptr);
}
size_t num_sites() const { return num_sites_; }
size_t num_new_files() const { return num_new_files_; }
size_t num_extra_static_data_bytes() const {
return num_extra_static_data_bytes_;
}
private:
struct PerTU {
container_internal::Layout<char, VLogSite, char> layout;
std::unique_ptr<char[]> buf;
};
std::mt19937 bitgen_;
std::vector<PerTU> per_tu_data_;
size_t num_sites_ = 0;
size_t num_new_files_ = 0;
size_t num_extra_static_data_bytes_ = 0;
};
namespace {
void BM_UpdateVModuleEmpty(benchmark::State& state) {
SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
256, state.range(1));
for (auto s : state) {
absl::log_internal::UpdateVModule("");
}
state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
}
BENCHMARK(BM_UpdateVModuleEmpty)
->ArgPair(100, 200)
->ArgPair(1000, 2000)
->ArgPair(10000, 20000);
void BM_UpdateVModuleShort(benchmark::State& state) {
SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
256, state.range(1));
for (auto s : state) {
absl::log_internal::UpdateVModule("directory2/*=4");
}
state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
}
BENCHMARK(BM_UpdateVModuleShort)
->ArgPair(100, 200)
->ArgPair(1000, 2000)
->ArgPair(10000, 20000);
void BM_UpdateVModuleLong(benchmark::State& state) {
SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
256, state.range(1));
for (auto s : state) {
absl::log_internal::UpdateVModule(
"d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,,"
"d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7,"
"d?*r?**o?*y1/aaa=6");
}
state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
}
BENCHMARK(BM_UpdateVModuleLong)
->ArgPair(100, 200)
->ArgPair(1000, 2000)
->ArgPair(10000, 20000);
} // namespace
} // namespace log_internal
ABSL_NAMESPACE_END
} // namespace absl