From aa8173bb324024c0c8b3be2e7d377f5e0c3e03d2 Mon Sep 17 00:00:00 2001 From: foo290 Date: Thu, 12 Aug 2021 13:39:31 +0530 Subject: [PATCH] feat: add lru caching algorithm --- others/lru_cache.cpp | 234 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 others/lru_cache.cpp diff --git a/others/lru_cache.cpp b/others/lru_cache.cpp new file mode 100644 index 000000000..a046ca69e --- /dev/null +++ b/others/lru_cache.cpp @@ -0,0 +1,234 @@ +/** + * @file + * @brief An implementation of + * [LRU Cache](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)). + * Lru is a part of cache algorithms (also frequently called cache replacement algorithms or cache replacement policies). + * + * ### Logic: + * * Discards the least recently used items first. + * * This algorithm requires keeping track of what was used when, which is expensive if one wants to make sure the + * algorithm always discards the least recently used item. + * * General implementations of this technique require keeping "age bits" for cache-lines and track the + * "Least Recently Used" cache-line based on age-bits. + * * In such an implementation, every time a cache-line is used, the age of all other cache-lines changes + * + * ### Algorithm explanation: + * For a cache of page frame x: + * * Check if the page is present in cache. + * * If not present, then check is the cache is full or not: + * * If the cache is full, REMOVE the last element from the cache. + * * If the element is present in cache, then shift that element to first position in cache from its original position. + * * This way you can keep the least recently used elements in the last and most recently used in front of the cache. + * + * Every time a requested page is not found in cache, that is a miss or page fault, and if the page is present in + * cache, then its a hit. + * + * ## Data Structure used: + * * In the algorithm below we used two different data structure, one is linked list and other one is a hash map + * * The linked list is used to contain the pages and the hash map contains the pages and their address. + * * Every time a new page is requested, we first check in the hash map if the page is present or not. + * * If not present, and the cache is full, we simply delete the last entry in the cache. + * * If present, we shift that page from its current location to beginning of the cache and update the address in + * hash map for that page. + * + * @author [Nitin Sharma](https://github.com/foo290) + * */ + + +#include /// for assert +#include /// for IO Operations +#include /// for std::vector + +/** + * @namespace lru_cache + * @brief Implementation of LRU caching algorithm + */ +namespace lru_cache { + +/** + * @brief LRU cache class + */ + class LRUCache { + int pageFrame; ///< Page frame, or total size of the cache. + std::list cache; ///< Cache linked list (using the STL) + std::unordered_map::iterator> pageMap; ///< Hash map containing pages and their addresses + + public: + int hits = 0; ///< Total number of hits, or total number of times a page was found in cache. + int pageFault = 0; ///< Total number of miss/page fault, or total number of times a page was not found in cache + +/** + * @brief Constructor, Initialize thee LRU class with page frame. + * @param pf Page frame or total size of cache. + * */ + explicit LRUCache(int pf) { + pageFrame = pf; + } + +/** + * @brief Refer to a page, or request a page from memory. + * @param page The page that you are referring to. + * @returns void + * */ + void refer(int page) { + // If the page requested not in cache. + if (pageMap.find(page) == pageMap.end()) { + pageFault++; ///< Increase the page fault by one. + + // Check if the cache is full + if (cache.size() == pageFrame) { + + // delete the last page from cache + int lastPage = cache.back(); + cache.pop_back(); + pageMap.erase(lastPage); + } + } + // The requested page is in the cache + else { + hits++; + // present in cache, erase from current position to bring in front + cache.erase(pageMap[page]); + } + // Push it in the front of the cache and update the page reference in page map. + cache.push_front(page); + pageMap[page] = cache.begin(); + } + +/** + * @brief A function to display the current cache + * @returns Void + * */ + void display() { + for (int &it : cache) { + std::cout << it << " "; + } + std::cout << std::endl; + } + }; +} // lru_cache + +namespace lru_tests { +/** + * @brief A function to print given message on console. + * @tparam T Type of the given message. + * @returns void + * */ + template + void log(T msg) { + // It's just to avoid writing cout and endl + std::cout << "[TESTS] : ---> " << msg << std::endl; + } + +/** + * @brief A simple test case + * The assert statement will check expected hist and miss to resultant hits and miss + * @returns void + * */ + static void test_1() { + int expected_hits = 2; + int expected_pageFault = 4; + + log("Running Test-1..."); + + lru_cache::LRUCache cache(4); + cache.refer(1); + cache.refer(2); + cache.refer(5); + cache.refer(1); + cache.refer(4); + cache.refer(5); + + log("Checking assert statement..."); + assert(cache.hits == expected_hits && cache.pageFault == expected_pageFault); + log("Assert successful!"); + log("Test-1 complete!"); + } + +/** + * @brief A test case contains hits more than cache size + * The assert statement will check expected hist and miss to resultant hits and miss + * @returns void + * */ + static void test_2() { + int expected_hits = 4; + int expected_pageFault = 2; + + log("Running Test-2..."); + + lru_cache::LRUCache cache(4); + cache.refer(1); + cache.refer(1); + cache.refer(1); + cache.refer(1); + cache.refer(1); + cache.refer(5); + + log("Checking assert statement..."); + assert(cache.hits == expected_hits && cache.pageFault == expected_pageFault); + log("Assert successful!"); + log("Test-2 complete!"); + } + +/** + * @brief A simple test case + * The assert statement will check expected hist and miss to resultant hits and miss + * @returns void + * */ + static void test_3() { + int expected_hits = 1; + int expected_pageFault = 5; + + log("Running Test-3..."); + + lru_cache::LRUCache cache(4); + cache.refer(1); + cache.refer(2); + cache.refer(3); + cache.refer(4); + cache.refer(5); + cache.refer(5); + + log("Checking assert statement..."); + assert(cache.hits == expected_hits && cache.pageFault == expected_pageFault); + log("Assert successful!"); + log("Test-3 complete!"); + } + +/** + * @brief A function to invoke all test cases + * @returns void + * */ + static void run_tests(){ + test_1(); + test_2(); + test_3(); + log(""); + log("TESTS COMPLETED!"); + } +} // namespace lru_tests + +/** + * @brief Main function + * @param argc commandline argument count (ignored) + * @param argv commandline array of arguments (ignored) + * @returns 0 on exit + */ +int main() { + lru_tests::run_tests(); + + // Usage + lru_cache::LRUCache cache(4); + cache.refer(1); + cache.refer(2); + cache.refer(3); + cache.refer(4); + cache.refer(5); + cache.refer(5); + + cache.display(); + + std::cout<<"Hits: "<< cache.hits << " Miss: " << cache.pageFault; + return 0; +} +