1#include <userver/engine/run_standalone.hpp>
2#include <userver/multi-index-lru/container.hpp>
3#include <userver/multi-index-lru/expirable_container.hpp>
4#include <userver/utils/rand.hpp>
10#include <benchmark/benchmark.h>
11#include <boost/multi_index/member.hpp>
12#include <boost/multi_index/ordered_index.hpp>
14USERVER_NAMESPACE_BEGIN
18const std::size_t kOperationsNumber = 100000;
19const int kMaxIdSize = 50000;
30 bool operator==(
const User& other)
const {
return id == other.id && email == other.email && name == other.name; }
33using UserIndices = boost::multi_index::indexed_by<
34 boost::multi_index::ordered_unique<
35 boost::multi_index::tag<IdTag>,
36 boost::multi_index::member<User,
int, &User::id>>,
37 boost::multi_index::ordered_unique<
38 boost::multi_index::tag<EmailTag>,
39 boost::multi_index::member<User, std::string, &User::email>>,
40 boost::multi_index::ordered_non_unique<
41 boost::multi_index::tag<NameTag>,
42 boost::multi_index::member<User, std::string, &User::name>>>;
45using UserLruCache = multi_index_lru::
Container<User, UserIndices>;
46using UserExpirableCache = multi_index_lru::ExpirableContainer<User, UserIndices>;
65template <
typename Container>
66void PrepareCache(Container& cache, std::size_t size) {
67 for (std::size_t i = 0; i < size; ++i) {
68 cache.insert(GenerateUser());
73 std::vector<std::string>& names,
74 std::vector<std::string>& emails,
75 std::vector<
int>& ids,
82 emails.reserve(count);
85 for (std::size_t i = 0; i < count; ++i) {
86 names.push_back(GenerateName());
87 emails.push_back(GenerateEmail());
88 ids.push_back(GenerateId());
92void PrepareWriteData(std::vector<User>& users, std::size_t count) {
95 for (std::size_t i = 0; i < count; ++i) {
96 users.push_back(GenerateUser());
103template <
typename Container>
104void RunFindInsertMix(benchmark::State& state, Container& cache) {
105 const std::size_t read_ops = kOperationsNumber * 4 / 5;
106 const std::size_t write_ops = kOperationsNumber / 5;
108 std::vector<std::string> names, emails;
109 std::vector<
int> ids;
110 std::vector<User> users;
112 PrepareReadData(names, emails, ids, read_ops);
113 PrepareWriteData(users, write_ops);
115 for ([[maybe_unused]]
auto _ : state) {
116 for (std::size_t i = 0; i < read_ops; ++i) {
117 benchmark::DoNotOptimize(cache.
template find<NameTag>(names[i]));
118 benchmark::DoNotOptimize(cache.
template find<EmailTag>(emails[i]));
119 benchmark::DoNotOptimize(cache.
template find<IdTag>(ids[i]));
122 for (std::size_t i = 0; i < write_ops; ++i) {
123 cache.insert(users[i]);
128template <
typename Container>
129void RunGetOperations(benchmark::State& state, Container& cache) {
130 std::vector<std::string> names, emails;
131 std::vector<
int> ids;
133 for (
auto _ : state) {
135 PrepareReadData(names, emails, ids, kOperationsNumber);
136 state.ResumeTiming();
138 for (std::size_t i = 0; i < kOperationsNumber; ++i) {
139 benchmark::DoNotOptimize(cache.
template find<NameTag>(names[i]));
140 benchmark::DoNotOptimize(cache.
template find<EmailTag>(emails[i]));
141 benchmark::DoNotOptimize(cache.
template find<IdTag>(ids[i]));
145 state.SetItemsProcessed(state.iterations() * kOperationsNumber * 3);
146 state.SetComplexityN(state.range(0));
149template <
typename Container>
150void RunInsertOperations(benchmark::State& state, Container& cache) {
151 std::vector<User> users;
153 for (
auto _ : state) {
155 PrepareWriteData(users, kOperationsNumber);
156 state.ResumeTiming();
158 for (std::size_t i = 0; i < kOperationsNumber; ++i) {
159 cache.insert(users[i]);
163 state.SetItemsProcessed(state.iterations() * kOperationsNumber);
164 state.SetComplexityN(state.range(0));
168static void LruFindInsertMix(benchmark::State& state) {
169 const std::size_t size = state.range(0);
170 UserLruCache cache(size);
171 PrepareCache(cache, size);
172 RunFindInsertMix(state, cache);
174BENCHMARK(LruFindInsertMix)->RangeMultiplier(10)->Range(100, 1'000'000);
176static void LruGetOperations(benchmark::State& state) {
177 const std::size_t cache_size = state.range(0);
178 UserLruCache cache(cache_size);
179 PrepareCache(cache, cache_size);
180 RunGetOperations(state, cache);
182BENCHMARK(LruGetOperations)->RangeMultiplier(10)->Range(100, 1'000'000);
184static void LruInsertOperations(benchmark::State& state) {
185 const std::size_t cache_size = state.range(0);
186 UserLruCache cache(cache_size);
187 PrepareCache(cache, cache_size);
188 RunInsertOperations(state, cache);
190BENCHMARK(LruInsertOperations)->RangeMultiplier(10)->Range(100, 1'000'000);
193static void ExpirableFindInsertMix(benchmark::State& state) {
195 const std::size_t cache_size = state.range(0);
196 UserExpirableCache cache(cache_size, std::chrono::seconds(5));
197 PrepareCache(cache, cache_size);
198 RunFindInsertMix(state, cache);
201BENCHMARK(ExpirableFindInsertMix)->RangeMultiplier(10)->Range(10, 1'000'000);
203static void ExpirableGetOperations(benchmark::State& state) {
205 const std::size_t cache_size = state.range(0);
206 UserExpirableCache cache(cache_size, std::chrono::minutes(10));
207 PrepareCache(cache, cache_size);
208 RunGetOperations(state, cache);
211BENCHMARK(ExpirableGetOperations)->RangeMultiplier(10)->Range(100, 1'000'000);
213static void ExpirableInsertOperations(benchmark::State& state) {
215 const std::size_t cache_size = state.range(0);
216 UserExpirableCache cache(cache_size, std::chrono::minutes(10));
217 PrepareCache(cache, cache_size);
218 RunInsertOperations(state, cache);
221BENCHMARK(ExpirableInsertOperations)->RangeMultiplier(10)->Range(100, 1'000'000);