VeloGraphX
High-performance dynamic graph analytics in C++20
Loading...
Searching...
No Matches
partition_cache.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <list>
6#include <optional>
7#include <stdexcept>
8#include <unordered_map>
9#include <utility>
10#include <vector>
11
13
14namespace velographx {
15
16using PartitionId = std::uint32_t;
17
19 std::size_t resident_bytes{0};
20 std::size_t hits{0};
21 std::size_t misses{0};
22 std::size_t evictions{0};
23};
24
25template <class Payload = std::vector<std::uint8_t>>
27 public:
28 explicit PartitionCache(MemoryBudget budget, std::size_t algorithm_state_bytes = 0)
29 : capacity_(budget.resident_limit(budget.bytes(), algorithm_state_bytes)) {}
30
31 explicit PartitionCache(std::size_t capacity_bytes) : capacity_(capacity_bytes) {}
32
33 [[nodiscard]] std::size_t capacity_bytes() const noexcept { return capacity_; }
34 [[nodiscard]] std::size_t resident_bytes() const noexcept { return stats_.resident_bytes; }
35 [[nodiscard]] std::size_t size() const noexcept { return entries_.size(); }
36 [[nodiscard]] const PartitionCacheStats& stats() const noexcept { return stats_; }
37
38 void clear() {
39 entries_.clear();
40 lru_.clear();
41 stats_.resident_bytes = 0;
42 }
43
44 [[nodiscard]] bool contains(PartitionId id) const noexcept {
45 return entries_.find(id) != entries_.end();
46 }
47
48 const Payload* get(PartitionId id) {
49 auto it = entries_.find(id);
50 if (it == entries_.end()) {
51 ++stats_.misses;
52 return nullptr;
53 }
54 ++stats_.hits;
55 touch(it);
56 return &it->second.payload;
57 }
58
59 void put(PartitionId id, Payload payload, std::size_t bytes) {
60 if (bytes > capacity_) {
61 throw std::length_error("partition exceeds cache capacity");
62 }
63
64 auto existing = entries_.find(id);
65 if (existing != entries_.end()) {
66 stats_.resident_bytes -= existing->second.bytes;
67 lru_.erase(existing->second.lru_it);
68 entries_.erase(existing);
69 }
70
71 while (stats_.resident_bytes + bytes > capacity_ && !lru_.empty()) {
72 evict_one();
73 }
74
75 lru_.push_front(id);
76 entries_.emplace(id, Entry{std::move(payload), bytes, lru_.begin()});
77 stats_.resident_bytes += bytes;
78 }
79
80 bool erase(PartitionId id) {
81 auto it = entries_.find(id);
82 if (it == entries_.end()) return false;
83 stats_.resident_bytes -= it->second.bytes;
84 lru_.erase(it->second.lru_it);
85 entries_.erase(it);
86 return true;
87 }
88
89 private:
90 struct Entry {
91 Payload payload;
92 std::size_t bytes{0};
93 typename std::list<PartitionId>::iterator lru_it;
94 };
95
96 using Map = std::unordered_map<PartitionId, Entry>;
97
98 void touch(typename Map::iterator it) {
99 lru_.erase(it->second.lru_it);
100 lru_.push_front(it->first);
101 it->second.lru_it = lru_.begin();
102 }
103
104 void evict_one() {
105 const auto victim = lru_.back();
106 lru_.pop_back();
107 auto it = entries_.find(victim);
108 if (it != entries_.end()) {
109 stats_.resident_bytes -= it->second.bytes;
110 entries_.erase(it);
111 ++stats_.evictions;
112 }
113 }
114
115 std::size_t capacity_{0};
116 std::list<PartitionId> lru_;
117 Map entries_;
118 PartitionCacheStats stats_;
119};
120
121} // namespace velographx
PartitionCache(MemoryBudget budget, std::size_t algorithm_state_bytes=0)
const PartitionCacheStats & stats() const noexcept
std::size_t resident_bytes() const noexcept
bool contains(PartitionId id) const noexcept
bool erase(PartitionId id)
const Payload * get(PartitionId id)
PartitionCache(std::size_t capacity_bytes)
std::size_t size() const noexcept
void put(PartitionId id, Payload payload, std::size_t bytes)
std::size_t capacity_bytes() const noexcept
std::uint32_t PartitionId