Skip to content

Instantly share code, notes, and snippets.

@AdjWang
Last active October 12, 2023 08:46
Show Gist options
  • Save AdjWang/57cda636dadbb4e9e79a1830ef2b5a09 to your computer and use it in GitHub Desktop.
Save AdjWang/57cda636dadbb4e9e79a1830ef2b5a09 to your computer and use it in GitHub Desktop.
A LRU cache sitting on shared memory. Requires boost.
// 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