|
| 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 | +template <typename TPage, typename TPageTraits> |
| 13 | +class TClockProCache : public ICacheCache<TPage> { |
| 14 | + using TPageKey = typename TPageTraits::TPageKey; |
| 15 | + |
| 16 | + enum class EPageEntryType { |
| 17 | + Test, |
| 18 | + Cold, |
| 19 | + Hot |
| 20 | + }; |
| 21 | + |
| 22 | + struct TPageEntry : public TIntrusiveListItem<TPageEntry> { |
| 23 | + EPageEntryType Type; |
| 24 | + TPageKey Key; // TODO: don't store key twice? |
| 25 | + TPage* Page; |
| 26 | + ui64 Size; |
| 27 | + bool Referenced; |
| 28 | + }; |
| 29 | + |
| 30 | + struct TPageKeyHash { |
| 31 | + inline size_t operator()(const TPageKey& key) const { |
| 32 | + return TPageTraits::GetHash(key); |
| 33 | + } |
| 34 | + }; |
| 35 | + |
| 36 | + struct TPageKeyEqual { |
| 37 | + inline bool operator()(const TPageKey& left, const TPageKey& right) const { |
| 38 | + return TPageTraits::Equals(left, right); |
| 39 | + } |
| 40 | + }; |
| 41 | + |
| 42 | +public: |
| 43 | + TClockProCache(ui64 limit) |
| 44 | + : MaxSize(limit) |
| 45 | + , ColdTarget(limit) |
| 46 | + {} |
| 47 | + |
| 48 | + TPage* EvictNext() override { |
| 49 | + // if (SmallQueue.Queue.Empty() && MainQueue.Queue.Empty()) { |
| 50 | + // return nullptr; |
| 51 | + // } |
| 52 | + |
| 53 | + // // TODO: account passive pages inside the cache |
| 54 | + // TLimit savedLimit = std::exchange(Limit, TLimit(SmallQueue.Size + MainQueue.Size - 1)); |
| 55 | + |
| 56 | + // TPage* evictedPage = EvictOneIfFull(); |
| 57 | + // Y_DEBUG_ABORT_UNLESS(evictedPage); |
| 58 | + |
| 59 | + // Limit = savedLimit; |
| 60 | + |
| 61 | + // return evictedPage; |
| 62 | + |
| 63 | + return {}; |
| 64 | + } |
| 65 | + |
| 66 | + TIntrusiveList<TPage> Touch(TPage* page) override { |
| 67 | + if (auto it = Entries.find(TPageTraits::GetKey(page)); it != Entries.end()) { |
| 68 | + TPageEntry& entry = *it->second; |
| 69 | + if (entry.Page) { |
| 70 | + entry.Referenced = true; |
| 71 | + return {}; |
| 72 | + } else { |
| 73 | + return Fill(entry, page); |
| 74 | + } |
| 75 | + } else { |
| 76 | + return Insert(page); |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + void Erase(TPage* page) override { |
| 81 | + Y_UNUSED(page); |
| 82 | + // const EClockProPageLocation location = TPageTraits::GetLocation(page); |
| 83 | + // switch (location) { |
| 84 | + // case EClockProPageLocation::None: |
| 85 | + // EraseGhost(page); |
| 86 | + // break; |
| 87 | + // case EClockProPageLocation::SmallQueue: |
| 88 | + // Erase(SmallQueue, page); |
| 89 | + // break; |
| 90 | + // case EClockProPageLocation::MainQueue: |
| 91 | + // Erase(MainQueue, page); |
| 92 | + // break; |
| 93 | + // default: |
| 94 | + // Y_ABORT("Unknown page location"); |
| 95 | + // } |
| 96 | + |
| 97 | + // TPageTraits::SetFrequency(page, 0); |
| 98 | + } |
| 99 | + |
| 100 | + void UpdateLimit(ui64 limit) override { |
| 101 | + MaxSize = limit; |
| 102 | + } |
| 103 | + |
| 104 | + TString Dump() const { |
| 105 | + TStringBuilder result; |
| 106 | + |
| 107 | + // auto dump = [&](const TQueue& queue) { |
| 108 | + // size_t count = 0; |
| 109 | + // ui64 size = 0; |
| 110 | + // for (auto it = queue.Queue.begin(); it != queue.Queue.end(); it++) { |
| 111 | + // const TPage* page = &*it; |
| 112 | + // if (count != 0) result << ", "; |
| 113 | + // result << "{" << TPageTraits::GetKeyToString(page) << " " << TPageTraits::GetFrequency(page) << "f " << TPageTraits::GetSize(page) << "b}"; |
| 114 | + // count++; |
| 115 | + // size += TPageTraits::GetSize(page); |
| 116 | + // } |
| 117 | + // Y_DEBUG_ABORT_UNLESS(queue.Size == size); |
| 118 | + // }; |
| 119 | + |
| 120 | + // result << "SmallQueue: "; |
| 121 | + // dump(SmallQueue); |
| 122 | + // result << Endl << "MainQueue: "; |
| 123 | + // dump(MainQueue); |
| 124 | + // result << Endl << "GhostQueue: "; |
| 125 | + // result << GhostQueue.Dump(); |
| 126 | + |
| 127 | + return result; |
| 128 | + } |
| 129 | + |
| 130 | +private: |
| 131 | + TIntrusiveList<TPage> Fill(TPageEntry& entry, TPage* page) { |
| 132 | + entry.Page = page; |
| 133 | + |
| 134 | + return EvictWhileFull(); |
| 135 | + } |
| 136 | + |
| 137 | + TIntrusiveList<TPage> Insert(TPage* page) { |
| 138 | + Y_UNUSED(page); |
| 139 | + |
| 140 | + return EvictWhileFull(); |
| 141 | + } |
| 142 | + |
| 143 | + TIntrusiveList<TPage> EvictWhileFull() { |
| 144 | + TIntrusiveList<TPage> evictedList; |
| 145 | + |
| 146 | + // while (TPage* evictedPage = EvictOneIfFull()) { |
| 147 | + // evictedList.PushBack(evictedPage); |
| 148 | + // } |
| 149 | + |
| 150 | + return evictedList; |
| 151 | + } |
| 152 | + |
| 153 | +private: |
| 154 | + ui64 MaxSize; |
| 155 | + ui64 ColdTarget; |
| 156 | + ui64 ReservedSize = 0; |
| 157 | + |
| 158 | + // TODO: unify this with TPageMap |
| 159 | + THashMap<TPageKey, THolder<TPageEntry>, TPageKeyHash, TPageKeyEqual> Entries; |
| 160 | + |
| 161 | + TPageEntry* HandHot = nullptr; |
| 162 | + TPageEntry* HandCold = nullptr; |
| 163 | + TPageEntry* HandTest = nullptr; |
| 164 | + ui64 SizeHot = 0, SizeCold = 0, SizeTest = 0; |
| 165 | + size_t CountHot = 0, CountCold = 0, CountTest = 0; |
| 166 | +}; |
| 167 | + |
| 168 | +} |
0 commit comments