diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 42c677e..d2840a2 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -24,7 +24,7 @@ jobs: # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: os: [ubuntu-latest, windows-latest] - build_type: [Debug] + build_type: [Release] c_compiler: [gcc, clang, cl] include: - os: windows-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 417d49f..cbb700b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(main) add_subdirectory(LRU) add_subdirectory(LFU) add_subdirectory(perfect) +add_subdirectory(benchmark) include(CTest) diff --git a/README.md b/README.md index 48cdd45..5ca4f7a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# cpp_proj +# HWC Caches. LRU, LFU, Belady's optimal cache -How to run +## How to run ```bash ./build/main/main <<< "2 6 3 3 1 2 1 2" # cache_size Nelem elem1 elem2 ... @@ -8,17 +8,27 @@ How to run output is ```bash Cache type N hits +Belady 3 LRU 3 LFU 1 ``` +### Benchmark +input : uniform random integers [0: 100) with input size 100'000 +![statistics](./benchmark/cache_hit_statistics.png) -# Tips +To reproduce benchmarks one needs to build the project and run +```bash +./build/benchmark/benchmark +cd benchmark +gnuplot script.gp +open cache_hit_statistics.png +``` -## CMAKE +## How to build: CMAKE ```bash cmake -S . -B build -G Ninja \ -DCMAKE_C_COMPILER=gcc-14 \ diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt new file mode 100644 index 0000000..953ba69 --- /dev/null +++ b/benchmark/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(benchmark) +target_sources(benchmark + PRIVATE + benchmark.cpp +) + +target_compile_options(benchmark + PRIVATE + $<$:/W4 /WX> + $<$:-Wall -Wextra -Werror> +) + +target_link_libraries(benchmark + PRIVATE + LRU + LFU + perfect +) diff --git a/benchmark/benchmark.cpp b/benchmark/benchmark.cpp new file mode 100644 index 0000000..ec258ab --- /dev/null +++ b/benchmark/benchmark.cpp @@ -0,0 +1,68 @@ +#include "LFU.hpp" +#include "LRU.hpp" +#include "perfect.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +int slow_get_page(int key) { return key; } + +std::vector generate_rnd_keys(size_t n, int mod = 100) { + std::vector keys(n); + std::generate(keys.begin(), keys.end(), + [&]() { return static_cast(rand() % mod); }); + return keys; +} + +int main() { + size_t input_sz = 100'000; + int rnd_mod = 100; + std::vector keys = generate_rnd_keys(input_sz, rnd_mod); + + std::map> cache_sizes_vs_strategy_hits; + std::cout << "Cache type\tN hits\n"; + for (size_t cache_sz = 10; cache_sz < 101; cache_sz += 10) { + std::map cache_hits; + cache::perfect_t perfect_cache(cache_sz, keys); + cache::LRU_t lru{cache_sz}; + cache::LFU_t lfu{cache_sz}; + + for (const auto key : keys) { + if (perfect_cache.lookup_update(key, slow_get_page)) + cache_hits["Belady"] += 1; + + if (lru.lookup_update(key, slow_get_page)) + cache_hits["LRU"] += 1; + + if (lfu.lookup_update(key, slow_get_page)) + cache_hits["LFU"] += 1; + } + cache_sizes_vs_strategy_hits[cache_sz] = cache_hits; + + std::cout << "cache size: " << cache_sz << '\n'; + for (auto [cache_type, nhits] : cache_hits) { + std::cout << cache_type << "\t" << nhits << '\n'; + } + std::cout << std::endl; + } + + // output to a file + std::ofstream file("benchmark/cache_hits.dat"); + if (file.is_open()) { + file << "#Number of keys = " << input_sz << "\n"; + file << "#input keys: uniform distribution [0:" << rnd_mod << ")\n"; + file << "#Cache_size\tLRU\tLFU\tBelady\n"; + for (auto &[sz, cache_stats] : cache_sizes_vs_strategy_hits) { + file << sz << '\t' << cache_stats["LRU"] << '\t' << cache_stats["LFU"] + << '\t' << cache_stats["Belady"] << '\n'; + } + file.close(); + } + + return 0; +} diff --git a/benchmark/cache_hit_statistics.png b/benchmark/cache_hit_statistics.png new file mode 100644 index 0000000..fead7dd Binary files /dev/null and b/benchmark/cache_hit_statistics.png differ diff --git a/benchmark/cache_hits.dat b/benchmark/cache_hits.dat new file mode 100644 index 0000000..f601acc --- /dev/null +++ b/benchmark/cache_hits.dat @@ -0,0 +1,13 @@ +#Number of keys = 100000 +#input keys: uniform distribution [0:100) +#Cache_size LRU LFU Belady +10 10015 9828 35205 +20 19947 19849 51756 +30 29887 29907 63218 +40 39921 39898 72054 +50 49944 49914 79207 +60 59924 59805 85109 +70 69960 69747 90046 +80 79974 79703 94157 +90 89939 89828 97468 +100 99900 99900 99900 diff --git a/benchmark/script.gp b/benchmark/script.gp new file mode 100644 index 0000000..ae1645a --- /dev/null +++ b/benchmark/script.gp @@ -0,0 +1,11 @@ +set terminal pngcairo size 800,600 enhanced font "Helvetica,12" +set output 'cache_hit_statistics.png' + +set title "Cache statistics for uniform random integers [0:100)" +set xlabel "Cache size" +set ylabel "Cache hits" +set key bottom right + +plot "cache_hits.dat" u 1:2 title "LRU",\ + "" u 1:3 title "LFU",\ + "" u 1:4 title "Belady"\ diff --git a/main/main.cpp b/main/main.cpp index 5400a7a..3bf01f5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -27,7 +27,7 @@ int main() { std::map cache_hits; for (const auto key : keys) { if (perfect_cache.lookup_update(key, slow_get_page)) - cache_hits["perfect_cache"] += 1; + cache_hits["Belady"] += 1; if (lru.lookup_update(key, slow_get_page)) cache_hits["LRU"] += 1; diff --git a/tests/LFU_test.cpp b/tests/LFU_test.cpp index 1e20f86..8d1d145 100644 --- a/tests/LFU_test.cpp +++ b/tests/LFU_test.cpp @@ -87,9 +87,7 @@ TEST(LFU, eviction_LRU) { size_t nhits(const std::vector &input) { size_t cache_sz = input[0]; - size_t nelem = input[1]; std::vector page_ids(input.begin() + 2, input.end()); - assert(page_ids.size() == nelem); cache::LFU_t lfu{cache_sz}; size_t hits{}; diff --git a/tests/LRU_test.cpp b/tests/LRU_test.cpp index 0d12d15..cdab383 100644 --- a/tests/LRU_test.cpp +++ b/tests/LRU_test.cpp @@ -67,9 +67,7 @@ TEST(LRU, eviction) { size_t nhits(const std::vector &input) { size_t cache_sz = input[0]; - size_t nelem = input[1]; std::vector page_ids(input.begin() + 2, input.end()); - assert(page_ids.size() == nelem); cache::LRU_t lru{cache_sz}; size_t hits{}; diff --git a/tests/perfect_test.cpp b/tests/perfect_test.cpp index 1304617..260d82a 100644 --- a/tests/perfect_test.cpp +++ b/tests/perfect_test.cpp @@ -86,9 +86,7 @@ TEST(Perfect_cache, eviction_2) { size_t nhits(const std::vector &input) { size_t cache_sz = input[0]; - size_t nelem = input[1]; std::vector page_ids(input.begin() + 2, input.end()); - assert(page_ids.size() == nelem); cache::perfect_t perf_cache{cache_sz, page_ids}; size_t hits{};