-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Andrew Naplavkov
committed
May 20, 2023
1 parent
e96a600
commit e9d6fcb
Showing
5 changed files
with
250 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Andrew Naplavkov | ||
|
||
#ifndef STEP20_LEAST_FREQUENTLY_USED_HPP | ||
#define STEP20_LEAST_FREQUENTLY_USED_HPP | ||
|
||
#include <list> | ||
#include <unordered_map> | ||
|
||
namespace step20::least_frequently_used { | ||
|
||
/// An O(1) algorithm for implementing the LFU cache eviction scheme | ||
template <class Key, | ||
class T, | ||
class Hash = std::hash<Key>, | ||
class KeyEqual = std::equal_to<Key>> | ||
class cache { | ||
struct item_type; | ||
using item_list = std::list<item_type>; | ||
using item_iterator = typename item_list::iterator; | ||
struct freq_type; | ||
using freq_list = std::list<freq_type>; | ||
using freq_iterator = typename freq_list::iterator; | ||
|
||
struct item_type { | ||
freq_iterator parent; | ||
Key key; | ||
T val; | ||
}; | ||
|
||
struct freq_type { | ||
size_t n; | ||
item_list items; | ||
}; | ||
|
||
freq_list list_; | ||
std::unordered_map<Key, item_iterator, Hash, KeyEqual> map_; | ||
size_t capacity_; | ||
|
||
freq_iterator emplace(freq_iterator it, size_t n) | ||
{ | ||
return it != list_.end() && it->n == n ? it : list_.emplace(it, n); | ||
} | ||
|
||
public: | ||
explicit cache(size_t capacity) : capacity_(capacity) {} | ||
|
||
/// @return nullptr if key is not found | ||
const T* find(const Key& key) | ||
{ | ||
auto it = map_.find(key); | ||
if (it == map_.end()) | ||
return nullptr; | ||
auto item = it->second; | ||
auto freq = item->parent; | ||
auto next = emplace(std::next(freq), freq->n + 1); | ||
try { | ||
next->items.splice(next->items.end(), freq->items, item); | ||
} | ||
catch (...) { | ||
if (next->items.empty()) | ||
list_.erase(next); | ||
throw; | ||
} | ||
item->parent = next; | ||
if (freq->items.empty()) | ||
list_.erase(freq); | ||
return std::addressof(item->val); | ||
} | ||
|
||
/// Basic exception guarantee. | ||
/// Evicted item is not restored if an exception occurs. | ||
void insert_or_assign(const Key& key, const T& val) | ||
{ | ||
if (auto ptr = find(key)) { | ||
*const_cast<T*>(ptr) = val; | ||
return; | ||
} | ||
if (!map_.empty() && map_.size() >= capacity_) { | ||
auto freq = list_.begin(); | ||
auto item = freq->items.begin(); | ||
map_.erase(item->key); | ||
freq->items.erase(item); | ||
if (freq->items.empty()) | ||
list_.erase(freq); | ||
} | ||
auto freq = emplace(list_.begin(), 1); | ||
bool was_freq_empty = freq->items.empty(); | ||
try { | ||
auto item = freq->items.emplace(freq->items.end(), freq, key, val); | ||
map_.emplace(key, item); | ||
} | ||
catch (...) { | ||
if (was_freq_empty) | ||
list_.erase(freq); | ||
throw; | ||
} | ||
} | ||
}; | ||
|
||
} // namespace step20::least_frequently_used | ||
|
||
#endif // STEP20_LEAST_FREQUENTLY_USED_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Andrew Naplavkov | ||
|
||
#ifndef STEP20_LEAST_RECENTLY_USED_HPP | ||
#define STEP20_LEAST_RECENTLY_USED_HPP | ||
|
||
#include <list> | ||
#include <unordered_map> | ||
|
||
namespace step20::least_recently_used { | ||
namespace detail { | ||
|
||
template <class Key, | ||
class T, | ||
class Hash = std::hash<Key>, | ||
class KeyEqual = std::equal_to<Key>> | ||
class linked_hash_map { | ||
public: | ||
using value_type = std::pair<const Key, T>; | ||
using list_type = std::list<value_type>; | ||
using iterator = typename list_type::iterator; | ||
|
||
auto size() const { return list_.size(); } | ||
iterator begin() { return list_.begin(); } | ||
iterator end() { return list_.end(); } | ||
void transfer(iterator from, iterator to) { list_.splice(to, list_, from); } | ||
|
||
iterator find(const Key& key) | ||
{ | ||
auto it = map_.find(key); | ||
return it == map_.end() ? list_.end() : it->second; | ||
} | ||
|
||
iterator erase(iterator it) | ||
{ | ||
map_.erase(it->first); | ||
return list_.erase(it); | ||
} | ||
|
||
template <class M> | ||
std::pair<iterator, bool> emplace(iterator it, const Key& key, M&& val) | ||
{ | ||
if (auto pos = find(key); pos != end()) | ||
return {pos, false}; | ||
it = list_.emplace(it, key, std::forward<M>(val)); | ||
try { | ||
map_.emplace(key, it); | ||
return {it, true}; | ||
} | ||
catch (...) { | ||
list_.erase(it); | ||
throw; | ||
} | ||
} | ||
|
||
private: | ||
list_type list_; | ||
std::unordered_map<Key, iterator, Hash, KeyEqual> map_; | ||
}; | ||
|
||
} // namespace detail | ||
|
||
/// An O(1) algorithm for implementing the LRU cache eviction scheme | ||
template <class Key, | ||
class T, | ||
class Hash = std::hash<Key>, | ||
class KeyEqual = std::equal_to<Key>> | ||
class cache { | ||
detail::linked_hash_map<Key, T, Hash, KeyEqual> map_; | ||
size_t capacity_; | ||
|
||
public: | ||
explicit cache(size_t capacity) : capacity_(capacity) {} | ||
|
||
/// @return nullptr if key is not found | ||
const T* find(const Key& key) | ||
{ | ||
auto it = map_.find(key); | ||
if (it == map_.end()) | ||
return nullptr; | ||
map_.transfer(it, map_.end()); | ||
return std::addressof(it->second); | ||
} | ||
|
||
/// Basic exception guarantee. | ||
/// Inserted item remains if an exception occurs. | ||
void insert_or_assign(const Key& key, const T& val) | ||
{ | ||
auto [it, success] = map_.emplace(map_.end(), key, val); | ||
if (!success) { | ||
map_.transfer(it, map_.end()); | ||
it->second = val; | ||
return; | ||
} | ||
while (map_.size() > capacity_) | ||
map_.erase(map_.begin()); | ||
} | ||
}; | ||
|
||
} // namespace step20::least_recently_used | ||
|
||
#endif // STEP20_LEAST_RECENTLY_USED_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters