Skip to content

Commit f62b90d

Browse files
committed
lru container prototype
1 parent 25bdbc2 commit f62b90d

File tree

10 files changed

+810
-0
lines changed

10 files changed

+810
-0
lines changed

lru_container/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*build
2+
*git

lru_container/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(LRUCacheDemo)
3+
4+
get_filename_component(MULTIINDEX_ROOT "../include" ABSOLUTE)
5+
message(STATUS "Boost root: ${MULTIINDEX_ROOT}")
6+
7+
find_package(Boost QUIET)
8+
9+
if(Boost_FOUND)
10+
message(STATUS "Found system Boost: ${Boost_INCLUDE_DIRS}")
11+
endif()
12+
13+
find_package(benchmark QUIET)
14+
15+
if(benchmark_FOUND)
16+
message(STATUS "Found Google Benchmark via find_package")
17+
endif()
18+
19+
include_directories(${MULTIINDEX_ROOT})
20+
21+
add_executable(lru_cache_demo
22+
main.cpp
23+
)
24+
25+
target_include_directories(lru_cache_demo PRIVATE ${Boost_INCLUDE_DIRS})
26+
target_compile_features(lru_cache_demo PRIVATE cxx_std_20)
27+
28+
target_link_libraries(lru_cache_demo PRIVATE benchmark::benchmark)

lru_container/main.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "src/tests/lru_basic_tests.h"
2+
#include "src/implements/lru_time_index_container.h"
3+
#include "src/benchmarks/lru_basic_benchmarks.h"
4+
#include "src/benchmarks/lru_google_benchmarks.h"
5+
#include "src/implements/lru_list_container.h"
6+
7+
int main() {
8+
test_lru_users<LRUCacheContainer_TimeIndex>();
9+
test_lru_products<LRUCacheContainer_TimeIndex>();
10+
std::cout << "all tests success" << std::endl;
11+
12+
test_lru_users<LRUCacheContainer_List>();
13+
test_lru_products<LRUCacheContainer_List>();
14+
std::cout << "all tests success" << std::endl;
15+
16+
benchmark::simple_benchmark<LRUCacheContainer_List>("output.txt");
17+
benchmark::simple_benchmark<LRUCacheContainer_TimeIndex>("output.txt");
18+
19+
benchmark::google_benchmark<LRUCacheContainer_List>();
20+
benchmark::google_benchmark<LRUCacheContainer_TimeIndex>();
21+
22+
benchmark::google_benchmark_init("google_output.txt");
23+
benchmark::google_benchmark_run();
24+
return 0;
25+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
3+
#include <iostream>
4+
#include <string>
5+
#include <fstream>
6+
#include <vector>
7+
#include <random>
8+
#include <chrono>
9+
#include <iomanip>
10+
11+
#include "../lru_container_concept.h"
12+
13+
namespace benchmark {
14+
15+
const std::vector<long long> CACHE_SIZES = {1000, 10000, 100000};
16+
const size_t OPERATIONS_NUMBER = 100000;
17+
const int MAX_ID_SIZE = 50000;
18+
19+
struct id_tag {};
20+
struct email_tag {};
21+
struct name_tag {};
22+
23+
struct User {
24+
int id;
25+
std::string email;
26+
std::string name;
27+
28+
bool operator==(const User& other) const {
29+
return id == other.id && email == other.email && name == other.name;
30+
}
31+
};
32+
33+
namespace generator {
34+
std::random_device rd;
35+
std::mt19937 gen(rd());
36+
std::uniform_real_distribution<double> action_dist(0.0, 1.0);
37+
std::uniform_int_distribution<int> id_dist(0, MAX_ID_SIZE);
38+
39+
User generate_user() {
40+
std::string email = "email" + std::to_string(id_dist(gen));
41+
std::string name = "name" + std::to_string(id_dist(gen));
42+
return User{id_dist(gen), email, name};
43+
}
44+
45+
int generate_id() {
46+
return id_dist(gen);
47+
}
48+
49+
std::string generate_name() {
50+
return "name" + std::to_string(id_dist(gen));
51+
}
52+
53+
std::string generate_email() {
54+
return "email" + std::to_string(id_dist(gen));
55+
}
56+
} // generator
57+
} // benchmark
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#pragma once
2+
3+
#include "benchmarks_resourses.h"
4+
5+
namespace benchmark {
6+
7+
template<
8+
template<typename, typename, typename> class LRUCacheContainer
9+
>
10+
void simple_benchmark(std::string &&output_filename) {
11+
12+
using UserCache = LRUCacheContainer<
13+
User,
14+
indexed_by<
15+
ordered_unique<tag<id_tag>, member<User, int, &User::id>>,
16+
ordered_unique<tag<email_tag>, member<User, std::string, &User::email>>,
17+
ordered_non_unique<tag<name_tag>, member<User, std::string, &User::name>>
18+
>,
19+
std::allocator<User>
20+
>;
21+
22+
lru_concept_assert_for_one_tag(UserCache, id_tag, int, User);
23+
lru_concept_assert_for_one_tag(UserCache, email_tag, std::string, User);
24+
lru_concept_assert_for_one_tag(UserCache, name_tag, std::string, User);
25+
26+
std::ofstream output_file(output_filename, std::ios::app);
27+
if (!output_file.is_open()) {
28+
std::cerr << "Failed to open output file: " << output_filename << std::endl;
29+
return;
30+
}
31+
32+
output_file << std::left << std::setw(20) << "Operations count"
33+
<< std::setw(16) << "Cache size"
34+
<< std::setw(12) << "Time (ms)"
35+
<< std::endl;
36+
output_file << std::string(50, '-') << std::endl;
37+
38+
for (const size_t size : CACHE_SIZES) {
39+
UserCache cache(size);
40+
for (size_t i = 0; i < size; ++i) {
41+
cache.emplace(generator::generate_user());
42+
}
43+
44+
size_t reading_operations_number = OPERATIONS_NUMBER * 4 / 5;
45+
size_t writing_operations_number = OPERATIONS_NUMBER / 5;
46+
47+
std::vector<std::string> names, emails;
48+
std::vector<int> ids;
49+
std::vector<User> users;
50+
51+
for (size_t i = 0; i < reading_operations_number; ++i) {
52+
names.push_back(generator::generate_name());
53+
emails.push_back(generator::generate_email());
54+
ids.push_back(generator::generate_id());
55+
}
56+
57+
for (size_t i = 0; i < writing_operations_number; ++i) {
58+
users.push_back(generator::generate_user());
59+
}
60+
61+
auto start_time = std::chrono::high_resolution_clock::now();
62+
63+
for (size_t i = 0; i < reading_operations_number; ++i) {
64+
cache.template find<name_tag, std::string>(names[i]);
65+
cache.template find<email_tag, std::string>(emails[i]);
66+
cache.template find<id_tag, int>(ids[i]);
67+
}
68+
69+
for (size_t i = 0; i < writing_operations_number; ++i) {
70+
cache.emplace(users[i]);
71+
}
72+
73+
auto end_time = std::chrono::high_resolution_clock::now();
74+
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
75+
76+
output_file << std::left << std::setw(20) << OPERATIONS_NUMBER
77+
<< std::setw(16) << size
78+
<< std::setw(12) << elapsed.count()
79+
<< std::endl;
80+
output_file << std::string(50, '-') << std::endl;
81+
}
82+
83+
output_file.close();
84+
}
85+
86+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#pragma once
2+
3+
#include <benchmark/benchmark.h>
4+
#include "benchmarks_resourses.h"
5+
6+
namespace benchmark {
7+
8+
template<
9+
template<typename, typename, typename> class LRUCacheContainer
10+
>
11+
class LRUCacheBenchmark {
12+
private:
13+
using UserCache = LRUCacheContainer<
14+
User,
15+
indexed_by<
16+
ordered_unique<tag<id_tag>, member<User, int, &User::id>>,
17+
ordered_unique<tag<email_tag>, member<User, std::string, &User::email>>,
18+
ordered_non_unique<tag<name_tag>, member<User, std::string, &User::name>>
19+
>,
20+
std::allocator<User>
21+
>;
22+
23+
static void prepare_cache(UserCache& cache, size_t size) {
24+
for (size_t i = 0; i < size; ++i) {
25+
cache.emplace(generator::generate_user());
26+
}
27+
}
28+
29+
public:
30+
static void BM_GetOperations(benchmark::State& state) {
31+
const size_t cache_size = state.range(0);
32+
const size_t operations_count = state.range(1);
33+
34+
UserCache cache(cache_size);
35+
prepare_cache(cache, cache_size);
36+
37+
for (auto _ : state) {
38+
state.PauseTiming();
39+
std::vector<std::string> names, emails;
40+
std::vector<int> ids;
41+
for (size_t i = 0; i < operations_count; ++i) {
42+
names.push_back(generator::generate_name());
43+
emails.push_back(generator::generate_email());
44+
ids.push_back(generator::generate_id());
45+
}
46+
state.ResumeTiming();
47+
48+
for (size_t i = 0; i < operations_count; ++i) {
49+
benchmark::DoNotOptimize(cache.template find<name_tag, std::string>(names[i]));
50+
benchmark::DoNotOptimize(cache.template find<email_tag, std::string>(emails[i]));
51+
benchmark::DoNotOptimize(cache.template find<id_tag, int>(ids[i]));
52+
}
53+
}
54+
55+
state.SetItemsProcessed(state.iterations() * operations_count * 3);
56+
state.SetComplexityN(cache_size);
57+
}
58+
59+
static void BM_EmplaceOperations(benchmark::State& state) {
60+
const size_t cache_size = state.range(0);
61+
const size_t operations_count = state.range(1);
62+
63+
UserCache cache(cache_size);
64+
prepare_cache(cache, cache_size);
65+
66+
for (auto _ : state) {
67+
state.PauseTiming();
68+
std::vector<User> users;
69+
for (size_t i = 0; i < operations_count; ++i) {
70+
users.push_back(generator::generate_user());
71+
}
72+
state.ResumeTiming();
73+
74+
for (size_t i = 0; i < operations_count; ++i) {
75+
cache.emplace(users[i]);
76+
}
77+
}
78+
79+
state.SetItemsProcessed(state.iterations() * operations_count);
80+
state.SetComplexityN(cache_size);
81+
}
82+
};
83+
84+
void google_benchmark_init(std::string&& output_filename) {
85+
std::vector<char*> args;
86+
std::string prog_name = "benchmark";
87+
args.push_back(prog_name.data());
88+
std::string out_arg = "--benchmark_out=" + output_filename;
89+
args.push_back(out_arg.data());
90+
std::string format_arg = "--benchmark_out_format=json";
91+
args.push_back(format_arg.data());
92+
int argc = args.size();
93+
benchmark::Initialize(&argc, args.data());
94+
}
95+
96+
void google_benchmark_run() {
97+
benchmark::RunSpecifiedBenchmarks();
98+
benchmark::ClearRegisteredBenchmarks();
99+
benchmark::Shutdown();
100+
}
101+
102+
template<
103+
template<typename, typename, typename> class LRUCacheContainer
104+
>
105+
void google_benchmark() {
106+
using UserCache = LRUCacheContainer<
107+
User,
108+
indexed_by<
109+
ordered_unique<tag<id_tag>, member<User, int, &User::id>>,
110+
ordered_unique<tag<email_tag>, member<User, std::string, &User::email>>,
111+
ordered_non_unique<tag<name_tag>, member<User, std::string, &User::name>>
112+
>,
113+
std::allocator<User>
114+
>;
115+
116+
lru_concept_assert_for_one_tag(UserCache, id_tag, int, User);
117+
lru_concept_assert_for_one_tag(UserCache, email_tag, std::string, User);
118+
lru_concept_assert_for_one_tag(UserCache, name_tag, std::string, User);
119+
120+
for (auto size : CACHE_SIZES) {
121+
benchmark::RegisterBenchmark(
122+
"GetOperations",
123+
&LRUCacheBenchmark<LRUCacheContainer>::BM_GetOperations
124+
)->Args({size, OPERATIONS_NUMBER})->Unit(benchmark::kMicrosecond);
125+
}
126+
127+
for (auto size : CACHE_SIZES) {
128+
benchmark::RegisterBenchmark(
129+
"EmplaceOperations",
130+
&LRUCacheBenchmark<LRUCacheContainer>::BM_EmplaceOperations
131+
)->Args({size, OPERATIONS_NUMBER})->Unit(benchmark::kMicrosecond);
132+
}
133+
}
134+
135+
} // namespace benchmark

0 commit comments

Comments
 (0)