/root/doris/be/src/olap/lru_cache.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
4 | | |
5 | | #include "olap/lru_cache.h" |
6 | | |
7 | | #include <stdlib.h> |
8 | | |
9 | | #include <mutex> |
10 | | #include <new> |
11 | | #include <sstream> |
12 | | #include <string> |
13 | | |
14 | | #include "gutil/bits.h" |
15 | | #include "util/doris_metrics.h" |
16 | | #include "util/time.h" |
17 | | |
18 | | using std::string; |
19 | | using std::stringstream; |
20 | | |
21 | | namespace doris { |
22 | | |
23 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_capacity, MetricUnit::BYTES); |
24 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_usage, MetricUnit::BYTES); |
25 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_element_count, MetricUnit::NOUNIT); |
26 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_usage_ratio, MetricUnit::NOUNIT); |
27 | | DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(cache_lookup_count, MetricUnit::OPERATIONS); |
28 | | DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(cache_hit_count, MetricUnit::OPERATIONS); |
29 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(cache_hit_ratio, MetricUnit::NOUNIT); |
30 | | |
31 | 662k | uint32_t CacheKey::hash(const char* data, size_t n, uint32_t seed) const { |
32 | | // Similar to murmur hash |
33 | 662k | const uint32_t m = 0xc6a4a793; |
34 | 662k | const uint32_t r = 24; |
35 | 662k | const char* limit = data + n; |
36 | 662k | uint32_t h = seed ^ (n * m); |
37 | | |
38 | | // Pick up four bytes at a time |
39 | 1.59M | while (data + 4 <= limit) { Branch (39:12): [True: 937k, False: 662k]
|
40 | 937k | uint32_t w = _decode_fixed32(data); |
41 | 937k | data += 4; |
42 | 937k | h += w; |
43 | 937k | h *= m; |
44 | 937k | h ^= (h >> 16); |
45 | 937k | } |
46 | | |
47 | | // Pick up remaining bytes |
48 | 662k | switch (limit - data) { |
49 | 9.06k | case 3: Branch (49:5): [True: 9.06k, False: 653k]
|
50 | 9.06k | h += static_cast<unsigned char>(data[2]) << 16; |
51 | | |
52 | | // fall through |
53 | 11.7k | case 2: Branch (53:5): [True: 2.71k, False: 659k]
|
54 | 11.7k | h += static_cast<unsigned char>(data[1]) << 8; |
55 | | |
56 | | // fall through |
57 | 14.5k | case 1: Branch (57:5): [True: 2.74k, False: 659k]
|
58 | 14.5k | h += static_cast<unsigned char>(data[0]); |
59 | 14.5k | h *= m; |
60 | 14.5k | h ^= (h >> r); |
61 | 14.5k | break; |
62 | | |
63 | 647k | default: Branch (63:5): [True: 647k, False: 14.5k]
|
64 | 647k | break; |
65 | 662k | } |
66 | | |
67 | 662k | return h; |
68 | 662k | } |
69 | | |
70 | 484 | Cache::~Cache() {} |
71 | | |
72 | 7.87k | HandleTable::~HandleTable() { |
73 | 7.87k | delete[] _list; |
74 | 7.87k | } |
75 | | |
76 | | // LRU cache implementation |
77 | 342k | LRUHandle* HandleTable::lookup(const CacheKey& key, uint32_t hash) { |
78 | 342k | return *_find_pointer(key, hash); |
79 | 342k | } |
80 | | |
81 | 320k | LRUHandle* HandleTable::insert(LRUHandle* h) { |
82 | 320k | LRUHandle** ptr = _find_pointer(h->key(), h->hash); |
83 | 320k | LRUHandle* old = *ptr; |
84 | 320k | h->next_hash = old ? old->next_hash : nullptr; Branch (84:20): [True: 16.0k, False: 304k]
|
85 | 320k | *ptr = h; |
86 | | |
87 | 320k | if (old == nullptr) { Branch (87:9): [True: 304k, False: 16.0k]
|
88 | 304k | ++_elems; |
89 | 304k | if (_elems > _length) { Branch (89:13): [True: 100, False: 303k]
|
90 | | // Since each cache entry is fairly large, we aim for a small |
91 | | // average linked list length (<= 1). |
92 | 100 | _resize(); |
93 | 100 | } |
94 | 304k | } |
95 | | |
96 | 320k | return old; |
97 | 320k | } |
98 | | |
99 | 16 | LRUHandle* HandleTable::remove(const CacheKey& key, uint32_t hash) { |
100 | 16 | LRUHandle** ptr = _find_pointer(key, hash); |
101 | 16 | LRUHandle* result = *ptr; |
102 | | |
103 | 16 | if (result != nullptr) { Branch (103:9): [True: 5, False: 11]
|
104 | 5 | *ptr = result->next_hash; |
105 | 5 | _elems--; |
106 | 5 | } |
107 | | |
108 | 16 | return result; |
109 | 16 | } |
110 | | |
111 | 298k | bool HandleTable::remove(const LRUHandle* h) { |
112 | 298k | LRUHandle** ptr = &(_list[h->hash & (_length - 1)]); |
113 | 306k | while (*ptr != nullptr && *ptr != h) { Branch (113:12): [True: 306k, False: 0]
Branch (113:31): [True: 7.47k, False: 298k]
|
114 | 7.47k | ptr = &(*ptr)->next_hash; |
115 | 7.47k | } |
116 | | |
117 | 298k | LRUHandle* result = *ptr; |
118 | 298k | if (result != nullptr) { Branch (118:9): [True: 298k, False: 0]
|
119 | 298k | *ptr = result->next_hash; |
120 | 298k | _elems--; |
121 | 298k | return true; |
122 | 298k | } |
123 | 0 | return false; |
124 | 298k | } |
125 | | |
126 | 662k | LRUHandle** HandleTable::_find_pointer(const CacheKey& key, uint32_t hash) { |
127 | 662k | LRUHandle** ptr = &(_list[hash & (_length - 1)]); |
128 | 1.05M | while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) { Branch (128:12): [True: 705k, False: 352k]
Branch (128:12): [True: 396k, False: 662k]
Branch (128:32): [True: 396k, False: 309k]
Branch (128:56): [True: 147, False: 309k]
|
129 | 396k | ptr = &(*ptr)->next_hash; |
130 | 396k | } |
131 | | |
132 | 662k | return ptr; |
133 | 662k | } |
134 | | |
135 | 8.39k | void HandleTable::_resize() { |
136 | 8.39k | uint32_t new_length = 16; |
137 | 8.75k | while (new_length < _elems * 1.5) { Branch (137:12): [True: 356, False: 8.39k]
|
138 | 356 | new_length *= 2; |
139 | 356 | } |
140 | | |
141 | 8.39k | LRUHandle** new_list = new (std::nothrow) LRUHandle*[new_length]; |
142 | 8.39k | memset(new_list, 0, sizeof(new_list[0]) * new_length); |
143 | | |
144 | 8.39k | uint32_t count = 0; |
145 | 65.1k | for (uint32_t i = 0; i < _length; i++) { Branch (145:26): [True: 56.8k, False: 8.39k]
|
146 | 56.8k | LRUHandle* h = _list[i]; |
147 | 113k | while (h != nullptr) { Branch (147:16): [True: 56.9k, False: 56.8k]
|
148 | 56.9k | LRUHandle* next = h->next_hash; |
149 | 56.9k | uint32_t hash = h->hash; |
150 | 56.9k | LRUHandle** ptr = &new_list[hash & (new_length - 1)]; |
151 | 56.9k | h->next_hash = *ptr; |
152 | 56.9k | *ptr = h; |
153 | 56.9k | h = next; |
154 | 56.9k | count++; |
155 | 56.9k | } |
156 | 56.8k | } |
157 | | |
158 | 8.39k | DCHECK_EQ(_elems, count); |
159 | 8.39k | delete[] _list; |
160 | 8.39k | _list = new_list; |
161 | 8.39k | _length = new_length; |
162 | 8.39k | } |
163 | | |
164 | 36 | uint32_t HandleTable::element_count() const { |
165 | 36 | return _elems; |
166 | 36 | } |
167 | | |
168 | 8.29k | LRUCache::LRUCache(LRUCacheType type) : _type(type) { |
169 | | // Make empty circular linked list |
170 | 8.29k | _lru_normal.next = &_lru_normal; |
171 | 8.29k | _lru_normal.prev = &_lru_normal; |
172 | 8.29k | _lru_durable.next = &_lru_durable; |
173 | 8.29k | _lru_durable.prev = &_lru_durable; |
174 | 8.29k | } |
175 | | |
176 | 7.87k | LRUCache::~LRUCache() { |
177 | 7.87k | prune(); |
178 | 7.87k | } |
179 | | |
180 | 8.30k | PrunedInfo LRUCache::set_capacity(size_t capacity) { |
181 | 8.30k | LRUHandle* last_ref_list = nullptr; |
182 | 8.30k | { |
183 | 8.30k | std::lock_guard l(_mutex); |
184 | 8.30k | if (capacity > _capacity) { Branch (184:13): [True: 8.29k, False: 8]
|
185 | 8.29k | _capacity = capacity; |
186 | 8.29k | return {0, 0}; |
187 | 8.29k | } |
188 | 8 | _capacity = capacity; |
189 | 8 | _evict_from_lru(0, &last_ref_list); |
190 | 8 | } |
191 | | |
192 | 0 | int64_t pruned_count = 0; |
193 | 8 | int64_t pruned_size = 0; |
194 | 48.0k | while (last_ref_list != nullptr) { Branch (194:12): [True: 48.0k, False: 8]
|
195 | 48.0k | ++pruned_count; |
196 | 48.0k | pruned_size += last_ref_list->total_size; |
197 | 48.0k | LRUHandle* next = last_ref_list->next; |
198 | 48.0k | last_ref_list->free(); |
199 | 48.0k | last_ref_list = next; |
200 | 48.0k | } |
201 | 8 | return {pruned_count, pruned_size}; |
202 | 8.30k | } |
203 | | |
204 | 0 | uint64_t LRUCache::get_lookup_count() { |
205 | 0 | std::lock_guard l(_mutex); |
206 | 0 | return _lookup_count; |
207 | 0 | } |
208 | | |
209 | 0 | uint64_t LRUCache::get_hit_count() { |
210 | 0 | std::lock_guard l(_mutex); |
211 | 0 | return _hit_count; |
212 | 0 | } |
213 | | |
214 | 95 | size_t LRUCache::get_usage() { |
215 | 95 | std::lock_guard l(_mutex); |
216 | 95 | return _usage; |
217 | 95 | } |
218 | | |
219 | 0 | size_t LRUCache::get_capacity() { |
220 | 0 | std::lock_guard l(_mutex); |
221 | 0 | return _capacity; |
222 | 0 | } |
223 | | |
224 | 0 | size_t LRUCache::get_element_count() { |
225 | 0 | std::lock_guard l(_mutex); |
226 | 0 | return _table.element_count(); |
227 | 0 | } |
228 | | |
229 | 928k | bool LRUCache::_unref(LRUHandle* e) { |
230 | 928k | DCHECK(e->refs > 0); |
231 | 928k | e->refs--; |
232 | 928k | return e->refs == 0; |
233 | 928k | } |
234 | | |
235 | 552k | void LRUCache::_lru_remove(LRUHandle* e) { |
236 | 552k | e->next->prev = e->prev; |
237 | 552k | e->prev->next = e->next; |
238 | 552k | e->prev = e->next = nullptr; |
239 | | |
240 | 552k | if (_cache_value_check_timestamp) { Branch (240:9): [True: 48, False: 552k]
|
241 | 48 | if (e->priority == CachePriority::NORMAL) { Branch (241:13): [True: 48, False: 0]
|
242 | 48 | auto pair = std::make_pair(_cache_value_time_extractor(e->value), e); |
243 | 48 | auto found_it = _sorted_normal_entries_with_timestamp.find(pair); |
244 | 48 | if (found_it != _sorted_normal_entries_with_timestamp.end()) { Branch (244:17): [True: 48, False: 0]
|
245 | 48 | _sorted_normal_entries_with_timestamp.erase(found_it); |
246 | 48 | } |
247 | 48 | } else if (e->priority == CachePriority::DURABLE) { Branch (247:20): [True: 0, False: 0]
|
248 | 0 | auto pair = std::make_pair(_cache_value_time_extractor(e->value), e); |
249 | 0 | auto found_it = _sorted_durable_entries_with_timestamp.find(pair); |
250 | 0 | if (found_it != _sorted_durable_entries_with_timestamp.end()) { Branch (250:17): [True: 0, False: 0]
|
251 | 0 | _sorted_durable_entries_with_timestamp.erase(found_it); |
252 | 0 | } |
253 | 0 | } |
254 | 48 | } |
255 | 552k | } |
256 | | |
257 | 558k | void LRUCache::_lru_append(LRUHandle* list, LRUHandle* e) { |
258 | | // Make "e" newest entry by inserting just before *list |
259 | 558k | e->next = list; |
260 | 558k | e->prev = list->prev; |
261 | 558k | e->prev->next = e; |
262 | 558k | e->next->prev = e; |
263 | | |
264 | | // _cache_value_check_timestamp is true, |
265 | | // means evict entry will depends on the timestamp asc set, |
266 | | // the timestamp is updated by higher level caller, |
267 | | // and the timestamp of hit entry is different with the insert entry, |
268 | | // that is why need check timestamp to evict entry, |
269 | | // in order to keep the survival time of hit entries |
270 | | // longer than the entries just inserted, |
271 | | // so use asc set to sorted these entries's timestamp and LRUHandle* |
272 | 558k | if (_cache_value_check_timestamp) { Branch (272:9): [True: 48, False: 558k]
|
273 | 48 | if (e->priority == CachePriority::NORMAL) { Branch (273:13): [True: 48, False: 0]
|
274 | 48 | _sorted_normal_entries_with_timestamp.insert( |
275 | 48 | std::make_pair(_cache_value_time_extractor(e->value), e)); |
276 | 48 | } else if (e->priority == CachePriority::DURABLE) { Branch (276:20): [True: 0, False: 0]
|
277 | 0 | _sorted_durable_entries_with_timestamp.insert( |
278 | 0 | std::make_pair(_cache_value_time_extractor(e->value), e)); |
279 | 0 | } |
280 | 48 | } |
281 | 558k | } |
282 | | |
283 | 342k | Cache::Handle* LRUCache::lookup(const CacheKey& key, uint32_t hash) { |
284 | 342k | std::lock_guard l(_mutex); |
285 | 342k | ++_lookup_count; |
286 | 342k | LRUHandle* e = _table.lookup(key, hash); |
287 | 342k | if (e != nullptr) { Branch (287:9): [True: 293k, False: 48.7k]
|
288 | | // we get it from _table, so in_cache must be true |
289 | 293k | DCHECK(e->in_cache); |
290 | 293k | if (e->refs == 1) { Branch (290:13): [True: 290k, False: 3.14k]
|
291 | | // only in LRU free list, remove it from list |
292 | 290k | _lru_remove(e); |
293 | 290k | } |
294 | 293k | e->refs++; |
295 | 293k | ++_hit_count; |
296 | 293k | e->last_visit_time = UnixMillis(); |
297 | 293k | } |
298 | 342k | return reinterpret_cast<Cache::Handle*>(e); |
299 | 342k | } |
300 | | |
301 | 613k | void LRUCache::release(Cache::Handle* handle) { |
302 | 613k | if (handle == nullptr) { Branch (302:9): [True: 0, False: 613k]
|
303 | 0 | return; |
304 | 0 | } |
305 | 613k | LRUHandle* e = reinterpret_cast<LRUHandle*>(handle); |
306 | 613k | bool last_ref = false; |
307 | 613k | { |
308 | 613k | std::lock_guard l(_mutex); |
309 | 613k | last_ref = _unref(e); |
310 | 613k | if (last_ref) { Branch (310:13): [True: 16.0k, False: 597k]
|
311 | 16.0k | _usage -= e->total_size; |
312 | 597k | } else if (e->in_cache && e->refs == 1) { Branch (312:20): [True: 597k, False: 0]
Branch (312:35): [True: 594k, False: 3.14k]
|
313 | | // only exists in cache |
314 | 594k | if (_usage > _capacity) { Branch (314:17): [True: 36.3k, False: 558k]
|
315 | | // take this opportunity and remove the item |
316 | 36.3k | bool removed = _table.remove(e); |
317 | 36.3k | DCHECK(removed); |
318 | 36.3k | e->in_cache = false; |
319 | 36.3k | _unref(e); |
320 | 36.3k | _usage -= e->total_size; |
321 | 36.3k | last_ref = true; |
322 | 558k | } else { |
323 | | // put it to LRU free list |
324 | 558k | if (e->priority == CachePriority::NORMAL) { Branch (324:21): [True: 558k, False: 17]
|
325 | 558k | _lru_append(&_lru_normal, e); |
326 | 558k | } else if (e->priority == CachePriority::DURABLE) { Branch (326:28): [True: 17, False: 0]
|
327 | 17 | _lru_append(&_lru_durable, e); |
328 | 17 | } |
329 | 558k | } |
330 | 594k | } |
331 | 613k | } |
332 | | |
333 | | // free handle out of mutex |
334 | 613k | if (last_ref) { Branch (334:9): [True: 52.3k, False: 561k]
|
335 | 52.3k | e->free(); |
336 | 52.3k | } |
337 | 613k | } |
338 | | |
339 | 18 | void LRUCache::_evict_from_lru_with_time(size_t total_size, LRUHandle** to_remove_head) { |
340 | | // 1. evict normal cache entries |
341 | 22 | while ((_usage + total_size > _capacity || _check_element_count_limit()) && Branch (341:13): [True: 3, False: 19]
Branch (341:48): [True: 2, False: 17]
|
342 | 22 | !_sorted_normal_entries_with_timestamp.empty()) { Branch (342:12): [True: 4, False: 1]
|
343 | 4 | auto entry_pair = _sorted_normal_entries_with_timestamp.begin(); |
344 | 4 | LRUHandle* remove_handle = entry_pair->second; |
345 | 4 | DCHECK(remove_handle != nullptr); |
346 | 4 | DCHECK(remove_handle->priority == CachePriority::NORMAL); |
347 | 4 | _evict_one_entry(remove_handle); |
348 | 4 | remove_handle->next = *to_remove_head; |
349 | 4 | *to_remove_head = remove_handle; |
350 | 4 | } |
351 | | |
352 | | // 2. evict durable cache entries if need |
353 | 18 | while ((_usage + total_size > _capacity || _check_element_count_limit()) && Branch (353:13): [True: 1, False: 17]
Branch (353:48): [True: 0, False: 17]
|
354 | 18 | !_sorted_durable_entries_with_timestamp.empty()) { Branch (354:12): [True: 0, False: 1]
|
355 | 0 | auto entry_pair = _sorted_durable_entries_with_timestamp.begin(); |
356 | 0 | LRUHandle* remove_handle = entry_pair->second; |
357 | 0 | DCHECK(remove_handle != nullptr); |
358 | 0 | DCHECK(remove_handle->priority == CachePriority::DURABLE); |
359 | 0 | _evict_one_entry(remove_handle); |
360 | 0 | remove_handle->next = *to_remove_head; |
361 | 0 | *to_remove_head = remove_handle; |
362 | 0 | } |
363 | 18 | } |
364 | | |
365 | 320k | void LRUCache::_evict_from_lru(size_t total_size, LRUHandle** to_remove_head) { |
366 | | // 1. evict normal cache entries |
367 | 573k | while ((_usage + total_size > _capacity || _check_element_count_limit()) && Branch (367:13): [True: 298k, False: 275k]
Branch (367:48): [True: 0, False: 275k]
|
368 | 573k | _lru_normal.next != &_lru_normal) { Branch (368:12): [True: 253k, False: 44.3k]
|
369 | 253k | LRUHandle* old = _lru_normal.next; |
370 | 253k | DCHECK(old->priority == CachePriority::NORMAL); |
371 | 253k | _evict_one_entry(old); |
372 | 253k | old->next = *to_remove_head; |
373 | 253k | *to_remove_head = old; |
374 | 253k | } |
375 | | // 2. evict durable cache entries if need |
376 | 320k | while ((_usage + total_size > _capacity || _check_element_count_limit()) && Branch (376:13): [True: 44.3k, False: 275k]
Branch (376:48): [True: 0, False: 275k]
|
377 | 320k | _lru_durable.next != &_lru_durable) { Branch (377:12): [True: 5, False: 44.3k]
|
378 | 5 | LRUHandle* old = _lru_durable.next; |
379 | 5 | DCHECK(old->priority == CachePriority::DURABLE); |
380 | 5 | _evict_one_entry(old); |
381 | 5 | old->next = *to_remove_head; |
382 | 5 | *to_remove_head = old; |
383 | 5 | } |
384 | 320k | } |
385 | | |
386 | 262k | void LRUCache::_evict_one_entry(LRUHandle* e) { |
387 | 262k | DCHECK(e->in_cache); |
388 | 262k | DCHECK(e->refs == 1); // LRU list contains elements which may be evicted |
389 | 262k | _lru_remove(e); |
390 | 262k | bool removed = _table.remove(e); |
391 | 262k | DCHECK(removed); |
392 | 262k | e->in_cache = false; |
393 | 262k | _unref(e); |
394 | 262k | _usage -= e->total_size; |
395 | 262k | } |
396 | | |
397 | 551k | bool LRUCache::_check_element_count_limit() { |
398 | 551k | return _element_count_capacity != 0 && _table.element_count() >= _element_count_capacity; Branch (398:12): [True: 36, False: 551k]
Branch (398:44): [True: 2, False: 34]
|
399 | 551k | } |
400 | | |
401 | | Cache::Handle* LRUCache::insert(const CacheKey& key, uint32_t hash, void* value, size_t charge, |
402 | 320k | CachePriority priority) { |
403 | 320k | size_t handle_size = sizeof(LRUHandle) - 1 + key.size(); |
404 | 320k | auto* e = reinterpret_cast<LRUHandle*>(malloc(handle_size)); |
405 | 320k | e->value = value; |
406 | 320k | e->charge = charge; |
407 | 320k | e->key_length = key.size(); |
408 | | // if LRUCacheType::NUMBER, charge not add handle_size, |
409 | | // because charge at this time is no longer the memory size, but an weight. |
410 | 320k | e->total_size = (_type == LRUCacheType::SIZE ? handle_size + charge : charge); Branch (410:22): [True: 207k, False: 112k]
|
411 | 320k | e->hash = hash; |
412 | 320k | e->refs = 2; // one for the returned handle, one for LRUCache. |
413 | 320k | e->next = e->prev = nullptr; |
414 | 320k | e->in_cache = true; |
415 | 320k | e->priority = priority; |
416 | 320k | e->type = _type; |
417 | 320k | memcpy(e->key_data, key.data(), key.size()); |
418 | 320k | e->last_visit_time = UnixMillis(); |
419 | 320k | LRUHandle* to_remove_head = nullptr; |
420 | 320k | { |
421 | 320k | std::lock_guard l(_mutex); |
422 | | |
423 | | // Free the space following strict LRU policy until enough space |
424 | | // is freed or the lru list is empty |
425 | 320k | if (_cache_value_check_timestamp) { Branch (425:13): [True: 18, False: 320k]
|
426 | 18 | _evict_from_lru_with_time(e->total_size, &to_remove_head); |
427 | 320k | } else { |
428 | 320k | _evict_from_lru(e->total_size, &to_remove_head); |
429 | 320k | } |
430 | | |
431 | | // insert into the cache |
432 | | // note that the cache might get larger than its capacity if not enough |
433 | | // space was freed |
434 | 320k | auto old = _table.insert(e); |
435 | 320k | _usage += e->total_size; |
436 | 320k | if (old != nullptr) { Branch (436:13): [True: 16.0k, False: 304k]
|
437 | 16.0k | old->in_cache = false; |
438 | 16.0k | if (_unref(old)) { Branch (438:17): [True: 45, False: 16.0k]
|
439 | 45 | _usage -= old->total_size; |
440 | | // old is on LRU because it's in cache and its reference count |
441 | | // was just 1 (Unref returned 0) |
442 | 45 | _lru_remove(old); |
443 | 45 | old->next = to_remove_head; |
444 | 45 | to_remove_head = old; |
445 | 45 | } |
446 | 16.0k | } |
447 | 320k | } |
448 | | |
449 | | // we free the entries here outside of mutex for |
450 | | // performance reasons |
451 | 526k | while (to_remove_head != nullptr) { Branch (451:12): [True: 205k, False: 320k]
|
452 | 205k | LRUHandle* next = to_remove_head->next; |
453 | 205k | to_remove_head->free(); |
454 | 205k | to_remove_head = next; |
455 | 205k | } |
456 | | |
457 | 320k | return reinterpret_cast<Cache::Handle*>(e); |
458 | 320k | } |
459 | | |
460 | 13 | void LRUCache::erase(const CacheKey& key, uint32_t hash) { |
461 | 13 | LRUHandle* e = nullptr; |
462 | 13 | bool last_ref = false; |
463 | 13 | { |
464 | 13 | std::lock_guard l(_mutex); |
465 | 13 | e = _table.remove(key, hash); |
466 | 13 | if (e != nullptr) { Branch (466:13): [True: 2, False: 11]
|
467 | 2 | last_ref = _unref(e); |
468 | 2 | if (last_ref) { Branch (468:17): [True: 1, False: 1]
|
469 | 1 | _usage -= e->total_size; |
470 | 1 | if (e->in_cache) { Branch (470:21): [True: 1, False: 0]
|
471 | | // locate in free list |
472 | 1 | _lru_remove(e); |
473 | 1 | } |
474 | 1 | } |
475 | 2 | e->in_cache = false; |
476 | 2 | } |
477 | 13 | } |
478 | | // free handle out of mutex, when last_ref is true, e must not be nullptr |
479 | 13 | if (last_ref) { Branch (479:9): [True: 1, False: 12]
|
480 | 1 | e->free(); |
481 | 1 | } |
482 | 13 | } |
483 | | |
484 | 7.87k | PrunedInfo LRUCache::prune() { |
485 | 7.87k | LRUHandle* to_remove_head = nullptr; |
486 | 7.87k | { |
487 | 7.87k | std::lock_guard l(_mutex); |
488 | 16.6k | while (_lru_normal.next != &_lru_normal) { Branch (488:16): [True: 8.73k, False: 7.87k]
|
489 | 8.73k | LRUHandle* old = _lru_normal.next; |
490 | 8.73k | _evict_one_entry(old); |
491 | 8.73k | old->next = to_remove_head; |
492 | 8.73k | to_remove_head = old; |
493 | 8.73k | } |
494 | 7.88k | while (_lru_durable.next != &_lru_durable) { Branch (494:16): [True: 4, False: 7.87k]
|
495 | 4 | LRUHandle* old = _lru_durable.next; |
496 | 4 | _evict_one_entry(old); |
497 | 4 | old->next = to_remove_head; |
498 | 4 | to_remove_head = old; |
499 | 4 | } |
500 | 7.87k | } |
501 | 7.87k | int64_t pruned_count = 0; |
502 | 7.87k | int64_t pruned_size = 0; |
503 | 16.6k | while (to_remove_head != nullptr) { Branch (503:12): [True: 8.73k, False: 7.87k]
|
504 | 8.73k | ++pruned_count; |
505 | 8.73k | pruned_size += to_remove_head->total_size; |
506 | 8.73k | LRUHandle* next = to_remove_head->next; |
507 | 8.73k | to_remove_head->free(); |
508 | 8.73k | to_remove_head = next; |
509 | 8.73k | } |
510 | 7.87k | return {pruned_count, pruned_size}; |
511 | 7.87k | } |
512 | | |
513 | 8 | PrunedInfo LRUCache::prune_if(CachePrunePredicate pred, bool lazy_mode) { |
514 | 8 | LRUHandle* to_remove_head = nullptr; |
515 | 8 | { |
516 | 8 | std::lock_guard l(_mutex); |
517 | 8 | LRUHandle* p = _lru_normal.next; |
518 | 23 | while (p != &_lru_normal) { Branch (518:16): [True: 18, False: 5]
|
519 | 18 | LRUHandle* next = p->next; |
520 | 18 | if (pred(p)) { Branch (520:17): [True: 10, False: 8]
|
521 | 10 | _evict_one_entry(p); |
522 | 10 | p->next = to_remove_head; |
523 | 10 | to_remove_head = p; |
524 | 10 | } else if (lazy_mode) { Branch (524:24): [True: 3, False: 5]
|
525 | 3 | break; |
526 | 3 | } |
527 | 15 | p = next; |
528 | 15 | } |
529 | | |
530 | 8 | p = _lru_durable.next; |
531 | 15 | while (p != &_lru_durable) { Branch (531:16): [True: 10, False: 5]
|
532 | 10 | LRUHandle* next = p->next; |
533 | 10 | if (pred(p)) { Branch (533:17): [True: 2, False: 8]
|
534 | 2 | _evict_one_entry(p); |
535 | 2 | p->next = to_remove_head; |
536 | 2 | to_remove_head = p; |
537 | 8 | } else if (lazy_mode) { Branch (537:24): [True: 3, False: 5]
|
538 | 3 | break; |
539 | 3 | } |
540 | 7 | p = next; |
541 | 7 | } |
542 | 8 | } |
543 | 8 | int64_t pruned_count = 0; |
544 | 8 | int64_t pruned_size = 0; |
545 | 20 | while (to_remove_head != nullptr) { Branch (545:12): [True: 12, False: 8]
|
546 | 12 | ++pruned_count; |
547 | 12 | pruned_size += to_remove_head->total_size; |
548 | 12 | LRUHandle* next = to_remove_head->next; |
549 | 12 | to_remove_head->free(); |
550 | 12 | to_remove_head = next; |
551 | 12 | } |
552 | 8 | return {pruned_count, pruned_size}; |
553 | 8 | } |
554 | | |
555 | 11 | void LRUCache::set_cache_value_time_extractor(CacheValueTimeExtractor cache_value_time_extractor) { |
556 | 11 | _cache_value_time_extractor = cache_value_time_extractor; |
557 | 11 | } |
558 | | |
559 | 11 | void LRUCache::set_cache_value_check_timestamp(bool cache_value_check_timestamp) { |
560 | 11 | _cache_value_check_timestamp = cache_value_check_timestamp; |
561 | 11 | } |
562 | | |
563 | 662k | inline uint32_t ShardedLRUCache::_hash_slice(const CacheKey& s) { |
564 | 662k | return s.hash(s.data(), s.size(), 0); |
565 | 662k | } |
566 | | |
567 | | ShardedLRUCache::ShardedLRUCache(const std::string& name, size_t capacity, LRUCacheType type, |
568 | | uint32_t num_shards, uint32_t total_element_count_capacity) |
569 | | : _name(name), |
570 | | _num_shard_bits(Bits::FindLSBSetNonZero(num_shards)), |
571 | | _num_shards(num_shards), |
572 | | _shards(nullptr), |
573 | | _last_id(1), |
574 | 490 | _capacity(capacity) { |
575 | 490 | CHECK(num_shards > 0) << "num_shards cannot be 0"; |
576 | 490 | CHECK_EQ((num_shards & (num_shards - 1)), 0) |
577 | 0 | << "num_shards should be power of two, but got " << num_shards; |
578 | | |
579 | 490 | const size_t per_shard = (capacity + (_num_shards - 1)) / _num_shards; |
580 | 490 | const size_t per_shard_element_count_capacity = |
581 | 490 | (total_element_count_capacity + (_num_shards - 1)) / _num_shards; |
582 | 490 | LRUCache** shards = new (std::nothrow) LRUCache*[_num_shards]; |
583 | 8.77k | for (int s = 0; s < _num_shards; s++) { Branch (583:21): [True: 8.28k, False: 490]
|
584 | 8.28k | shards[s] = new LRUCache(type); |
585 | 8.28k | shards[s]->set_capacity(per_shard); |
586 | 8.28k | shards[s]->set_element_count_capacity(per_shard_element_count_capacity); |
587 | 8.28k | } |
588 | 490 | _shards = shards; |
589 | | |
590 | 490 | _entity = DorisMetrics::instance()->metric_registry()->register_entity( |
591 | 490 | std::string("lru_cache:") + name, {{"name", name}}); |
592 | 490 | _entity->register_hook(name, std::bind(&ShardedLRUCache::update_cache_metrics, this)); |
593 | 490 | INT_GAUGE_METRIC_REGISTER(_entity, cache_capacity); Line | Count | Source | 212 | 490 | metric = (IntGauge*)(entity->register_metric<IntGauge>(&METRIC_##metric)) |
|
594 | 490 | INT_GAUGE_METRIC_REGISTER(_entity, cache_usage); Line | Count | Source | 212 | 490 | metric = (IntGauge*)(entity->register_metric<IntGauge>(&METRIC_##metric)) |
|
595 | 490 | INT_GAUGE_METRIC_REGISTER(_entity, cache_element_count); Line | Count | Source | 212 | 490 | metric = (IntGauge*)(entity->register_metric<IntGauge>(&METRIC_##metric)) |
|
596 | 490 | DOUBLE_GAUGE_METRIC_REGISTER(_entity, cache_usage_ratio); Line | Count | Source | 215 | 490 | metric = (DoubleGauge*)(entity->register_metric<DoubleGauge>(&METRIC_##metric)) |
|
597 | 490 | INT_COUNTER_METRIC_REGISTER(_entity, cache_lookup_count); Line | Count | Source | 209 | 490 | metric = (IntCounter*)(entity->register_metric<IntCounter>(&METRIC_##metric)) |
|
598 | 490 | INT_COUNTER_METRIC_REGISTER(_entity, cache_hit_count); Line | Count | Source | 209 | 490 | metric = (IntCounter*)(entity->register_metric<IntCounter>(&METRIC_##metric)) |
|
599 | 490 | DOUBLE_GAUGE_METRIC_REGISTER(_entity, cache_hit_ratio); Line | Count | Source | 215 | 490 | metric = (DoubleGauge*)(entity->register_metric<DoubleGauge>(&METRIC_##metric)) |
|
600 | | |
601 | 490 | _hit_count_bvar.reset(new bvar::Adder<uint64_t>("doris_cache", _name)); |
602 | 490 | _hit_count_per_second.reset(new bvar::PerSecond<bvar::Adder<uint64_t>>( |
603 | 490 | "doris_cache", _name + "_persecond", _hit_count_bvar.get(), 60)); |
604 | 490 | _lookup_count_bvar.reset(new bvar::Adder<uint64_t>("doris_cache", _name)); |
605 | 490 | _lookup_count_per_second.reset(new bvar::PerSecond<bvar::Adder<uint64_t>>( |
606 | 490 | "doris_cache", _name + "_persecond", _lookup_count_bvar.get(), 60)); |
607 | 490 | } |
608 | | |
609 | | ShardedLRUCache::ShardedLRUCache(const std::string& name, size_t capacity, LRUCacheType type, |
610 | | uint32_t num_shards, |
611 | | CacheValueTimeExtractor cache_value_time_extractor, |
612 | | bool cache_value_check_timestamp, |
613 | | uint32_t total_element_count_capacity) |
614 | 11 | : ShardedLRUCache(name, capacity, type, num_shards, total_element_count_capacity) { |
615 | 22 | for (int s = 0; s < _num_shards; s++) { Branch (615:21): [True: 11, False: 11]
|
616 | 11 | _shards[s]->set_cache_value_time_extractor(cache_value_time_extractor); |
617 | 11 | _shards[s]->set_cache_value_check_timestamp(cache_value_check_timestamp); |
618 | 11 | } |
619 | 11 | } |
620 | | |
621 | 484 | ShardedLRUCache::~ShardedLRUCache() { |
622 | 484 | _entity->deregister_hook(_name); |
623 | 484 | DorisMetrics::instance()->metric_registry()->deregister_entity(_entity); |
624 | 484 | if (_shards) { Branch (624:9): [True: 484, False: 0]
|
625 | 8.35k | for (int s = 0; s < _num_shards; s++) { Branch (625:25): [True: 7.87k, False: 484]
|
626 | 7.87k | delete _shards[s]; |
627 | 7.87k | } |
628 | 484 | delete[] _shards; |
629 | 484 | } |
630 | 484 | } |
631 | | |
632 | 14 | PrunedInfo ShardedLRUCache::set_capacity(size_t capacity) { |
633 | 14 | std::lock_guard l(_mutex); |
634 | 14 | PrunedInfo pruned_info; |
635 | 14 | const size_t per_shard = (capacity + (_num_shards - 1)) / _num_shards; |
636 | 28 | for (int s = 0; s < _num_shards; s++) { Branch (636:21): [True: 14, False: 14]
|
637 | 14 | PrunedInfo info = _shards[s]->set_capacity(per_shard); |
638 | 14 | pruned_info.pruned_count += info.pruned_count; |
639 | 14 | pruned_info.pruned_size += info.pruned_size; |
640 | 14 | } |
641 | 14 | _capacity = capacity; |
642 | 14 | return pruned_info; |
643 | 14 | } |
644 | | |
645 | 50 | size_t ShardedLRUCache::get_capacity() { |
646 | 50 | std::lock_guard l(_mutex); |
647 | 50 | return _capacity; |
648 | 50 | } |
649 | | |
650 | | Cache::Handle* ShardedLRUCache::insert(const CacheKey& key, void* value, size_t charge, |
651 | 320k | CachePriority priority) { |
652 | 320k | const uint32_t hash = _hash_slice(key); |
653 | 320k | return _shards[_shard(hash)]->insert(key, hash, value, charge, priority); |
654 | 320k | } |
655 | | |
656 | 342k | Cache::Handle* ShardedLRUCache::lookup(const CacheKey& key) { |
657 | 342k | const uint32_t hash = _hash_slice(key); |
658 | 342k | return _shards[_shard(hash)]->lookup(key, hash); |
659 | 342k | } |
660 | | |
661 | 613k | void ShardedLRUCache::release(Handle* handle) { |
662 | 613k | LRUHandle* h = reinterpret_cast<LRUHandle*>(handle); |
663 | 613k | _shards[_shard(h->hash)]->release(handle); |
664 | 613k | } |
665 | | |
666 | 13 | void ShardedLRUCache::erase(const CacheKey& key) { |
667 | 13 | const uint32_t hash = _hash_slice(key); |
668 | 13 | _shards[_shard(hash)]->erase(key, hash); |
669 | 13 | } |
670 | | |
671 | 297k | void* ShardedLRUCache::value(Handle* handle) { |
672 | 297k | return reinterpret_cast<LRUHandle*>(handle)->value; |
673 | 297k | } |
674 | | |
675 | 2 | uint64_t ShardedLRUCache::new_id() { |
676 | 2 | return _last_id.fetch_add(1, std::memory_order_relaxed); |
677 | 2 | } |
678 | | |
679 | 0 | PrunedInfo ShardedLRUCache::prune() { |
680 | 0 | PrunedInfo pruned_info; |
681 | 0 | for (int s = 0; s < _num_shards; s++) { Branch (681:21): [True: 0, False: 0]
|
682 | 0 | PrunedInfo info = _shards[s]->prune(); |
683 | 0 | pruned_info.pruned_count += info.pruned_count; |
684 | 0 | pruned_info.pruned_size += info.pruned_size; |
685 | 0 | } |
686 | 0 | return pruned_info; |
687 | 0 | } |
688 | | |
689 | 0 | PrunedInfo ShardedLRUCache::prune_if(CachePrunePredicate pred, bool lazy_mode) { |
690 | 0 | PrunedInfo pruned_info; |
691 | 0 | for (int s = 0; s < _num_shards; s++) { Branch (691:21): [True: 0, False: 0]
|
692 | 0 | PrunedInfo info = _shards[s]->prune_if(pred, lazy_mode); |
693 | 0 | pruned_info.pruned_count += info.pruned_count; |
694 | 0 | pruned_info.pruned_size += info.pruned_size; |
695 | 0 | } |
696 | 0 | return pruned_info; |
697 | 0 | } |
698 | | |
699 | 50 | int64_t ShardedLRUCache::get_usage() { |
700 | 50 | size_t total_usage = 0; |
701 | 100 | for (int i = 0; i < _num_shards; i++) { Branch (701:21): [True: 50, False: 50]
|
702 | 50 | total_usage += _shards[i]->get_usage(); |
703 | 50 | } |
704 | 50 | return total_usage; |
705 | 50 | } |
706 | | |
707 | 0 | size_t ShardedLRUCache::get_element_count() { |
708 | 0 | size_t total_element_count = 0; |
709 | 0 | for (int i = 0; i < _num_shards; i++) { Branch (709:21): [True: 0, False: 0]
|
710 | 0 | total_element_count += _shards[i]->get_element_count(); |
711 | 0 | } |
712 | 0 | return total_element_count; |
713 | 0 | } |
714 | | |
715 | 0 | void ShardedLRUCache::update_cache_metrics() const { |
716 | 0 | size_t capacity = 0; |
717 | 0 | size_t total_usage = 0; |
718 | 0 | size_t total_lookup_count = 0; |
719 | 0 | size_t total_hit_count = 0; |
720 | 0 | size_t total_element_count = 0; |
721 | 0 | for (int i = 0; i < _num_shards; i++) { Branch (721:21): [True: 0, False: 0]
|
722 | 0 | capacity += _shards[i]->get_capacity(); |
723 | 0 | total_usage += _shards[i]->get_usage(); |
724 | 0 | total_lookup_count += _shards[i]->get_lookup_count(); |
725 | 0 | total_hit_count += _shards[i]->get_hit_count(); |
726 | 0 | total_element_count += _shards[i]->get_element_count(); |
727 | 0 | } |
728 | |
|
729 | 0 | cache_capacity->set_value(capacity); |
730 | 0 | cache_usage->set_value(total_usage); |
731 | 0 | cache_element_count->set_value(total_element_count); |
732 | 0 | cache_lookup_count->set_value(total_lookup_count); |
733 | 0 | cache_hit_count->set_value(total_hit_count); |
734 | 0 | cache_usage_ratio->set_value(capacity == 0 ? 0 : ((double)total_usage / capacity)); Branch (734:34): [True: 0, False: 0]
|
735 | 0 | cache_hit_ratio->set_value( |
736 | 0 | total_lookup_count == 0 ? 0 : ((double)total_hit_count / total_lookup_count)); Branch (736:13): [True: 0, False: 0]
|
737 | 0 | } |
738 | | |
739 | | Cache::Handle* DummyLRUCache::insert(const CacheKey& key, void* value, size_t charge, |
740 | 232 | CachePriority priority) { |
741 | 232 | size_t handle_size = sizeof(LRUHandle); |
742 | 232 | auto* e = reinterpret_cast<LRUHandle*>(malloc(handle_size)); |
743 | 232 | e->value = value; |
744 | 232 | e->charge = charge; |
745 | 232 | e->key_length = 0; |
746 | 232 | e->total_size = 0; |
747 | 232 | e->hash = 0; |
748 | 232 | e->refs = 1; // only one for the returned handle |
749 | 232 | e->next = e->prev = nullptr; |
750 | 232 | e->in_cache = false; |
751 | 232 | return reinterpret_cast<Cache::Handle*>(e); |
752 | 232 | } |
753 | | |
754 | 232 | void DummyLRUCache::release(Cache::Handle* handle) { |
755 | 232 | if (handle == nullptr) { Branch (755:9): [True: 0, False: 232]
|
756 | 0 | return; |
757 | 0 | } |
758 | 232 | auto* e = reinterpret_cast<LRUHandle*>(handle); |
759 | 232 | e->free(); |
760 | 232 | } |
761 | | |
762 | 31 | void* DummyLRUCache::value(Handle* handle) { |
763 | 31 | return reinterpret_cast<LRUHandle*>(handle)->value; |
764 | 31 | } |
765 | | |
766 | | } // namespace doris |