cache.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. #pragma once
  2. #include <util/generic/algorithm.h>
  3. #include <util/generic/ptr.h>
  4. #include <util/generic/intrlist.h>
  5. #include <util/generic/hash_set.h>
  6. #include <util/generic/vector.h>
  7. #include <util/generic/yexception.h>
  8. #include <utility>
  9. template <class TValue>
  10. struct TUniformSizeProvider {
  11. size_t operator()(const TValue&) {
  12. return 1;
  13. }
  14. };
  15. template <typename TKey, typename TValue, class TSizeProvider = TUniformSizeProvider<TValue>>
  16. class TLRUList {
  17. public:
  18. TLRUList(size_t maxSize, const TSizeProvider& sizeProvider = TSizeProvider())
  19. : List()
  20. , SizeProvider(sizeProvider)
  21. , ItemsAmount(0)
  22. , TotalSize(0)
  23. , MaxSize(maxSize)
  24. {
  25. }
  26. public:
  27. struct TItem: public TIntrusiveListItem<TItem> {
  28. typedef TIntrusiveListItem<TItem> TBase;
  29. TItem(const TKey& key, const TValue& value = TValue())
  30. : TBase()
  31. , Key(key)
  32. , Value(value)
  33. {
  34. }
  35. TItem(const TItem& rhs)
  36. : TBase()
  37. , Key(rhs.Key)
  38. , Value(rhs.Value)
  39. {
  40. }
  41. bool operator<(const TItem& rhs) const {
  42. return Key < rhs.Key;
  43. }
  44. bool operator==(const TItem& rhs) const {
  45. return Key == rhs.Key;
  46. }
  47. TKey Key;
  48. TValue Value;
  49. struct THash {
  50. size_t operator()(const TItem& item) const {
  51. return ::THash<TKey>()(item.Key);
  52. }
  53. };
  54. };
  55. public:
  56. TItem* Insert(TItem* item) {
  57. List.PushBack(item);
  58. ++ItemsAmount;
  59. TotalSize += SizeProvider(item->Value);
  60. return RemoveIfOverflown();
  61. }
  62. TItem* RemoveIfOverflown() {
  63. TItem* deleted = nullptr;
  64. if (TotalSize > MaxSize && ItemsAmount > 1) {
  65. deleted = GetOldest();
  66. Erase(deleted);
  67. }
  68. return deleted;
  69. }
  70. TItem* GetOldest() {
  71. typename TListType::TIterator it = List.Begin();
  72. Y_ASSERT(it != List.End());
  73. return &*it;
  74. }
  75. void Erase(TItem* item) {
  76. item->Unlink();
  77. --ItemsAmount;
  78. TotalSize -= SizeProvider(item->Value);
  79. }
  80. void Promote(TItem* item) {
  81. item->Unlink();
  82. List.PushBack(item);
  83. }
  84. size_t GetSize() const {
  85. return ItemsAmount;
  86. }
  87. size_t GetTotalSize() const {
  88. return TotalSize;
  89. }
  90. size_t GetMaxSize() const {
  91. return MaxSize;
  92. }
  93. // It does not remove current items if newSize is less than TotalSize.
  94. // Caller should use RemoveIfOverflown to clean up list in this case
  95. void SetMaxSize(size_t newSize) {
  96. MaxSize = newSize;
  97. }
  98. private:
  99. typedef TIntrusiveList<TItem> TListType;
  100. TListType List;
  101. TSizeProvider SizeProvider;
  102. size_t ItemsAmount;
  103. size_t TotalSize;
  104. size_t MaxSize;
  105. };
  106. template <typename TKey, typename TValue>
  107. class TLFUList {
  108. public:
  109. TLFUList(size_t maxSize)
  110. : List()
  111. , ListSize(0)
  112. , MaxSize(maxSize)
  113. {
  114. }
  115. struct TItem: public TIntrusiveListItem<TItem> {
  116. typedef TIntrusiveListItem<TItem> TBase;
  117. TItem(const TKey& key, const TValue& value = TValue())
  118. : TBase()
  119. , Key(key)
  120. , Value(value)
  121. , Counter(0)
  122. {
  123. }
  124. TItem(const TItem& rhs)
  125. : TBase()
  126. , Key(rhs.Key)
  127. , Value(rhs.Value)
  128. , Counter(rhs.Counter)
  129. {
  130. }
  131. bool operator<(const TItem& rhs) const {
  132. return Key < rhs.Key;
  133. }
  134. bool operator==(const TItem& rhs) const {
  135. return Key == rhs.Key;
  136. }
  137. TKey Key;
  138. TValue Value;
  139. size_t Counter;
  140. struct THash {
  141. size_t operator()(const TItem& item) const {
  142. return ::THash<TKey>()(item.Key);
  143. }
  144. };
  145. };
  146. public:
  147. TItem* Insert(TItem* item) {
  148. List.PushBack(item); // give a chance for promotion
  149. ++ListSize;
  150. return RemoveIfOverflown();
  151. }
  152. TItem* RemoveIfOverflown() {
  153. TItem* deleted = nullptr;
  154. if (ListSize > MaxSize) {
  155. deleted = GetLeastFrequentlyUsed();
  156. Erase(deleted);
  157. }
  158. return deleted;
  159. }
  160. TItem* GetLeastFrequentlyUsed() {
  161. typename TListType::TIterator it = List.Begin();
  162. Y_ASSERT(it != List.End());
  163. return &*it;
  164. }
  165. void Erase(TItem* item) {
  166. item->Unlink();
  167. --ListSize;
  168. }
  169. void Promote(TItem* item) {
  170. size_t counter = ++item->Counter;
  171. typename TListType::TIterator it = item;
  172. while (it != List.End() && counter >= it->Counter) {
  173. ++it;
  174. }
  175. item->LinkBefore(&*it);
  176. }
  177. size_t GetSize() const {
  178. return ListSize;
  179. }
  180. size_t GetMaxSize() const {
  181. return MaxSize;
  182. }
  183. // It does not remove current items if newSize is less than TotalSize.
  184. // Caller should use RemoveIfOverflown to clean up list in this case
  185. void SetMaxSize(size_t newSize) {
  186. MaxSize = newSize;
  187. }
  188. private:
  189. typedef TIntrusiveList<TItem> TListType;
  190. TListType List;
  191. size_t ListSize;
  192. size_t MaxSize;
  193. };
  194. // Least Weighted list
  195. // discards the least weighted items first
  196. // doesn't support promotion
  197. template <typename TKey, typename TValue, typename TWeight, typename TWeighter>
  198. class TLWList {
  199. public:
  200. TLWList(size_t maxSize)
  201. : Size(0)
  202. , MaxSize(maxSize)
  203. {
  204. }
  205. struct TItem {
  206. TItem(const TKey& key, const TValue& value = TValue())
  207. : Key(key)
  208. , Value(value)
  209. , Weight(TWeighter::Weight(value))
  210. {
  211. }
  212. TItem(const TItem& rhs)
  213. : Key(rhs.Key)
  214. , Value(rhs.Value)
  215. , Weight(rhs.Weight)
  216. {
  217. }
  218. bool operator<(const TItem& rhs) const {
  219. return Key < rhs.Key;
  220. }
  221. bool operator==(const TItem& rhs) const {
  222. return Key == rhs.Key;
  223. }
  224. TKey Key;
  225. TValue Value;
  226. TWeight Weight;
  227. struct THash {
  228. size_t operator()(const TItem& item) const {
  229. return ::THash<TKey>()(item.Key);
  230. }
  231. };
  232. };
  233. struct THeapComparator {
  234. bool operator()(TItem* const item1, TItem* const item2) const {
  235. return item1->Weight > item2->Weight;
  236. }
  237. };
  238. public:
  239. TItem* Insert(TItem* item) {
  240. FixHeap();
  241. if (Size >= MaxSize && item->Weight < GetLightest()->Weight) {
  242. return item;
  243. }
  244. Heap.push_back(item);
  245. PushHeap(Heap.begin(), Heap.end(), THeapComparator());
  246. ++Size;
  247. return RemoveIfOverflown();
  248. }
  249. TItem* RemoveIfOverflown() {
  250. if (Size <= MaxSize) {
  251. return nullptr;
  252. }
  253. auto lightest = GetLightest();
  254. Erase(lightest);
  255. PopHeap(Heap.begin(), Heap.end(), THeapComparator());
  256. return lightest;
  257. }
  258. TItem* GetLightest() {
  259. FixHeap();
  260. Y_ASSERT(!Heap.empty());
  261. return Heap.front();
  262. }
  263. // This method doesn't remove items from the heap.
  264. // Erased items are stored in Removed set
  265. // and will be deleted on-access (using FixHeap method)
  266. void Erase(TItem* item) {
  267. Y_ASSERT(Size > 0);
  268. --Size;
  269. Removed.insert(item);
  270. }
  271. void Promote(TItem*) {
  272. // do nothing
  273. }
  274. [[nodiscard]] size_t GetSize() const {
  275. return Size;
  276. }
  277. size_t GetMaxSize() const {
  278. return MaxSize;
  279. }
  280. // It does not remove current items if newSize is less than TotalSize.
  281. // Caller should use RemoveIfOverflown to clean up list in this case
  282. void SetMaxSize(size_t newSize) {
  283. MaxSize = newSize;
  284. }
  285. void Clear() {
  286. Heap.clear();
  287. Removed.clear();
  288. Size = 0;
  289. }
  290. private:
  291. // Physically remove erased elements from the heap
  292. void FixHeap() {
  293. if (Removed.empty()) {
  294. return;
  295. }
  296. Heap.erase(std::remove_if(Heap.begin(), Heap.end(), [this](TItem* item) {
  297. return this->Removed.contains(item);
  298. }),
  299. Heap.end());
  300. MakeHeap(Heap.begin(), Heap.end(), THeapComparator());
  301. Removed.clear();
  302. Size = Heap.size();
  303. }
  304. private:
  305. TVector<TItem*> Heap;
  306. THashSet<TItem*> Removed;
  307. size_t Size;
  308. size_t MaxSize;
  309. };
  310. template <typename TKey, typename TValue, typename TListType, typename TDeleter>
  311. class TCache {
  312. typedef typename TListType::TItem TItem;
  313. typedef typename TItem::THash THash;
  314. typedef THashMultiSet<TItem, THash> TIndex;
  315. typedef typename TIndex::iterator TIndexIterator;
  316. typedef typename TIndex::const_iterator TIndexConstIterator;
  317. public:
  318. class TIterator {
  319. public:
  320. explicit TIterator(const TIndexConstIterator& iter)
  321. : Iter(iter)
  322. {
  323. }
  324. TValue& operator*() {
  325. return const_cast<TValue&>(Iter->Value);
  326. }
  327. TValue* operator->() {
  328. return const_cast<TValue*>(&Iter->Value);
  329. }
  330. bool operator==(const TIterator& rhs) const {
  331. return Iter == rhs.Iter;
  332. }
  333. bool operator!=(const TIterator& rhs) const {
  334. return Iter != rhs.Iter;
  335. }
  336. TIterator& operator++() {
  337. ++Iter;
  338. return *this;
  339. }
  340. const TKey& Key() const {
  341. return Iter->Key;
  342. }
  343. const TValue& Value() const {
  344. return Iter->Value;
  345. }
  346. friend class TCache<TKey, TValue, TListType, TDeleter>;
  347. private:
  348. TIndexConstIterator Iter;
  349. };
  350. TCache(TListType&& list, bool multiValue = false)
  351. : Index()
  352. , List(std::move(list))
  353. , MultiValue(multiValue)
  354. {
  355. }
  356. ~TCache() {
  357. Clear();
  358. }
  359. size_t Size() const {
  360. return Index.size();
  361. }
  362. TIterator Begin() const {
  363. return TIterator(Index.begin());
  364. }
  365. TIterator End() const {
  366. return TIterator(Index.end());
  367. }
  368. TIterator Find(const TKey& key) {
  369. TIndexIterator it = Index.find(TItem(key));
  370. if (it != Index.end())
  371. List.Promote(const_cast<TItem*>(&*it));
  372. return TIterator(it);
  373. }
  374. TIterator FindWithoutPromote(const TKey& key) const {
  375. return TIterator(Index.find(TItem(key)));
  376. }
  377. // note: it shouldn't touch 'value' if it returns false.
  378. bool PickOut(const TKey& key, TValue* value) {
  379. Y_ASSERT(value);
  380. TIndexIterator it = Index.find(TItem(key));
  381. if (it == Index.end())
  382. return false;
  383. *value = it->Value;
  384. List.Erase(const_cast<TItem*>(&*it));
  385. Index.erase(it);
  386. Y_ASSERT(Index.size() == List.GetSize());
  387. return true;
  388. }
  389. bool Insert(const std::pair<TKey, TValue>& p) {
  390. return Insert(p.first, p.second);
  391. }
  392. bool Insert(const TKey& key, const TValue& value) {
  393. TItem tmpItem(key, value);
  394. if (!MultiValue && Index.find(tmpItem) != Index.end())
  395. return false;
  396. TIndexIterator it = Index.insert(tmpItem);
  397. TItem* insertedItem = const_cast<TItem*>(&*it);
  398. auto removedItem = List.Insert(insertedItem);
  399. auto insertedWasRemoved = removedItem == insertedItem;
  400. if (removedItem) {
  401. EraseFromIndex(removedItem);
  402. while ((removedItem = List.RemoveIfOverflown())) {
  403. insertedWasRemoved = insertedWasRemoved || insertedItem == removedItem;
  404. EraseFromIndex(removedItem);
  405. }
  406. }
  407. Y_ASSERT(Index.size() == List.GetSize());
  408. return !insertedWasRemoved;
  409. }
  410. void Update(const TKey& key, const TValue& value) {
  411. if (MultiValue)
  412. ythrow yexception() << "TCache: can't \"Update\" in multicache";
  413. TIterator it = Find(key);
  414. if (it != End()) {
  415. Erase(it);
  416. }
  417. Insert(key, value);
  418. Y_ASSERT(Index.size() == List.GetSize());
  419. }
  420. void Erase(TIterator it) {
  421. TItem* item = const_cast<TItem*>(&*it.Iter);
  422. List.Erase(item);
  423. TDeleter::Destroy(item->Value);
  424. Index.erase(it.Iter);
  425. Y_ASSERT(Index.size() == List.GetSize());
  426. }
  427. bool Empty() const {
  428. return Index.empty();
  429. }
  430. void Clear() {
  431. for (TIndexIterator it = Index.begin(); it != Index.end(); ++it) {
  432. TItem* item = const_cast<TItem*>(&*it);
  433. List.Erase(item);
  434. TDeleter::Destroy(item->Value);
  435. }
  436. Y_ASSERT(List.GetSize() == 0);
  437. Index.clear();
  438. }
  439. void SetMaxSize(size_t newSize) {
  440. List.SetMaxSize(newSize);
  441. TItem* removedItem = nullptr;
  442. while ((removedItem = List.RemoveIfOverflown())) {
  443. EraseFromIndex(removedItem);
  444. }
  445. Y_ASSERT(Index.size() == List.GetSize());
  446. }
  447. size_t GetMaxSize() const {
  448. return List.GetMaxSize();
  449. }
  450. protected:
  451. TIndex Index;
  452. TListType List;
  453. bool MultiValue;
  454. TIterator FindByItem(TItem* item) {
  455. std::pair<TIndexIterator, TIndexIterator> p = Index.equal_range(*item);
  456. // we have to delete the exact unlinked item (there may be multiple items for one key)
  457. TIndexIterator it;
  458. for (it = p.first; it != p.second; ++it)
  459. if (&*it == item)
  460. break;
  461. return (it == p.second ? End() : TIterator(it));
  462. }
  463. void EraseFromIndex(TItem* item) {
  464. TDeleter::Destroy(item->Value);
  465. TIterator it = FindByItem(item);
  466. Y_ASSERT(it != End());
  467. Index.erase(it.Iter);
  468. }
  469. };
  470. struct TNoopDelete {
  471. template <class T>
  472. static inline void Destroy(const T&) noexcept {
  473. }
  474. };
  475. template <typename TKey, typename TValue, typename TDeleter = TNoopDelete, class TSizeProvider = TUniformSizeProvider<TValue>>
  476. class TLRUCache: public TCache<TKey, TValue, TLRUList<TKey, TValue, TSizeProvider>, TDeleter> {
  477. using TListType = TLRUList<TKey, TValue, TSizeProvider>;
  478. typedef TCache<TKey, TValue, TListType, TDeleter> TBase;
  479. public:
  480. TLRUCache(size_t maxSize, bool multiValue = false, const TSizeProvider& sizeProvider = TSizeProvider())
  481. : TBase(TListType(maxSize, sizeProvider), multiValue)
  482. {
  483. }
  484. public:
  485. typedef typename TBase::TIterator TIterator;
  486. TValue& GetOldest() {
  487. return TBase::List.GetOldest()->Value;
  488. }
  489. TIterator FindOldest() {
  490. return TBase::Empty() ? TBase::End() : this->FindByItem(TBase::List.GetOldest());
  491. }
  492. size_t TotalSize() const {
  493. return TBase::List.GetTotalSize();
  494. }
  495. };
  496. template <typename TKey, typename TValue, typename TDeleter = TNoopDelete>
  497. class TLFUCache: public TCache<TKey, TValue, TLFUList<TKey, TValue>, TDeleter> {
  498. typedef TCache<TKey, TValue, TLFUList<TKey, TValue>, TDeleter> TBase;
  499. using TListType = TLFUList<TKey, TValue>;
  500. public:
  501. typedef typename TBase::TIterator TIterator;
  502. TLFUCache(size_t maxSize, bool multiValue = false)
  503. : TBase(TListType(maxSize), multiValue)
  504. {
  505. }
  506. TValue& GetLeastFrequentlyUsed() {
  507. return TBase::List.GetLeastFrequentlyUsed()->Value;
  508. }
  509. TIterator FindLeastFrequentlyUsed() {
  510. return TBase::Empty() ? TBase::End() : this->FindByItem(TBase::List.GetLeastFrequentlyUsed());
  511. }
  512. };
  513. // Least Weighted cache
  514. // discards the least weighted items first
  515. // doesn't support promotion
  516. template <typename TKey, typename TValue, typename TWeight, typename TWeighter, typename TDeleter = TNoopDelete>
  517. class TLWCache: public TCache<TKey, TValue, TLWList<TKey, TValue, TWeight, TWeighter>, TDeleter> {
  518. typedef TCache<TKey, TValue, TLWList<TKey, TValue, TWeight, TWeighter>, TDeleter> TBase;
  519. using TListType = TLWList<TKey, TValue, TWeight, TWeighter>;
  520. public:
  521. typedef typename TBase::TIterator TIterator;
  522. TLWCache(size_t maxSize, bool multiValue = false)
  523. : TBase(TListType(maxSize), multiValue)
  524. {
  525. }
  526. TValue& GetLightest() {
  527. return TBase::List.GetLightest()->Value;
  528. }
  529. TIterator FindLightest() {
  530. return TBase::Empty() ? TBase::End() : this->FindByItem(TBase::List.GetLightest());
  531. }
  532. };