Skip to content

Commit 883fc23

Browse files
authored
Merge fa5945b into 99bbf73
2 parents 99bbf73 + fa5945b commit 883fc23

File tree

3 files changed

+522
-0
lines changed

3 files changed

+522
-0
lines changed
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
#pragma once
2+
#include "defs.h"
3+
#include <ydb/core/util/cache_cache_iface.h>
4+
#include <ydb/library/yverify_stream/yverify_stream.h>
5+
#include <library/cpp/monlib/counters/counters.h>
6+
#include <library/cpp/monlib/dynamic_counters/counters.h>
7+
8+
namespace NKikimr::NCache {
9+
10+
// TODO: remove template args and make some page base class
11+
12+
enum class EClockProPageLocation {
13+
None,
14+
Hot,
15+
Cold
16+
};
17+
18+
template <typename TPage, typename TPageTraits>
19+
class TClockProCache : public ICacheCache<TPage> {
20+
using TPageKey = typename TPageTraits::TPageKey;
21+
22+
struct TPageEntry : public TIntrusiveListItem<TPageEntry> {
23+
TPageKey Key; // TODO: don't store key twice?
24+
TPage* Page;
25+
ui64 Size;
26+
27+
TPageEntry(const TPageKey& key, TPage* page, ui64 size)
28+
: Key(key)
29+
, Page(page)
30+
, Size(size)
31+
{}
32+
};
33+
34+
struct TPageKeyHash {
35+
using is_transparent = void;
36+
37+
inline size_t operator()(const THolder<TPageEntry>& entry) const {
38+
return TPageTraits::GetHash(entry->Key);
39+
}
40+
41+
inline size_t operator()(const TPageKey& key) const {
42+
return TPageTraits::GetHash(key);
43+
}
44+
};
45+
46+
struct TPageKeyEqual {
47+
using is_transparent = void;
48+
49+
inline bool operator()(const THolder<TPageEntry>& left, const THolder<TPageEntry>& right) const {
50+
return TPageTraits::Equals(left->Key, right->Key);
51+
}
52+
53+
inline bool operator()(const THolder<TPageEntry>& left, const TPageKey& right) const {
54+
return TPageTraits::Equals(left->Key, right);
55+
}
56+
};
57+
58+
public:
59+
TClockProCache(ui64 limit)
60+
: MaxSize(limit)
61+
, ColdTarget(limit)
62+
{}
63+
64+
TPage* EvictNext() override {
65+
// if (SmallQueue.Queue.Empty() && MainQueue.Queue.Empty()) {
66+
// return nullptr;
67+
// }
68+
69+
// // TODO: account passive pages inside the cache
70+
// TLimit savedLimit = std::exchange(Limit, TLimit(SmallQueue.Size + MainQueue.Size - 1));
71+
72+
// TPage* evictedPage = EvictOneIfFull();
73+
// Y_DEBUG_ABORT_UNLESS(evictedPage);
74+
75+
// Limit = savedLimit;
76+
77+
// return evictedPage;
78+
79+
return {};
80+
}
81+
82+
TIntrusiveList<TPage> Touch(TPage* page) override {
83+
if (auto it = Entries.find(TPageTraits::GetKey(page)); it != Entries.end()) {
84+
TPageEntry* entry = it->Get();
85+
if (entry->Page) {
86+
TouchFast(entry);
87+
return {};
88+
} else {
89+
return Fill(entry, page);
90+
}
91+
} else {
92+
return Insert(page);
93+
}
94+
}
95+
96+
void Erase(TPage* page) override {
97+
Y_UNUSED(page);
98+
// const EClockProPageLocation location = TPageTraits::GetLocation(page);
99+
// switch (location) {
100+
// case EClockProPageLocation::None:
101+
// EraseGhost(page);
102+
// break;
103+
// case EClockProPageLocation::SmallQueue:
104+
// Erase(SmallQueue, page);
105+
// break;
106+
// case EClockProPageLocation::MainQueue:
107+
// Erase(MainQueue, page);
108+
// break;
109+
// default:
110+
// Y_ABORT("Unknown page location");
111+
// }
112+
113+
// TPageTraits::SetFrequency(page, 0);
114+
}
115+
116+
void UpdateLimit(ui64 limit) override {
117+
MaxSize = limit;
118+
}
119+
120+
TString Dump() const {
121+
TStringBuilder result;
122+
123+
size_t count = 0;
124+
ui64 sizeHot = 0, sizeCold = 0, sizeTest = 0;
125+
126+
auto it = HandHot;
127+
while (it != nullptr) {
128+
if (count != 0) result << ", ";
129+
TPageEntry* entry = it->Node();
130+
if (entry == HandHot) result << "Hot>";
131+
if (entry == HandCold) result << "Cold>";
132+
if (entry == HandTest) result << "Test>";
133+
134+
result << "{" << TPageTraits::ToString(entry->Key) << " ";
135+
136+
count++;
137+
if (entry->Page) {
138+
auto location = TPageTraits::GetLocation(entry->Page);
139+
switch (location) {
140+
case EClockProPageLocation::Hot:
141+
result << "H ";
142+
sizeHot += entry->Size;
143+
break;
144+
case EClockProPageLocation::Cold:
145+
result << "C ";
146+
sizeCold += entry->Size;
147+
break;
148+
default:
149+
Y_ABORT("Unknown location");
150+
}
151+
} else {
152+
result << "T ";
153+
sizeTest += entry->Size;
154+
}
155+
156+
if (entry->Page) {
157+
result << TPageTraits::GetReferenced(entry->Page) << "r ";
158+
}
159+
result << entry->Size << "b}";
160+
161+
it = it->Next();
162+
if (it == HandHot) break;
163+
}
164+
165+
Y_DEBUG_ABORT_UNLESS(sizeHot == SizeHot);
166+
Y_DEBUG_ABORT_UNLESS(sizeCold == SizeCold);
167+
Y_DEBUG_ABORT_UNLESS(sizeTest == SizeTest);
168+
if (count == 0) {
169+
Y_DEBUG_ABORT_UNLESS(!HandHot);
170+
Y_DEBUG_ABORT_UNLESS(!HandCold);
171+
Y_DEBUG_ABORT_UNLESS(!HandTest);
172+
}
173+
174+
return result;
175+
}
176+
177+
private:
178+
// sets referenced flag for a 'Cold resident' or a 'Hot' page
179+
void TouchFast(TPageEntry* entry) {
180+
Y_DEBUG_ABORT_UNLESS(entry->Page);
181+
Y_ABORT_IF(TPageTraits::GetLocation(entry->Page) == EClockProPageLocation::None);
182+
TPageTraits::SetReferenced(entry->Page, true);
183+
}
184+
185+
// transforms a 'Cold non-resident' page to a 'Hot' page
186+
TIntrusiveList<TPage> Fill(TPageEntry* entry, TPage* page) {
187+
Y_DEBUG_ABORT_UNLESS(!entry->Page);
188+
Y_ABORT_UNLESS(TPageTraits::GetLocation(page) == EClockProPageLocation::None);
189+
Y_ABORT_IF(TPageTraits::GetReferenced(page));
190+
Y_ABORT_UNLESS(entry->Size == TPageTraits::GetSize(page));
191+
192+
Y_ABORT_UNLESS(SizeTest >= entry->Size);
193+
SizeTest -= entry->Size;
194+
195+
UnlinkEntry(entry);
196+
entry->Page = page;
197+
LinkEntry(entry);
198+
199+
TPageTraits::SetLocation(page, EClockProPageLocation::Hot);
200+
SizeHot += entry->Size;
201+
202+
ColdTarget = Min(ColdTarget + entry->Size, GetTargetSize());
203+
204+
return EvictWhileFull();
205+
}
206+
207+
// adds a 'Cold resident' page
208+
TIntrusiveList<TPage> Insert(TPage* page) {
209+
Y_ABORT_UNLESS(TPageTraits::GetLocation(page) == EClockProPageLocation::None);
210+
211+
auto entry_ = MakeHolder<TPageEntry>(TPageTraits::GetKey(page), page, TPageTraits::GetSize(page));
212+
auto inserted = Entries.emplace(std::move(entry_));
213+
Y_ABORT_UNLESS(inserted.second);
214+
TPageEntry* entry = inserted.first->Get();
215+
216+
LinkEntry(entry);
217+
218+
TPageTraits::SetLocation(entry->Page, EClockProPageLocation::Cold);
219+
SizeCold += entry->Size;
220+
221+
return EvictWhileFull();
222+
}
223+
224+
TIntrusiveList<TPage> EvictWhileFull() {
225+
TIntrusiveList<TPage> evictedList;
226+
227+
// while (TPage* evictedPage = EvictOneIfFull()) {
228+
// evictedList.PushBack(evictedPage);
229+
// }
230+
231+
return evictedList;
232+
}
233+
234+
void LinkEntry(TPageEntry* entry) {
235+
if (HandHot == nullptr) { // first element
236+
HandHot = HandCold = HandTest = entry;
237+
} else {
238+
entry->LinkBefore(HandHot);
239+
}
240+
241+
if (HandHot == HandCold) {
242+
HandCold = HandCold->Prev();
243+
}
244+
}
245+
246+
void UnlinkEntry(TPageEntry* entry) {
247+
if (entry == HandHot) {
248+
HandHot = HandHot->Prev();
249+
}
250+
if (entry == HandCold) {
251+
HandCold = HandCold->Prev();
252+
}
253+
if (entry == HandTest) {
254+
HandTest = HandTest->Prev();
255+
}
256+
257+
if (entry->Empty()) { // the last entry in the cache
258+
HandHot = HandCold = HandTest = nullptr;
259+
} else {
260+
entry->Unlink();
261+
}
262+
}
263+
264+
ui64 GetTargetSize() {
265+
if (MaxSize > ReservedSize) {
266+
return MaxSize - ReservedSize;
267+
}
268+
return 1; // prevents an infinite loop while evicting
269+
}
270+
271+
private:
272+
ui64 MaxSize;
273+
ui64 ColdTarget;
274+
ui64 ReservedSize = 0;
275+
276+
// TODO: unify this with TPageMap
277+
THashSet<THolder<TPageEntry>, TPageKeyHash, TPageKeyEqual> Entries;
278+
279+
TIntrusiveListItem<TPageEntry>* HandHot = nullptr;
280+
TIntrusiveListItem<TPageEntry>* HandCold = nullptr;
281+
TIntrusiveListItem<TPageEntry>* HandTest = nullptr;
282+
ui64 SizeHot = 0, SizeCold = 0, SizeTest = 0;
283+
};
284+
285+
}

0 commit comments

Comments
 (0)