diff --git a/DIRECTORY.md b/DIRECTORY.md index f2cb78cba..693d8df32 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -242,6 +242,7 @@ * [Fast Integer Input](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/fast_integer_input.cpp) * [Happy Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/happy_number.cpp) * [Iterative Tree Traversals](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/iterative_tree_traversals.cpp) + * [Lru Cache](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/lru_cache.cpp) * [Matrix Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/matrix_exponentiation.cpp) * [Palindrome Of Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/palindrome_of_number.cpp) * [Paranthesis Matching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/paranthesis_matching.cpp) diff --git a/hashing/chaining.cpp b/hashing/chaining.cpp index cdfebe78f..9c5a2a076 100644 --- a/hashing/chaining.cpp +++ b/hashing/chaining.cpp @@ -24,7 +24,7 @@ class hash_chain { }; std::vector> head; ///< array of nodes - int _mod; ///< modulus of the class + int _mod; ///< modulus of the class public: /** @@ -164,7 +164,8 @@ int main() { case 3: std::cout << "Enter element to generate hash = " << std::endl; std::cin >> x; - std::cout << "Hash of " << x << " is = " << mychain.hash(x) << std::endl; + std::cout << "Hash of " << x << " is = " << mychain.hash(x) + << std::endl; break; case 4: mychain.display(); diff --git a/others/lru_cache.cpp b/others/lru_cache.cpp new file mode 100644 index 000000000..f9cd3caec --- /dev/null +++ b/others/lru_cache.cpp @@ -0,0 +1,268 @@ +/** + * @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::list +#include /// for std::unordered_map + +/** + * @namespace others + * @brief Other algorithms + */ +namespace others { +/** + * @namespace lru_cache + * @brief Implementation of the [LRU caching + * algorithm](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)) + */ +namespace lru_cache { +/** + * @brief LRU cache class + */ +class LRUCache { + uint64_t 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 + + uint64_t hits = + 0; ///< Total number of hits, or total number of times a page + ///< was found in cache. + uint64_t pageFault = 0; ///< Total number of miss/page fault, or total + ///< number of times a page was not found in cache + + public: + /** + * @brief Constructor, Initialize thee LRU class with page frame. + * @param pf Page frame or total size of cache. + * */ + explicit LRUCache(uint64_t 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(uint64_t 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 + uint64_t 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 (uint64_t &it : cache) { + std::cout << it << " "; + } + std::cout << std::endl; + } + /** + * @brief A function to get page hits + * @returns int + * */ + uint64_t getHits() const { return hits; } + /** + * @brief A function to get page fault + * @returns int + * */ + uint64_t getPageFault() const { return pageFault; } +}; + +} // namespace lru_cache +} // namespace others + +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() { + uint64_t expected_hits = 2; + uint64_t expected_pageFault = 4; + + log("Running Test-1..."); + + others::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.getHits() == expected_hits && + cache.getPageFault() == 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() { + uint64_t expected_hits = 4; + uint64_t expected_pageFault = 2; + + log("Running Test-2..."); + + others::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.getHits() == expected_hits && + cache.getPageFault() == 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() { + uint64_t expected_hits = 1; + uint64_t expected_pageFault = 5; + + log("Running Test-3..."); + + others::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.getHits() == expected_hits && + cache.getPageFault() == 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 + * @returns 0 on exit + */ +int main() { + lru_tests::run_tests(); + + // Usage + others::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.getHits() + << " Miss: " << cache.getPageFault() << std::endl; + return 0; +}