Last active
October 12, 2023 08:46
-
-
Save AdjWang/57cda636dadbb4e9e79a1830ef2b5a09 to your computer and use it in GitHub Desktop.
A LRU cache sitting on shared memory. Requires boost.
This file contains hidden or 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
// Derived from: https://www.boost.org/doc/libs/1_67_0/boost/compute/detail/lru_cache.hpp | |
// CXXFLAGS = -std=c++17 -Wall -Wextra -Werror -Wno-unused-parameter -fdata-sections -ffunction-sections (append -D__OWNER for owner) | |
// LDFLAGS = -lrt -lpthread -Wl,--gc-sections | |
#pragma once | |
#include <boost/optional.hpp> | |
#include <boost/interprocess/managed_shared_memory.hpp> | |
#include <boost/interprocess/containers/list.hpp> | |
#include <boost/interprocess/containers/map.hpp> | |
#include <boost/interprocess/containers/string.hpp> | |
using namespace boost::interprocess; | |
// a cache which evicts the least recently used item when it is full | |
class LRUCache { | |
public: | |
typedef managed_shared_memory::allocator<char>::type char_allocator_t; | |
typedef basic_string<char, std::char_traits<char>, char_allocator_t> shm_string_t; | |
typedef allocator<shm_string_t, managed_shared_memory::segment_manager> list_allocator_t; | |
typedef list<shm_string_t, list_allocator_t> list_t; | |
typedef std::pair<shm_string_t, typename list_t::iterator> mapped_t; | |
typedef std::pair<const shm_string_t, mapped_t> value_t; | |
typedef allocator<value_t, managed_shared_memory::segment_manager> map_allocator_t; | |
typedef map<shm_string_t, mapped_t, std::less<shm_string_t>, map_allocator_t> map_t; | |
LRUCache(size_t capacity) | |
: capacity_(capacity), | |
#ifdef __OWNER | |
segment_(create_only, "MySharedMemory", capacity), | |
ca_(segment_.get_allocator<char>()), | |
list_alloc_inst_(segment_.get_segment_manager()), | |
map_alloc_inst_(segment_.get_segment_manager()), | |
lru_list_(segment_.construct<list_t>("MyList")(list_alloc_inst_)), | |
lru_map_(segment_.construct<map_t>("MyMap")(std::less<shm_string_t>(), | |
map_alloc_inst_)) | |
#else | |
segment_(open_only, "MySharedMemory"), | |
ca_(segment_.get_allocator<char>()), | |
lru_list_(segment_.find<list_t>("MyList").first), | |
lru_map_(segment_.find<map_t>("MyMap").first) | |
#endif | |
{ | |
if (lru_list_ == nullptr) { | |
printf("failed to construct list\n"); | |
exit(1); | |
} | |
if (lru_map_ == nullptr) { | |
printf("failed to construct map\n"); | |
exit(1); | |
} | |
} | |
~LRUCache() { | |
#ifdef __OWNER | |
shared_memory_object::remove("MySharedMemory"); | |
#endif | |
} | |
size_t size() const { return lru_map_->size(); } | |
size_t capacity() const { return capacity_; } | |
bool empty() const { return lru_map_->empty(); } | |
bool contains(const std::string& key) { | |
shm_string_t shm_key(key.c_str(), ca_); | |
return lru_map_->find(shm_key) != lru_map_->end(); | |
} | |
void insert(const std::string& key, const std::string& value) { | |
shm_string_t shm_key(key.c_str(), ca_); | |
typename map_t::iterator i = lru_map_->find(shm_key); | |
if (i == lru_map_->end()) { | |
// insert item into the cache, but first check if it is full | |
if (size() >= capacity_) { | |
// cache is full, evict the least recently used item | |
evict(); | |
} | |
// insert the new item | |
lru_list_->push_front(shm_key); | |
shm_string_t shm_value(value.begin(), value.end(), ca_); | |
lru_map_->emplace(shm_key, std::make_pair(shm_value, lru_list_->begin())); | |
} | |
} | |
boost::optional<std::string> get(const std::string& key) { | |
// lookup value in the cache | |
shm_string_t shm_key(key.c_str(), ca_); | |
typename map_t::iterator i = lru_map_->find(shm_key); | |
if (i == lru_map_->end()) { | |
// value not in cache | |
return boost::none; | |
} | |
// return the value, but first update its place in the most | |
// recently used list | |
typename list_t::iterator j = i->second.second; | |
if (j != lru_list_->begin()) { | |
// move item to the front of the most recently used list | |
lru_list_->erase(j); | |
lru_list_->push_front(shm_key); | |
// update iterator in map | |
j = lru_list_->begin(); | |
const shm_string_t& value = i->second.first; | |
lru_map_->emplace(shm_key, std::make_pair(value, j)); | |
// return the value | |
return std::string(value.begin(), value.end()); | |
} else { | |
// the item is already at the front of the most recently | |
// used list so just return it | |
return std::string(i->second.first.begin(), i->second.first.end()); | |
} | |
} | |
void clear() { | |
lru_map_->clear(); | |
lru_list_->clear(); | |
} | |
private: | |
size_t capacity_; | |
managed_shared_memory segment_; | |
char_allocator_t ca_; | |
#ifdef __OWNER | |
list_allocator_t list_alloc_inst_; | |
map_allocator_t map_alloc_inst_; | |
#endif | |
list_t* lru_list_; | |
map_t* lru_map_; | |
void evict() { | |
// evict item from the end of most recently used list | |
typename list_t::iterator i = --lru_list_->end(); | |
lru_map_->erase(*i); | |
lru_list_->erase(i); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment