diff options
author | ivanmautin <ivanmautin@yandex-team.com> | 2024-06-06 09:57:03 +0300 |
---|---|---|
committer | ivanmautin <ivanmautin@yandex-team.com> | 2024-06-06 10:07:42 +0300 |
commit | 3babd5b1391836f4f4fdb3add9064a59707f9f7d (patch) | |
tree | 74336eeadabd9f2d99cdd4d93742c6c1fc240ae3 /library/cpp/cache/ut | |
parent | caa721da15eab69ba0c5eae383d494627ff115f5 (diff) | |
download | ydb-3babd5b1391836f4f4fdb3add9064a59707f9f7d.tar.gz |
add TThreadSafeLRUCacheWithSizeProvider wrapper
На данный момент никак нельзя создать thread-safe кэш с произвольным SizeProvider, из-за того, что это не позволяет сделать шаблон `TThreadSafeCache`, при этом отредактировтаь его тоже не удастся, так как для этого нужно передать дополнительный параметр `typename TSizeProvider`, что сломает обратную совместимость, так как шаблон принимает далее переменное число аргументов (см. [TThreadSafeCache](https://a.yandex-team.ru/arcadia/library/cpp/cache/thread_safe_cache.h?rev=rXXXXXX#L15))
В связи с этим добавлен еще один хелпер, для создания LRUCache с TSizeProvider
293511a33b45f23d8afc9ff217a817481401932c
Diffstat (limited to 'library/cpp/cache/ut')
-rw-r--r-- | library/cpp/cache/ut/cache_ut.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/library/cpp/cache/ut/cache_ut.cpp b/library/cpp/cache/ut/cache_ut.cpp index 36a3eff094..8546bc166c 100644 --- a/library/cpp/cache/ut/cache_ut.cpp +++ b/library/cpp/cache/ut/cache_ut.cpp @@ -735,3 +735,265 @@ Y_UNIT_TEST_SUITE(TThreadSafeLRUCacheTest) { UNIT_ASSERT(cache.Contains(0)); } } + +Y_UNIT_TEST_SUITE(TThreadSafeLFUCacheTest) { + using TCache = TThreadSafeLFUCache<size_t, TString, size_t>; + + TVector<TString> Values = {"a", "bb", "ccc", "dddd", "eeeee"}; + + class TCallbacks: public TCache::ICallbacks { + public: + TKey GetKey(size_t i) const override { + return i; + } + + TValue* CreateObject(size_t i) const override { + UNIT_ASSERT(i < Values.size()); + ++Creations; + return new TString(Values[i]); + } + + mutable size_t Creations = 0; + }; + + Y_UNIT_TEST(SimpleTest) { + for (size_t i = 0; i < Values.size(); ++i) { + const TString data = *TCache::Get<TCallbacks>(i); + UNIT_ASSERT_EQUAL(data, Values[i]); + } + } + + Y_UNIT_TEST(InsertUpdateTest) { + TCallbacks callbacks; + TCache cache(callbacks, 10); + + cache.Insert(2, MakeAtomicShared<TString>("hj")); + TAtomicSharedPtr<TString> item = cache.Get(2); + + UNIT_ASSERT(callbacks.Creations == 0); + UNIT_ASSERT(*item == "hj"); + + cache.Insert(2, MakeAtomicShared<TString>("hjk")); + item = cache.Get(2); + + UNIT_ASSERT(callbacks.Creations == 0); + UNIT_ASSERT(*item == "hj"); + + cache.Update(2, MakeAtomicShared<TString>("hjk")); + item = cache.Get(2); + + UNIT_ASSERT_EQUAL(cache.TotalSize(), 1); + UNIT_ASSERT_EQUAL(cache.Size(), 1); + UNIT_ASSERT(callbacks.Creations == 0); + UNIT_ASSERT(*item == "hjk"); + } + + Y_UNIT_TEST(LFUTest) { + TCallbacks callbacks; + TCache cache(callbacks, 3); + size_t expectedCreations = 0; + + UNIT_ASSERT_EQUAL(cache.GetMaxSize(), 3); + auto item = cache.Get(0); + UNIT_ASSERT(*item == "a"); + UNIT_ASSERT(cache.TotalSize() == 1); + UNIT_ASSERT(callbacks.Creations == ++expectedCreations); + + item = cache.Get(1); + UNIT_ASSERT(*item == "bb"); + UNIT_ASSERT(cache.TotalSize() == 2); + UNIT_ASSERT(callbacks.Creations == ++expectedCreations); + + item = cache.Get(2); + UNIT_ASSERT(*item == "ccc"); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(callbacks.Creations == ++expectedCreations); + + cache.Get(0); + cache.Get(0); + cache.Get(0); + + cache.Get(1); + + cache.Get(2); + cache.Get(2); + + // evict 1 + item = cache.Get(3); + UNIT_ASSERT(*item == "dddd"); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(callbacks.Creations == ++expectedCreations); + + // check that 0 was evicted and left only 1 2 3 + item = cache.Get(0); + UNIT_ASSERT(*item == "a"); + + item = cache.Get(2); + UNIT_ASSERT(*item == "ccc"); + + item = cache.Get(3); + UNIT_ASSERT(*item == "dddd"); + UNIT_ASSERT(callbacks.Creations == expectedCreations); + + cache.Get(0); + cache.Get(2); + cache.Get(3); + + // evict 3 + item = cache.Get(4); + UNIT_ASSERT(*item == "eeeee"); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(callbacks.Creations == ++expectedCreations); + + // check that 1 was evicted and left only 2 3 4 + item = cache.Get(0); + UNIT_ASSERT(*item == "a"); + + item = cache.Get(2); + UNIT_ASSERT(*item == "ccc"); + + item = cache.Get(4); + UNIT_ASSERT(*item == "eeeee"); + UNIT_ASSERT(callbacks.Creations == expectedCreations); + } +} + +Y_UNIT_TEST_SUITE(TThreadSafeLRUCacheWithSizeProviderTest) { + struct TStringLengthSizeProvider { + size_t operator()(const TString& s) const { + return s.size(); + } + }; + using TCache = TThreadSafeLRUCacheWithSizeProvider<size_t, TString, TStringLengthSizeProvider, size_t>; + + TVector<TString> Values = {"a", "bb", "ccc", "dddd", "eeeee"}; + + class TCallbacks: public TCache::ICallbacks { + public: + TKey GetKey(size_t i) const override { + return i; + } + TValue* CreateObject(size_t i) const override { + UNIT_ASSERT(i < Values.size()); + return new TString(Values[i]); + } + }; + + Y_UNIT_TEST(Test) { + TCallbacks callbacks; + TCache cache(callbacks, 6); + + auto item = cache.Get(0); + UNIT_ASSERT(*item == "a"); + UNIT_ASSERT(cache.TotalSize() == 1); + UNIT_ASSERT(cache.Size() == 1); + + item = cache.Get(1); + UNIT_ASSERT(*item == "bb"); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(cache.Size() == 2); + + item = cache.Get(2); + UNIT_ASSERT(*item == "ccc"); + UNIT_ASSERT(cache.TotalSize() == 6); + UNIT_ASSERT(cache.Size() == 3); + + item = cache.Get(3); + UNIT_ASSERT(*item == "dddd"); + UNIT_ASSERT(cache.TotalSize() == 4); + UNIT_ASSERT(cache.Size() == 1); + + item = cache.Get(0); + UNIT_ASSERT(*item == "a"); + UNIT_ASSERT(cache.TotalSize() == 5); + UNIT_ASSERT(cache.Size() == 2); + + item = cache.Get(4); + UNIT_ASSERT(*item == "eeeee"); + UNIT_ASSERT(cache.TotalSize() == 6); + UNIT_ASSERT(cache.Size() == 2); + + cache.Update(0, MakeAtomicShared<TString>("aaa")); + item = cache.Get(0); + UNIT_ASSERT(*item == "aaa"); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(cache.Size() == 1); + } +} + +Y_UNIT_TEST_SUITE(TThreadSafeLFUCacheWithSizeProviderTest) { + struct TStringLengthSizeProvider { + size_t operator()(const TString& s) const { + return s.size(); + } + }; + using TCache = TThreadSafeLFUCacheWithSizeProvider<size_t, TString, TStringLengthSizeProvider, size_t>; + + TVector<TString> Values = {"a", "bb", "ccc", "dddd", "eeeee"}; + + class TCallbacks: public TCache::ICallbacks { + public: + TKey GetKey(size_t i) const override { + return i; + } + TValue* CreateObject(size_t i) const override { + UNIT_ASSERT(i < Values.size()); + return new TString(Values[i]); + } + }; + + Y_UNIT_TEST(Test) { + TCallbacks callbacks; + TCache cache(callbacks, 6); + + auto item = cache.Get(0); + UNIT_ASSERT(*item == "a"); + UNIT_ASSERT(cache.TotalSize() == 1); + UNIT_ASSERT(cache.Size() == 1); + + item = cache.Get(1); + UNIT_ASSERT(*item == "bb"); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(cache.Size() == 2); + + item = cache.Get(2); + UNIT_ASSERT(*item == "ccc"); + UNIT_ASSERT(cache.TotalSize() == 6); + UNIT_ASSERT(cache.Size() == 3); + + cache.Get(0); + cache.Get(0); + cache.Get(0); + + cache.Get(1); + + cache.Get(2); + cache.Get(2); + + // evict 1. 0 and 3 left + item = cache.Get(3); + UNIT_ASSERT(*item == "dddd"); + UNIT_ASSERT(cache.TotalSize() == 5); + UNIT_ASSERT(cache.Size() == 2); + + cache.Get(0); + UNIT_ASSERT(cache.TotalSize() == 5); + UNIT_ASSERT(cache.Size() == 2); + + // evict 3. 0 and 4 left + cache.Get(4); + UNIT_ASSERT(cache.TotalSize() == 6); + UNIT_ASSERT(cache.Size() == 2); + + cache.Get(4); + cache.Get(4); + cache.Get(4); + cache.Get(4); + cache.Get(4); + // evict both 0 and 4, even if evict only 4 was ok to fit size + // thats because 4 used more times, so it is deleted only after 0 + cache.Get(2); + UNIT_ASSERT(cache.TotalSize() == 3); + UNIT_ASSERT(cache.Size() == 1); + } +} |