diff options
author | Anton Samokhvalov <pg83@yandex.ru> | 2022-02-10 16:45:15 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:15 +0300 |
commit | 72cb13b4aff9bc9cf22e49251bc8fd143f82538f (patch) | |
tree | da2c34829458c7d4e74bdfbdf85dff449e9e7fb8 /library/cpp/threading/skip_list | |
parent | 778e51ba091dc39e7b7fcab2b9cf4dbedfb6f2b5 (diff) | |
download | ydb-72cb13b4aff9bc9cf22e49251bc8fd143f82538f.tar.gz |
Restoring authorship annotation for Anton Samokhvalov <pg83@yandex.ru>. Commit 1 of 2.
Diffstat (limited to 'library/cpp/threading/skip_list')
-rw-r--r-- | library/cpp/threading/skip_list/compare.h | 114 | ||||
-rw-r--r-- | library/cpp/threading/skip_list/perf/main.cpp | 586 | ||||
-rw-r--r-- | library/cpp/threading/skip_list/skiplist.h | 642 | ||||
-rw-r--r-- | library/cpp/threading/skip_list/skiplist_ut.cpp | 60 |
4 files changed, 701 insertions, 701 deletions
diff --git a/library/cpp/threading/skip_list/compare.h b/library/cpp/threading/skip_list/compare.h index ac98b3e1ce..336582a1b8 100644 --- a/library/cpp/threading/skip_list/compare.h +++ b/library/cpp/threading/skip_list/compare.h @@ -4,74 +4,74 @@ #include <util/str_stl.h> namespace NThreading { - namespace NImpl { - Y_HAS_MEMBER(compare); - Y_HAS_MEMBER(Compare); + namespace NImpl { + Y_HAS_MEMBER(compare); + Y_HAS_MEMBER(Compare); template <typename T> - inline int CompareImpl(const T& l, const T& r) { - if (l < r) { - return -1; - } else if (r < l) { - return +1; - } else { - return 0; - } + inline int CompareImpl(const T& l, const T& r) { + if (l < r) { + return -1; + } else if (r < l) { + return +1; + } else { + return 0; + } } - template <bool val> - struct TSmallCompareSelector { - template <typename T> - static inline int Compare(const T& l, const T& r) { - return CompareImpl(l, r); - } - }; + template <bool val> + struct TSmallCompareSelector { + template <typename T> + static inline int Compare(const T& l, const T& r) { + return CompareImpl(l, r); + } + }; - template <> - struct TSmallCompareSelector<true> { - template <typename T> - static inline int Compare(const T& l, const T& r) { - return l.compare(r); - } - }; + template <> + struct TSmallCompareSelector<true> { + template <typename T> + static inline int Compare(const T& l, const T& r) { + return l.compare(r); + } + }; - template <bool val> - struct TBigCompareSelector { - template <typename T> - static inline int Compare(const T& l, const T& r) { + template <bool val> + struct TBigCompareSelector { + template <typename T> + static inline int Compare(const T& l, const T& r) { return TSmallCompareSelector<THascompare<T>::value>::Compare(l, r); - } - }; - - template <> - struct TBigCompareSelector<true> { - template <typename T> - static inline int Compare(const T& l, const T& r) { - return l.Compare(r); - } - }; - + } + }; + + template <> + struct TBigCompareSelector<true> { + template <typename T> + static inline int Compare(const T& l, const T& r) { + return l.Compare(r); + } + }; + template <typename T> struct TCompareSelector: public TBigCompareSelector<THasCompare<T>::value> { - }; - } - - //////////////////////////////////////////////////////////////////////////////// - // Generic compare function + }; + } + //////////////////////////////////////////////////////////////////////////////// + // Generic compare function + template <typename T> - inline int Compare(const T& l, const T& r) { - return NImpl::TCompareSelector<T>::Compare(l, r); - } - - //////////////////////////////////////////////////////////////////////////////// - // Generic compare functor - - template <typename T> - struct TCompare { - inline int operator()(const T& l, const T& r) const { - return Compare(l, r); - } + inline int Compare(const T& l, const T& r) { + return NImpl::TCompareSelector<T>::Compare(l, r); + } + + //////////////////////////////////////////////////////////////////////////////// + // Generic compare functor + + template <typename T> + struct TCompare { + inline int operator()(const T& l, const T& r) const { + return Compare(l, r); + } }; } diff --git a/library/cpp/threading/skip_list/perf/main.cpp b/library/cpp/threading/skip_list/perf/main.cpp index 4ad52049e7..d722d43436 100644 --- a/library/cpp/threading/skip_list/perf/main.cpp +++ b/library/cpp/threading/skip_list/perf/main.cpp @@ -14,345 +14,345 @@ #include <util/system/thread.h> namespace { - using namespace NThreading; + using namespace NThreading; - //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// IOutputStream& LogInfo() { - return Cerr << TInstant::Now() << " INFO: "; - } + return Cerr << TInstant::Now() << " INFO: "; + } IOutputStream& LogError() { - return Cerr << TInstant::Now() << " ERROR: "; - } - - //////////////////////////////////////////////////////////////////////////////// - - struct TListItem { - TStringBuf Key; - TStringBuf Value; - - TListItem(const TStringBuf& key, const TStringBuf& value) - : Key(key) - , Value(value) - { - } - - int Compare(const TListItem& other) const { - return Key.compare(other.Key); - } - }; - - using TListType = TSkipList<TListItem>; - - //////////////////////////////////////////////////////////////////////////////// - - class TRandomData { - private: - TVector<char> Buffer; - - public: - TRandomData() - : Buffer(1024 * 1024) - { - for (size_t i = 0; i < Buffer.size(); ++i) { - Buffer[i] = RandomNumber<char>(); - } - } - - TStringBuf GetString(size_t len) const { - size_t start = RandomNumber(Buffer.size() - len); - return TStringBuf(&Buffer[start], len); - } - - TStringBuf GetString(size_t min, size_t max) const { - return GetString(min + RandomNumber(max - min)); + return Cerr << TInstant::Now() << " ERROR: "; + } + + //////////////////////////////////////////////////////////////////////////////// + + struct TListItem { + TStringBuf Key; + TStringBuf Value; + + TListItem(const TStringBuf& key, const TStringBuf& value) + : Key(key) + , Value(value) + { + } + + int Compare(const TListItem& other) const { + return Key.compare(other.Key); + } + }; + + using TListType = TSkipList<TListItem>; + + //////////////////////////////////////////////////////////////////////////////// + + class TRandomData { + private: + TVector<char> Buffer; + + public: + TRandomData() + : Buffer(1024 * 1024) + { + for (size_t i = 0; i < Buffer.size(); ++i) { + Buffer[i] = RandomNumber<char>(); + } + } + + TStringBuf GetString(size_t len) const { + size_t start = RandomNumber(Buffer.size() - len); + return TStringBuf(&Buffer[start], len); } - }; - //////////////////////////////////////////////////////////////////////////////// - - class TWorkerThread: public ISimpleThread { - private: - std::function<void()> Func; - TDuration Time; - - public: - TWorkerThread(std::function<void()> func) - : Func(func) - { - } - - TDuration GetTime() const { - return Time; - } - - private: - void* ThreadProc() noexcept override { - TInstant started = TInstant::Now(); - Func(); - Time = TInstant::Now() - started; - return nullptr; - } - }; - - inline TAutoPtr<TWorkerThread> StartThread(std::function<void()> func) { - TAutoPtr<TWorkerThread> thread = new TWorkerThread(func); - thread->Start(); - return thread; + TStringBuf GetString(size_t min, size_t max) const { + return GetString(min + RandomNumber(max - min)); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + class TWorkerThread: public ISimpleThread { + private: + std::function<void()> Func; + TDuration Time; + + public: + TWorkerThread(std::function<void()> func) + : Func(func) + { + } + + TDuration GetTime() const { + return Time; + } + + private: + void* ThreadProc() noexcept override { + TInstant started = TInstant::Now(); + Func(); + Time = TInstant::Now() - started; + return nullptr; + } + }; + + inline TAutoPtr<TWorkerThread> StartThread(std::function<void()> func) { + TAutoPtr<TWorkerThread> thread = new TWorkerThread(func); + thread->Start(); + return thread; } - //////////////////////////////////////////////////////////////////////////////// - - typedef std::function<void()> TTestFunc; - - struct TTest { - TString Name; - TTestFunc Func; - - TTest() { - } - - TTest(const TString& name, const TTestFunc& func) - : Name(name) - , Func(func) - { - } - }; - - //////////////////////////////////////////////////////////////////////////////// - - class TTestSuite { - private: - size_t Iterations = 1000000; - size_t KeyLen = 10; - size_t ValueLen = 100; - size_t NumReaders = 4; - size_t NumWriters = 1; - size_t BatchSize = 20; - - TMemoryPool MemoryPool; - TListType List; - TMutex Mutex; - TRandomData Random; - - TMap<TCiString, TTest> AllTests; - TVector<TTest> Tests; - - public: - TTestSuite() - : MemoryPool(64 * 1024) - , List(MemoryPool) - { - } - - bool Init(int argc, const char* argv[]) { - TVector<TString> tests; - try { - NLastGetopt::TOpts opts; - opts.AddHelpOption(); - -#define OPTION(opt, x) \ - opts.AddLongOption(opt, #x) \ - .Optional() \ - .DefaultValue(ToString(x)) \ - .StoreResult(&x) // end of OPTION - - OPTION('i', Iterations); - OPTION('k', KeyLen); - OPTION('v', ValueLen); - OPTION('r', NumReaders); - OPTION('w', NumWriters); - OPTION('b', BatchSize); + //////////////////////////////////////////////////////////////////////////////// + + typedef std::function<void()> TTestFunc; + + struct TTest { + TString Name; + TTestFunc Func; + + TTest() { + } + + TTest(const TString& name, const TTestFunc& func) + : Name(name) + , Func(func) + { + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + class TTestSuite { + private: + size_t Iterations = 1000000; + size_t KeyLen = 10; + size_t ValueLen = 100; + size_t NumReaders = 4; + size_t NumWriters = 1; + size_t BatchSize = 20; + + TMemoryPool MemoryPool; + TListType List; + TMutex Mutex; + TRandomData Random; + + TMap<TCiString, TTest> AllTests; + TVector<TTest> Tests; + + public: + TTestSuite() + : MemoryPool(64 * 1024) + , List(MemoryPool) + { + } + + bool Init(int argc, const char* argv[]) { + TVector<TString> tests; + try { + NLastGetopt::TOpts opts; + opts.AddHelpOption(); + +#define OPTION(opt, x) \ + opts.AddLongOption(opt, #x) \ + .Optional() \ + .DefaultValue(ToString(x)) \ + .StoreResult(&x) // end of OPTION + + OPTION('i', Iterations); + OPTION('k', KeyLen); + OPTION('v', ValueLen); + OPTION('r', NumReaders); + OPTION('w', NumWriters); + OPTION('b', BatchSize); #undef OPTION - NLastGetopt::TOptsParseResultException optsRes(&opts, argc, argv); - for (const auto& opt : opts.Opts_) { - const NLastGetopt::TOptParseResult* r = optsRes.FindOptParseResult(opt.Get(), true); - if (r) { - LogInfo() << "[-" << opt->GetChar() << "] " << opt->GetName() << ": " << r->Back() << Endl; - } + NLastGetopt::TOptsParseResultException optsRes(&opts, argc, argv); + for (const auto& opt : opts.Opts_) { + const NLastGetopt::TOptParseResult* r = optsRes.FindOptParseResult(opt.Get(), true); + if (r) { + LogInfo() << "[-" << opt->GetChar() << "] " << opt->GetName() << ": " << r->Back() << Endl; + } } - tests = optsRes.GetFreeArgs(); - } catch (...) { - LogError() << CurrentExceptionMessage() << Endl; - return false; + tests = optsRes.GetFreeArgs(); + } catch (...) { + LogError() << CurrentExceptionMessage() << Endl; + return false; } #define TEST(type) \ - AddTest(#type, std::bind(&TTestSuite::Y_CAT(TEST_, type), this)) // end of TEST + AddTest(#type, std::bind(&TTestSuite::Y_CAT(TEST_, type), this)) // end of TEST - TEST(Clear); - TEST(InsertRandom); - TEST(InsertSequential); - TEST(InsertSequentialSimple); - TEST(LookupRandom); - TEST(Concurrent); + TEST(Clear); + TEST(InsertRandom); + TEST(InsertSequential); + TEST(InsertSequentialSimple); + TEST(LookupRandom); + TEST(Concurrent); #undef TEST - if (tests.empty()) { - LogError() << "no tests specified, choose from: " << PrintTests() << Endl; + if (tests.empty()) { + LogError() << "no tests specified, choose from: " << PrintTests() << Endl; return false; } - - for (size_t i = 0; i < tests.size(); ++i) { + + for (size_t i = 0; i < tests.size(); ++i) { if (!AllTests.contains(tests[i])) { - LogError() << "unknown test name: " << tests[i] << Endl; - return false; - } - Tests.push_back(AllTests[tests[i]]); - } - - return true; + LogError() << "unknown test name: " << tests[i] << Endl; + return false; + } + Tests.push_back(AllTests[tests[i]]); + } + + return true; } - void Run() { + void Run() { #if !defined(NDEBUG) - LogInfo() << "*** DEBUG build! ***" << Endl; + LogInfo() << "*** DEBUG build! ***" << Endl; #endif - for (const TTest& test : Tests) { - LogInfo() << "Starting test " << test.Name << Endl; - - TInstant started = TInstant::Now(); - try { - test.Func(); - } catch (...) { - LogError() << "test " << test.Name - << " failed: " << CurrentExceptionMessage() - << Endl; - } - - LogInfo() << "List size = " << List.GetSize() << Endl; - - TDuration duration = TInstant::Now() - started; - LogInfo() << "test " << test.Name - << " duration: " << duration - << " (" << (double)duration.MicroSeconds() / (Iterations * NumWriters) << "us per iteration)" - << Endl; - LogInfo() << "Finished test " << test.Name << Endl; - } - } - - private: - void AddTest(const char* name, TTestFunc func) { - AllTests[name] = TTest(name, func); + for (const TTest& test : Tests) { + LogInfo() << "Starting test " << test.Name << Endl; + + TInstant started = TInstant::Now(); + try { + test.Func(); + } catch (...) { + LogError() << "test " << test.Name + << " failed: " << CurrentExceptionMessage() + << Endl; + } + + LogInfo() << "List size = " << List.GetSize() << Endl; + + TDuration duration = TInstant::Now() - started; + LogInfo() << "test " << test.Name + << " duration: " << duration + << " (" << (double)duration.MicroSeconds() / (Iterations * NumWriters) << "us per iteration)" + << Endl; + LogInfo() << "Finished test " << test.Name << Endl; + } } - TString PrintTests() const { - TVector<TString> names; - for (const auto& it : AllTests) { - names.push_back(it.first); - } - return JoinSeq(", ", names); + private: + void AddTest(const char* name, TTestFunc func) { + AllTests[name] = TTest(name, func); + } + + TString PrintTests() const { + TVector<TString> names; + for (const auto& it : AllTests) { + names.push_back(it.first); + } + return JoinSeq(", ", names); } - void TEST_Clear() { - List.Clear(); - } + void TEST_Clear() { + List.Clear(); + } - void TEST_InsertRandom() { - for (size_t i = 0; i < Iterations; ++i) { - List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen))); - } + void TEST_InsertRandom() { + for (size_t i = 0; i < Iterations; ++i) { + List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen))); + } } - void TEST_InsertSequential() { - TString key; - for (size_t i = 0; i < Iterations;) { - key.assign(Random.GetString(KeyLen)); - size_t batch = BatchSize / 2 + RandomNumber(BatchSize); - for (size_t j = 0; j < batch; ++j, ++i) { - key.resize(KeyLen - 1); - key.append((char)j); - List.Insert(TListItem(key, Random.GetString(ValueLen))); - } + void TEST_InsertSequential() { + TString key; + for (size_t i = 0; i < Iterations;) { + key.assign(Random.GetString(KeyLen)); + size_t batch = BatchSize / 2 + RandomNumber(BatchSize); + for (size_t j = 0; j < batch; ++j, ++i) { + key.resize(KeyLen - 1); + key.append((char)j); + List.Insert(TListItem(key, Random.GetString(ValueLen))); + } } } - void TEST_InsertSequentialSimple() { - for (size_t i = 0; i < Iterations; ++i) { - List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen))); - } + void TEST_InsertSequentialSimple() { + for (size_t i = 0; i < Iterations; ++i) { + List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen))); + } } - void TEST_LookupRandom() { - for (size_t i = 0; i < Iterations; ++i) { - List.SeekTo(TListItem(Random.GetString(KeyLen), TStringBuf())); - } + void TEST_LookupRandom() { + for (size_t i = 0; i < Iterations; ++i) { + List.SeekTo(TListItem(Random.GetString(KeyLen), TStringBuf())); + } } - void TEST_Concurrent() { - LogInfo() << "starting producers..." << Endl; - - TVector<TAutoPtr<TWorkerThread>> producers(NumWriters); - for (size_t i1 = 0; i1 < producers.size(); ++i1) { - producers[i1] = StartThread([&] { - TInstant started = TInstant::Now(); - for (size_t i2 = 0; i2 < Iterations; ++i2) { - { - TGuard<TMutex> guard(Mutex); - List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen))); - } - } - TDuration duration = TInstant::Now() - started; - LogInfo() - << "Average time for producer = " - << (double)duration.MicroSeconds() / Iterations << "us per iteration" - << Endl; - }); - } - - LogInfo() << "starting consumers..." << Endl; - - TVector<TAutoPtr<TWorkerThread>> consumers(NumReaders); - for (size_t i1 = 0; i1 < consumers.size(); ++i1) { - consumers[i1] = StartThread([&] { - TInstant started = TInstant::Now(); - for (size_t i2 = 0; i2 < Iterations; ++i2) { - List.SeekTo(TListItem(Random.GetString(KeyLen), TStringBuf())); + void TEST_Concurrent() { + LogInfo() << "starting producers..." << Endl; + + TVector<TAutoPtr<TWorkerThread>> producers(NumWriters); + for (size_t i1 = 0; i1 < producers.size(); ++i1) { + producers[i1] = StartThread([&] { + TInstant started = TInstant::Now(); + for (size_t i2 = 0; i2 < Iterations; ++i2) { + { + TGuard<TMutex> guard(Mutex); + List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen))); + } } - TDuration duration = TInstant::Now() - started; - LogInfo() - << "Average time for consumer = " - << (double)duration.MicroSeconds() / Iterations << "us per iteration" - << Endl; - }); - } - - LogInfo() << "wait for producers..." << Endl; - - TDuration producerTime; - for (size_t i = 0; i < producers.size(); ++i) { - producers[i]->Join(); - producerTime += producers[i]->GetTime(); - } - - LogInfo() << "wait for consumers..." << Endl; - - TDuration consumerTime; - for (size_t i = 0; i < consumers.size(); ++i) { - consumers[i]->Join(); - consumerTime += consumers[i]->GetTime(); - } - - LogInfo() << "average producer time: " - << producerTime.SecondsFloat() / producers.size() << " seconds" - << Endl; - - LogInfo() << "average consumer time: " - << consumerTime.SecondsFloat() / consumers.size() << " seconds" - << Endl; - } - }; - -} + TDuration duration = TInstant::Now() - started; + LogInfo() + << "Average time for producer = " + << (double)duration.MicroSeconds() / Iterations << "us per iteration" + << Endl; + }); + } + + LogInfo() << "starting consumers..." << Endl; + + TVector<TAutoPtr<TWorkerThread>> consumers(NumReaders); + for (size_t i1 = 0; i1 < consumers.size(); ++i1) { + consumers[i1] = StartThread([&] { + TInstant started = TInstant::Now(); + for (size_t i2 = 0; i2 < Iterations; ++i2) { + List.SeekTo(TListItem(Random.GetString(KeyLen), TStringBuf())); + } + TDuration duration = TInstant::Now() - started; + LogInfo() + << "Average time for consumer = " + << (double)duration.MicroSeconds() / Iterations << "us per iteration" + << Endl; + }); + } + + LogInfo() << "wait for producers..." << Endl; + + TDuration producerTime; + for (size_t i = 0; i < producers.size(); ++i) { + producers[i]->Join(); + producerTime += producers[i]->GetTime(); + } + + LogInfo() << "wait for consumers..." << Endl; + + TDuration consumerTime; + for (size_t i = 0; i < consumers.size(); ++i) { + consumers[i]->Join(); + consumerTime += consumers[i]->GetTime(); + } + + LogInfo() << "average producer time: " + << producerTime.SecondsFloat() / producers.size() << " seconds" + << Endl; + + LogInfo() << "average consumer time: " + << consumerTime.SecondsFloat() / consumers.size() << " seconds" + << Endl; + } + }; + +} //////////////////////////////////////////////////////////////////////////////// -int main(int argc, const char* argv[]) { +int main(int argc, const char* argv[]) { TTestSuite suite; if (!suite.Init(argc, argv)) { return -1; diff --git a/library/cpp/threading/skip_list/skiplist.h b/library/cpp/threading/skip_list/skiplist.h index 914a7c6ee7..c1ed46c4aa 100644 --- a/library/cpp/threading/skip_list/skiplist.h +++ b/library/cpp/threading/skip_list/skiplist.h @@ -10,399 +10,399 @@ #include <util/system/atomic.h> namespace NThreading { - //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// - class TNopCounter { - protected: - template <typename T> - void OnInsert(const T&) { - } + class TNopCounter { + protected: + template <typename T> + void OnInsert(const T&) { + } - template <typename T> - void OnUpdate(const T&) { - } + template <typename T> + void OnUpdate(const T&) { + } - void Reset() { - } - }; + void Reset() { + } + }; - //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// - class TSizeCounter { + class TSizeCounter { private: - size_t Size; + size_t Size; public: - TSizeCounter() - : Size(0) + TSizeCounter() + : Size(0) { } - size_t GetSize() const { - return Size; + size_t GetSize() const { + return Size; } - protected: - template <typename T> - void OnInsert(const T&) { - ++Size; + protected: + template <typename T> + void OnInsert(const T&) { + ++Size; } - template <typename T> - void OnUpdate(const T&) { + template <typename T> + void OnUpdate(const T&) { } - void Reset() { - Size = 0; + void Reset() { + Size = 0; } }; - //////////////////////////////////////////////////////////////////////////////// - // Append-only concurrent skip-list - // - // Readers do not require any synchronization. - // Writers should be externally synchronized. - // Nodes will be allocated using TMemoryPool instance. - - template < - typename T, - typename TComparer = TCompare<T>, - typename TAllocator = TMemoryPool, - typename TCounter = TSizeCounter, - int MaxHeight = 12, - int Branching = 4> - class TSkipList: public TCounter, private TNonCopyable { - class TNode { - private: - T Value; // should be immutable after insert - TNode* Next[]; // variable-size array maximum of MaxHeight values - - public: + //////////////////////////////////////////////////////////////////////////////// + // Append-only concurrent skip-list + // + // Readers do not require any synchronization. + // Writers should be externally synchronized. + // Nodes will be allocated using TMemoryPool instance. + + template < + typename T, + typename TComparer = TCompare<T>, + typename TAllocator = TMemoryPool, + typename TCounter = TSizeCounter, + int MaxHeight = 12, + int Branching = 4> + class TSkipList: public TCounter, private TNonCopyable { + class TNode { + private: + T Value; // should be immutable after insert + TNode* Next[]; // variable-size array maximum of MaxHeight values + + public: TNode(T&& value) : Value(std::move(value)) - { - Y_UNUSED(Next); - } - - const T& GetValue() const { - return Value; - } - - T& GetValue() { - return Value; - } - - TNode* GetNext(int height) const { - return AtomicGet(Next[height]); - } - - void Link(int height, TNode** prev) { - for (int i = 0; i < height; ++i) { - Next[i] = prev[i]->Next[i]; - AtomicSet(prev[i]->Next[i], this); - } - } - }; - + { + Y_UNUSED(Next); + } + + const T& GetValue() const { + return Value; + } + + T& GetValue() { + return Value; + } + + TNode* GetNext(int height) const { + return AtomicGet(Next[height]); + } + + void Link(int height, TNode** prev) { + for (int i = 0; i < height; ++i) { + Next[i] = prev[i]->Next[i]; + AtomicSet(prev[i]->Next[i], this); + } + } + }; + public: - class TIterator { - private: - const TSkipList* List; - const TNode* Node; - - public: - TIterator() - : List(nullptr) - , Node(nullptr) - { - } - - TIterator(const TSkipList* list, const TNode* node) - : List(list) - , Node(node) - { - } - - TIterator(const TIterator& other) - : List(other.List) - , Node(other.Node) - { + class TIterator { + private: + const TSkipList* List; + const TNode* Node; + + public: + TIterator() + : List(nullptr) + , Node(nullptr) + { + } + + TIterator(const TSkipList* list, const TNode* node) + : List(list) + , Node(node) + { + } + + TIterator(const TIterator& other) + : List(other.List) + , Node(other.Node) + { + } + + TIterator& operator=(const TIterator& other) { + List = other.List; + Node = other.Node; + return *this; + } + + void Next() { + Node = Node ? Node->GetNext(0) : nullptr; } - TIterator& operator=(const TIterator& other) { - List = other.List; - Node = other.Node; - return *this; - } + // much less efficient than Next as our list is single-linked + void Prev() { + if (Node) { + TNode* node = List->FindLessThan(Node->GetValue(), nullptr); + Node = (node != List->Head ? node : nullptr); + } + } - void Next() { - Node = Node ? Node->GetNext(0) : nullptr; - } + void Reset() { + Node = nullptr; + } - // much less efficient than Next as our list is single-linked - void Prev() { - if (Node) { - TNode* node = List->FindLessThan(Node->GetValue(), nullptr); - Node = (node != List->Head ? node : nullptr); - } - } - - void Reset() { - Node = nullptr; - } - - bool IsValid() const { - return Node != nullptr; - } + bool IsValid() const { + return Node != nullptr; + } - const T& GetValue() const { - Y_ASSERT(IsValid()); - return Node->GetValue(); - } - }; + const T& GetValue() const { + Y_ASSERT(IsValid()); + return Node->GetValue(); + } + }; - private: - TAllocator& Allocator; - TComparer Comparer; + private: + TAllocator& Allocator; + TComparer Comparer; - TNode* Head; - TAtomic Height; - TCounter Counter; + TNode* Head; + TAtomic Height; + TCounter Counter; - TNode* Prev[MaxHeight]; + TNode* Prev[MaxHeight]; - template <typename TValue> + template <typename TValue> using TComparerReturnType = std::invoke_result_t<TComparer, const T&, const TValue&>; - public: - TSkipList(TAllocator& allocator, const TComparer& comparer = TComparer()) - : Allocator(allocator) - , Comparer(comparer) - { - Init(); - } - - ~TSkipList() { - CallDtors(); - } - - void Clear() { - CallDtors(); - Allocator.ClearKeepFirstChunk(); - Init(); + public: + TSkipList(TAllocator& allocator, const TComparer& comparer = TComparer()) + : Allocator(allocator) + , Comparer(comparer) + { + Init(); + } + + ~TSkipList() { + CallDtors(); + } + + void Clear() { + CallDtors(); + Allocator.ClearKeepFirstChunk(); + Init(); } bool Insert(T value) { - TNode* node = PrepareInsert(value); - if (Y_UNLIKELY(node && Compare(node, value) == 0)) { - // we do not allow duplicates - return false; + TNode* node = PrepareInsert(value); + if (Y_UNLIKELY(node && Compare(node, value) == 0)) { + // we do not allow duplicates + return false; } node = DoInsert(std::move(value)); - TCounter::OnInsert(node->GetValue()); - return true; + TCounter::OnInsert(node->GetValue()); + return true; } - template <typename TInsertAction, typename TUpdateAction> - bool Insert(const T& value, TInsertAction insert, TUpdateAction update) { - TNode* node = PrepareInsert(value); - if (Y_UNLIKELY(node && Compare(node, value) == 0)) { - if (update(node->GetValue())) { - TCounter::OnUpdate(node->GetValue()); - return true; - } - // we do not allow duplicates - return false; - } - node = DoInsert(insert(value)); - TCounter::OnInsert(node->GetValue()); - return true; - } - - template <typename TValue> - bool Contains(const TValue& value) const { - TNode* node = FindGreaterThanOrEqual(value); - return node && Compare(node, value) == 0; - } - - TIterator SeekToFirst() const { - return TIterator(this, FindFirst()); - } - - TIterator SeekToLast() const { - TNode* last = FindLast(); - return TIterator(this, last != Head ? last : nullptr); - } - - template <typename TValue> - TIterator SeekTo(const TValue& value) const { - return TIterator(this, FindGreaterThanOrEqual(value)); + template <typename TInsertAction, typename TUpdateAction> + bool Insert(const T& value, TInsertAction insert, TUpdateAction update) { + TNode* node = PrepareInsert(value); + if (Y_UNLIKELY(node && Compare(node, value) == 0)) { + if (update(node->GetValue())) { + TCounter::OnUpdate(node->GetValue()); + return true; + } + // we do not allow duplicates + return false; + } + node = DoInsert(insert(value)); + TCounter::OnInsert(node->GetValue()); + return true; + } + + template <typename TValue> + bool Contains(const TValue& value) const { + TNode* node = FindGreaterThanOrEqual(value); + return node && Compare(node, value) == 0; + } + + TIterator SeekToFirst() const { + return TIterator(this, FindFirst()); + } + + TIterator SeekToLast() const { + TNode* last = FindLast(); + return TIterator(this, last != Head ? last : nullptr); + } + + template <typename TValue> + TIterator SeekTo(const TValue& value) const { + return TIterator(this, FindGreaterThanOrEqual(value)); } - private: - static int RandomHeight() { - int height = 1; - while (height < MaxHeight && (RandomNumber<unsigned int>() % Branching) == 0) { - ++height; - } - return height; + private: + static int RandomHeight() { + int height = 1; + while (height < MaxHeight && (RandomNumber<unsigned int>() % Branching) == 0) { + ++height; + } + return height; + } + + void Init() { + Head = AllocateRootNode(); + Height = 1; + TCounter::Reset(); + + for (int i = 0; i < MaxHeight; ++i) { + Prev[i] = Head; + } } - void Init() { - Head = AllocateRootNode(); - Height = 1; - TCounter::Reset(); - - for (int i = 0; i < MaxHeight; ++i) { - Prev[i] = Head; + void CallDtors() { + if (!TTypeTraits<T>::IsPod) { + // we should explicitly call destructors for our nodes + TNode* node = Head->GetNext(0); + while (node) { + TNode* next = node->GetNext(0); + node->~TNode(); + node = next; + } } } - void CallDtors() { - if (!TTypeTraits<T>::IsPod) { - // we should explicitly call destructors for our nodes - TNode* node = Head->GetNext(0); - while (node) { - TNode* next = node->GetNext(0); - node->~TNode(); - node = next; - } - } - } - - TNode* AllocateRootNode() { - size_t size = sizeof(TNode) + sizeof(TNode*) * MaxHeight; - void* buffer = Allocator.Allocate(size); - memset(buffer, 0, size); - return static_cast<TNode*>(buffer); - } + TNode* AllocateRootNode() { + size_t size = sizeof(TNode) + sizeof(TNode*) * MaxHeight; + void* buffer = Allocator.Allocate(size); + memset(buffer, 0, size); + return static_cast<TNode*>(buffer); + } TNode* AllocateNode(T&& value, int height) { - size_t size = sizeof(TNode) + sizeof(TNode*) * height; - void* buffer = Allocator.Allocate(size); - memset(buffer, 0, size); + size_t size = sizeof(TNode) + sizeof(TNode*) * height; + void* buffer = Allocator.Allocate(size); + memset(buffer, 0, size); return new (buffer) TNode(std::move(value)); - } - - TNode* FindFirst() const { - return Head->GetNext(0); - } - - TNode* FindLast() const { - TNode* node = Head; - int height = AtomicGet(Height) - 1; - - while (true) { - TNode* next = node->GetNext(height); - if (next) { - node = next; - continue; - } - - if (height) { - --height; - } else { - return node; - } + } + + TNode* FindFirst() const { + return Head->GetNext(0); + } + + TNode* FindLast() const { + TNode* node = Head; + int height = AtomicGet(Height) - 1; + + while (true) { + TNode* next = node->GetNext(height); + if (next) { + node = next; + continue; + } + + if (height) { + --height; + } else { + return node; + } } } - template <typename TValue> - TComparerReturnType<TValue> Compare(const TNode* node, const TValue& value) const { - return Comparer(node->GetValue(), value); - } - - template <typename TValue> - TNode* FindLessThan(const TValue& value, TNode** links) const { - TNode* node = Head; - int height = AtomicGet(Height) - 1; - - TNode* prev = nullptr; - while (true) { - TNode* next = node->GetNext(height); - if (next && next != prev) { - TComparerReturnType<TValue> cmp = Compare(next, value); - if (cmp < 0) { - node = next; - continue; - } + template <typename TValue> + TComparerReturnType<TValue> Compare(const TNode* node, const TValue& value) const { + return Comparer(node->GetValue(), value); + } + + template <typename TValue> + TNode* FindLessThan(const TValue& value, TNode** links) const { + TNode* node = Head; + int height = AtomicGet(Height) - 1; + + TNode* prev = nullptr; + while (true) { + TNode* next = node->GetNext(height); + if (next && next != prev) { + TComparerReturnType<TValue> cmp = Compare(next, value); + if (cmp < 0) { + node = next; + continue; + } } - if (links) { - // collect links from upper levels - links[height] = node; - } - - if (height) { - prev = next; - --height; - } else { - return node; - } + if (links) { + // collect links from upper levels + links[height] = node; + } + + if (height) { + prev = next; + --height; + } else { + return node; + } } } - template <typename TValue> - TNode* FindGreaterThanOrEqual(const TValue& value) const { - TNode* node = Head; - int height = AtomicGet(Height) - 1; - - TNode* prev = nullptr; - while (true) { - TNode* next = node->GetNext(height); - if (next && next != prev) { - TComparerReturnType<TValue> cmp = Compare(next, value); - if (cmp < 0) { - node = next; - continue; - } - if (cmp == 0) { - return next; - } + template <typename TValue> + TNode* FindGreaterThanOrEqual(const TValue& value) const { + TNode* node = Head; + int height = AtomicGet(Height) - 1; + + TNode* prev = nullptr; + while (true) { + TNode* next = node->GetNext(height); + if (next && next != prev) { + TComparerReturnType<TValue> cmp = Compare(next, value); + if (cmp < 0) { + node = next; + continue; + } + if (cmp == 0) { + return next; + } } - - if (height) { - prev = next; - --height; - } else { + + if (height) { + prev = next; + --height; + } else { return next; } } - } + } - TNode* PrepareInsert(const T& value) { - TNode* prev = Prev[0]; - TNode* next = prev->GetNext(0); - if ((prev == Head || Compare(prev, value) < 0) && (next == nullptr || Compare(next, value) >= 0)) { - // avoid seek in case of sequential insert + TNode* PrepareInsert(const T& value) { + TNode* prev = Prev[0]; + TNode* next = prev->GetNext(0); + if ((prev == Head || Compare(prev, value) < 0) && (next == nullptr || Compare(next, value) >= 0)) { + // avoid seek in case of sequential insert } else { - prev = FindLessThan(value, Prev); - next = prev->GetNext(0); + prev = FindLessThan(value, Prev); + next = prev->GetNext(0); } - return next; + return next; } TNode* DoInsert(T&& value) { - // choose level to place new node - int currentHeight = AtomicGet(Height); - int height = RandomHeight(); - if (height > currentHeight) { - for (int i = currentHeight; i < height; ++i) { - // head should link to all levels - Prev[i] = Head; - } - AtomicSet(Height, height); + // choose level to place new node + int currentHeight = AtomicGet(Height); + int height = RandomHeight(); + if (height > currentHeight) { + for (int i = currentHeight; i < height; ++i) { + // head should link to all levels + Prev[i] = Head; + } + AtomicSet(Height, height); } TNode* node = AllocateNode(std::move(value), height); - node->Link(height, Prev); + node->Link(height, Prev); - // keep last inserted node to optimize sequential inserts - for (int i = 0; i < height; i++) { - Prev[i] = node; - } - return node; + // keep last inserted node to optimize sequential inserts + for (int i = 0; i < height; i++) { + Prev[i] = node; + } + return node; } - }; + }; -} +} diff --git a/library/cpp/threading/skip_list/skiplist_ut.cpp b/library/cpp/threading/skip_list/skiplist_ut.cpp index 52fcffda66..fdc831dffd 100644 --- a/library/cpp/threading/skip_list/skiplist_ut.cpp +++ b/library/cpp/threading/skip_list/skiplist_ut.cpp @@ -3,41 +3,41 @@ #include <library/cpp/testing/unittest/registar.h> namespace NThreading { - namespace { - struct TTestObject { - static size_t Count; - int Tag; - - TTestObject(int tag) - : Tag(tag) - { - ++Count; - } - - TTestObject(const TTestObject& other) - : Tag(other.Tag) - { - ++Count; - } - - ~TTestObject() { - --Count; - } - - bool operator<(const TTestObject& other) const { - return Tag < other.Tag; - } - }; - - size_t TTestObject::Count = 0; + namespace { + struct TTestObject { + static size_t Count; + int Tag; + + TTestObject(int tag) + : Tag(tag) + { + ++Count; + } + + TTestObject(const TTestObject& other) + : Tag(other.Tag) + { + ++Count; + } + + ~TTestObject() { + --Count; + } + + bool operator<(const TTestObject& other) const { + return Tag < other.Tag; + } + }; + + size_t TTestObject::Count = 0; } - //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// Y_UNIT_TEST_SUITE(TSkipListTest) { Y_UNIT_TEST(ShouldBeEmptyAfterCreation) { - TMemoryPool pool(1024); + TMemoryPool pool(1024); TSkipList<int> list(pool); UNIT_ASSERT_EQUAL(list.GetSize(), 0); @@ -182,4 +182,4 @@ namespace NThreading { } } -} +} |