-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKLruCache.h
More file actions
278 lines (237 loc) · 13.6 KB
/
KLruCache.h
File metadata and controls
278 lines (237 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#pragma once
#include <cstring>
#include <list>
#include <memory>
#include <mutex>
#include <unordered_map>
#include "KICachePolicy.h"
namespace KamaCache
{
// 前向声明
template<typename Key, typename Value> class KLruCache;
template<typename Key, typename Value>
class LruNode
{
private:
Key key_;
Value value_;
size_t accessCount_; // 访问次数 在本KLruCache中没有被用到。在KLruKCache类中是用value_代替访问次数
std::shared_ptr<LruNode<Key, Value>> prev_;
std::shared_ptr<LruNode<Key, Value>> next_;
public:
LruNode(Key key, Value value)
: key_(key)
, value_(value)
, accessCount_(1)
, prev_(nullptr)
, next_(nullptr)
{}
// 提供必要的访问器
Key getKey() const { return key_; }
Value getValue() const { return value_; }
void setValue(const Value& value) { value_ = value; }
size_t getAccessCount() const { return accessCount_; }
void incrementAccessCount() { ++accessCount_; }
friend class KLruCache<Key, Value>;
};
template<typename Key, typename Value>
class KLruCache : public KICachePolicy<Key, Value>
{
public:
using LruNodeType = LruNode<Key, Value>;
using NodePtr = std::shared_ptr<LruNodeType>;
using NodeMap = std::unordered_map<Key, NodePtr>;
KLruCache(int capacity)
: capacity_(capacity)
{
initializeList();
}
~KLruCache() override = default;
// 添加缓存
void put(Key key, Value value) override
{
if (capacity_ <= 0)
return;
std::lock_guard<std::mutex> lock(mutex_);
auto it = nodeMap_.find(key);
if (it != nodeMap_.end())
{
// 如果在当前容器中,则更新value,并调用get方法,代表该数据刚被访问
updateExistingNode(it->second, value);
return ;
}
addNewNode(key, value);
}
bool get(Key key, Value& value) override
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = nodeMap_.find(key);
if (it != nodeMap_.end())
{
moveToMostRecent(it->second);
value = it->second->getValue();
return true;
}
return false;
}
Value get(Key key) override
{
Value value{};
// memset(&value, 0, sizeof(value)); // memset 是按字节设置内存的,对于复杂类型(如 string)使用 memset 可能会破坏对象的内部结构
get(key, value);
return value;
}
// 删除指定元素
void remove(Key key)
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = nodeMap_.find(key);
if (it != nodeMap_.end())
{
removeNode(it->second);
nodeMap_.erase(it);
}
}
private:
void initializeList()
{
// 创建首尾虚拟节点
dummyHead_ = std::make_shared<LruNodeType>(Key(), Value());
dummyTail_ = std::make_shared<LruNodeType>(Key(), Value());
dummyHead_->next_ = dummyTail_;
dummyTail_->prev_ = dummyHead_;
}
void updateExistingNode(NodePtr node, const Value& value)
{
node->setValue(value);
moveToMostRecent(node);
}
void addNewNode(const Key& key, const Value& value)
{
if (nodeMap_.size() >= capacity_)
{
evictLeastRecent();
}
NodePtr newNode = std::make_shared<LruNodeType>(key, value);
insertNode(newNode);
nodeMap_[key] = newNode;
}
// 将该节点移动到最新的位置
void moveToMostRecent(NodePtr node)
{
removeNode(node);
insertNode(node);
}
void removeNode(NodePtr node)
{
node->prev_->next_ = node->next_;
node->next_->prev_ = node->prev_;
}
// 从尾部插入结点
void insertNode(NodePtr node)
{
node->next_ = dummyTail_;
node->prev_ = dummyTail_->prev_;
dummyTail_->prev_->next_ = node;
dummyTail_->prev_ = node;
}
// 驱逐最近最少访问
void evictLeastRecent()
{
NodePtr leastRecent = dummyHead_->next_;
removeNode(leastRecent);
nodeMap_.erase(leastRecent->getKey());
}
private:
int capacity_; // 缓存容量
NodeMap nodeMap_; // key -> Node
std::mutex mutex_;
NodePtr dummyHead_; // 虚拟头结点
NodePtr dummyTail_;
};
// LRU优化:Lru-k版本。 通过继承的方式进行再优化
template<typename Key, typename Value>
class KLruKCache : public KLruCache<Key, Value>
{
public:
KLruKCache(int capacity, int historyCapacity, int k)
: KLruCache<Key, Value>(capacity) // 调用基类构造
, historyList_(std::make_unique<KLruCache<Key, size_t>>(historyCapacity))
, k_(k)
{}
Value get(Key key)
{
// 获取该数据访问次数
int historyCount = historyList_->get(key);
// 如果访问到数据,则更新历史访问记录节点值count++
historyList_->put(key, ++historyCount);
// 从缓存中获取数据,不一定能获取到,因为可能不在缓存中
return KLruCache<Key, Value>::get(key);
}
void put(Key key, Value value)
{
// 先判断是否存在于缓存中,如果存在于则直接覆盖,如果不存在则不直接添加到缓存
if (KLruCache<Key, Value>::get(key) != "")
KLruCache<Key, Value>::put(key, value);
// 如果数据历史访问次数达到上限,则添加入缓存
int historyCount = historyList_->get(key);
historyList_->put(key, ++historyCount);
if (historyCount >= k_)
{
// 移除历史访问记录
historyList_->remove(key);
// 添加入缓存中
KLruCache<Key, Value>::put(key, value);
}
}
private:
int k_; // 进入缓存队列的评判标准
std::unique_ptr<KLruCache<Key, size_t>> historyList_; // 访问数据历史记录(value为访问次数)
};
// lru优化:对lru进行分片,提高高并发使用的性能
template<typename Key, typename Value>
class KHashLruCaches
{
public:
KHashLruCaches(size_t capacity, int sliceNum)
: capacity_(capacity)
, sliceNum_(sliceNum > 0 ? sliceNum : std::thread::hardware_concurrency())
{
size_t sliceSize = std::ceil(capacity / static_cast<double>(sliceNum_)); // 获取每个分片的大小
for (int i = 0; i < sliceNum_; ++i)
{
lruSliceCaches_.emplace_back(new KLruCache<Key, Value>(sliceSize));
}
}
void put(Key key, Value value)
{
// 获取key的hash值,并计算出对应的分片索引
size_t sliceIndex = Hash(key) % sliceNum_;
return lruSliceCaches_[sliceIndex]->put(key, value);
}
bool get(Key key, Value& value)
{
// 获取key的hash值,并计算出对应的分片索引
size_t sliceIndex = Hash(key) % sliceNum_;
return lruSliceCaches_[sliceIndex]->get(key, value);
}
Value get(Key key)
{
Value value;
memset(&value, 0, sizeof(value));
get(key, value);
return value;
}
private:
// 将key转换为对应hash值
size_t Hash(Key key)
{
std::hash<Key> hashFunc;
return hashFunc(key);
}
private:
size_t capacity_; // 总容量
int sliceNum_; // 切片数量
std::vector<std::unique_ptr<KLruCache<Key, Value>>> lruSliceCaches_; // 切片LRU缓存
};
} // namespace KamaCache