diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 7ab1b4d..d2840a2 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -14,7 +14,7 @@ jobs: strategy: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. - fail-fast: false + fail-fast: true # Set up a matrix to run the following 3 configurations: # 1. @@ -31,7 +31,7 @@ jobs: c_compiler: cl cpp_compiler: cl - os: ubuntu-latest - c_compiler: gcc + c_compilel: gcc cpp_compiler: g++ - os: ubuntu-latest c_compiler: clang diff --git a/CMakeLists.txt b/CMakeLists.txt index ed582c8..4c3131a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) add_subdirectory(main) -add_subdirectory(hello_world_lib) +add_subdirectory(LRU) include(CTest) diff --git a/LRU/CMakeLists.txt b/LRU/CMakeLists.txt new file mode 100644 index 0000000..b460d1f --- /dev/null +++ b/LRU/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(LRU INTERFACE) + +target_sources(LRU + INTERFACE + FILE_SET HEADERS + FILES + LRU.hpp +) diff --git a/LRU/LRU.hpp b/LRU/LRU.hpp new file mode 100644 index 0000000..16f64cd --- /dev/null +++ b/LRU/LRU.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace cache { + +template class LRU_t { + size_t sz_; + std::list> cache_; + + using ListIt = typename decltype(cache_)::iterator; + std::unordered_map hash_; + +public: + explicit LRU_t(size_t sz) : sz_{sz} {} + + bool full() const { return cache_.size() == sz_; }; + + template bool lookup_update(KeyT key, F slow_get_page) { + auto hit = hash_.find(key); + if (hit == hash_.end()) { // not found + if (full()) { + hash_.erase(cache_.back().first); + cache_.pop_back(); + } + cache_.emplace_front(key, slow_get_page(key)); + hash_.emplace(key, cache_.begin()); + return false; + } + + auto eltit = hit->second; + cache_.splice(cache_.begin(), cache_, eltit); + return true; + } +}; + +} // namespace cache diff --git a/hello_world_lib/CMakeLists.txt b/hello_world_lib/CMakeLists.txt deleted file mode 100644 index 2326389..0000000 --- a/hello_world_lib/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_library(hello_world_lib) - -target_sources(hello_world_lib - PRIVATE - hello.cpp - - PUBLIC - FILE_SET HEADERS - FILES - hello.hpp -) diff --git a/hello_world_lib/hello.cpp b/hello_world_lib/hello.cpp deleted file mode 100644 index 9c6e92a..0000000 --- a/hello_world_lib/hello.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "hello.hpp" - -std::string hello() { return std::string("Hello, world!"); } diff --git a/hello_world_lib/hello.hpp b/hello_world_lib/hello.hpp deleted file mode 100644 index 60c1132..0000000 --- a/hello_world_lib/hello.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -std::string hello(); diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 31a6e4d..c0efbd5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -4,8 +4,13 @@ target_sources(main main.cpp ) +target_compile_options(main + PRIVATE + $<$:/W4 /WX> + $<$:-Wall -Wextra -Werror> +) target_link_libraries(main PRIVATE - hello_world_lib + LRU ) diff --git a/main/main.cpp b/main/main.cpp index b278a92..c7a109c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,8 +1,32 @@ -#include "hello.hpp" +#include "LRU.hpp" +#include #include +#include +#include + +int slow_get_page(int key) { return key; } + int main() { - std::string str = hello(); + std::map cache_hits; + size_t sz; + int nelts; + + std::cin >> sz >> nelts; + + cache::LRU_t lru{sz}; + + for (int i = 0; i < nelts; ++i) { + int p; + std::cin >> p; + assert(std::cin.good()); + if (lru.lookup_update(p, slow_get_page)) + cache_hits["LRU"] += 1; + } - std::cout << str << std::endl; + std::cout << "Cache type\tN hits\n"; + for(auto [cache_type, nhits]: cache_hits){ + std::cout << cache_type << "\t" << nhits << std::endl; + } + return 0; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cd0e7f2..eb26a4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,14 +9,21 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -add_executable(hello_test - hello_test.cc +add_executable(caches_test + LRU_test.cpp ) -target_link_libraries(hello_test + +target_compile_options(caches_test + PRIVATE + $<$:/W4 /WX> + $<$:-Wall -Wextra -Werror> +) + +target_link_libraries(caches_test PRIVATE - hello_world_lib + LRU GTest::gtest_main ) include(GoogleTest) -gtest_discover_tests(hello_test) +gtest_discover_tests(caches_test) diff --git a/tests/LRU_test.cpp b/tests/LRU_test.cpp new file mode 100644 index 0000000..342bd6a --- /dev/null +++ b/tests/LRU_test.cpp @@ -0,0 +1,69 @@ +#include "LRU.hpp" +#include + +namespace { +struct page_t { + int id; +}; + +page_t slow_get_page(int page_id) { return page_t{page_id}; } +} // namespace + +TEST(LRU, no_hit) { + cache::LRU_t lru{3}; + + // cache is empty, no hits + EXPECT_FALSE(lru.lookup_update(1, slow_get_page)); +} + +TEST(LRU, one_hit) { + cache::LRU_t lru{3}; + + EXPECT_FALSE(lru.lookup_update(1, slow_get_page)); + EXPECT_TRUE(lru.lookup_update(1, slow_get_page)); // cache hit +} + +TEST(LRU, is_full) { + cache::LRU_t lru{2}; + + lru.lookup_update(1, slow_get_page); // [1] + lru.lookup_update(1, slow_get_page); // [1] + EXPECT_FALSE(lru.full()); + + lru.lookup_update(2, slow_get_page); // [2, 1] + EXPECT_TRUE(lru.full()); +} + +TEST(LRU, no_eviction) { + cache::LRU_t lru{3}; + + EXPECT_FALSE(lru.lookup_update(1, slow_get_page)); // [1] + EXPECT_FALSE(lru.lookup_update(2, slow_get_page)); // [2, 1] + EXPECT_TRUE(lru.lookup_update(1, slow_get_page)); // [1, 2] + EXPECT_FALSE(lru.lookup_update(3, slow_get_page)); // [3, 1, 2] + EXPECT_TRUE(lru.lookup_update(1, slow_get_page)); // [1, 3, 2] + EXPECT_TRUE(lru.lookup_update(2, slow_get_page)); // [2, 1, 3] +} + +TEST(LRU, eviction) { + cache::LRU_t lru{2}; + + lru.lookup_update(1, slow_get_page); // [1] + lru.lookup_update(2, slow_get_page); // [2, 1] + lru.lookup_update(3, slow_get_page); // [3, 2] + + EXPECT_FALSE( + lru.lookup_update(1, slow_get_page)); // 1 is not present before lookup +} + +TEST(LRU, example_from_lecture) { + cache::LRU_t lru{2}; + + size_t hits{}; + for (int page_id : {1, 2, 1, 2, 1, 2}) { + if (lru.lookup_update(page_id, slow_get_page)) + hits += 1; + } + + EXPECT_EQ(4, hits); +} diff --git a/tests/hello_test.cc b/tests/hello_test.cc deleted file mode 100644 index b6e2278..0000000 --- a/tests/hello_test.cc +++ /dev/null @@ -1,8 +0,0 @@ -#include "hello.hpp" -#include - -// Demonstrate some basic assertions. -TEST(HelloTest, BasicAssertions) { - // Expect equality. - EXPECT_EQ(hello(), std::string("Hello, world!")); -}