/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "td/utils/common.h" #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/ThreadLocalStorage.h" #include #include #include namespace td { template class ThreadSafeMultiCounter { public: void add(size_t index, int64 diff) { CHECK(index < N); tls_.get()[index].fetch_add(diff, std::memory_order_relaxed); } int64 sum(size_t index) const { CHECK(index < N); int64 res = 0; tls_.for_each([&](auto &value) { res += value[index].load(std::memory_order_relaxed); }); return res; } void clear() { tls_.for_each([&](auto &value) { for (auto &x : value) { x = 0; } }); } private: ThreadLocalStorage, N>> tls_; }; class ThreadSafeCounter { public: void add(int64 diff) { counter_.add(0, diff); } int64 sum() const { return counter_.sum(0); } private: ThreadSafeMultiCounter<1> counter_; }; class NamedThreadSafeCounter { static constexpr int N = 128; using Counter = ThreadSafeMultiCounter; public: class CounterRef { public: CounterRef() = default; CounterRef(size_t index, Counter *counter) : index_(index), counter_(counter) { } void add(int64 diff) { counter_->add(index_, diff); } int64 sum() const { return counter_->sum(index_); } private: size_t index_{0}; Counter *counter_{nullptr}; }; CounterRef get_counter(Slice name) { std::unique_lock guard(mutex_); for (size_t i = 0; i < names_.size(); i++) { if (names_[i] == name) { return get_counter_ref(i); } } CHECK(names_.size() < N); names_.emplace_back(name.begin(), name.size()); return get_counter_ref(names_.size() - 1); } CounterRef get_counter_ref(size_t index) { return CounterRef(index, &counter_); } static NamedThreadSafeCounter &get_default() { static NamedThreadSafeCounter res; return res; } template void for_each(F &&f) const { std::unique_lock guard(mutex_); for (size_t i = 0; i < names_.size(); i++) { f(names_[i], counter_.sum(i)); } } void clear() { std::unique_lock guard(mutex_); counter_.clear(); } friend StringBuilder &operator<<(StringBuilder &sb, const NamedThreadSafeCounter &counter) { counter.for_each([&sb](Slice name, int64 cnt) { sb << name << ": " << cnt << "\n"; }); return sb; } private: mutable std::mutex mutex_; std::vector names_; Counter counter_; }; // another class for simplicity, it struct NamedPerfCounter { public: static NamedPerfCounter &get_default() { static NamedPerfCounter res; return res; } struct PerfCounterRef { NamedThreadSafeCounter::CounterRef count; NamedThreadSafeCounter::CounterRef duration; }; PerfCounterRef get_counter(Slice name) { return {.count = counter_.get_counter(PSLICE() << name << ".count"), .duration = counter_.get_counter(PSLICE() << name << ".duration")}; } struct ScopedPerfCounterRef : public NoCopyOrMove { PerfCounterRef perf_counter; uint64 started_at_ticks{td::Clocks::rdtsc()}; ~ScopedPerfCounterRef() { perf_counter.count.add(1); perf_counter.duration.add(td::Clocks::rdtsc() - started_at_ticks); } }; template void for_each(F &&f) const { counter_.for_each(f); } void clear() { counter_.clear(); } friend StringBuilder &operator<<(StringBuilder &sb, const NamedPerfCounter &counter) { return sb << counter.counter_; } private: NamedThreadSafeCounter counter_; }; } // namespace td #define TD_PERF_COUNTER(name) \ static auto perf_##name = td::NamedPerfCounter::get_default().get_counter(td::Slice(#name)); \ auto scoped_perf_##name = td::NamedPerfCounter::ScopedPerfCounterRef{.perf_counter = perf_##name}; #define TD_PERF_COUNTER_SINCE(name, since) \ static auto perf_##name = td::NamedPerfCounter::get_default().get_counter(td::Slice(#name)); \ auto scoped_perf_##name = \ td::NamedPerfCounter::ScopedPerfCounterRef{.perf_counter = perf_##name, .started_at_ticks = since};