aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/threading/skip_list
diff options
context:
space:
mode:
authorAnton Samokhvalov <pg83@yandex.ru>2022-02-10 16:45:15 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:45:15 +0300
commit72cb13b4aff9bc9cf22e49251bc8fd143f82538f (patch)
treeda2c34829458c7d4e74bdfbdf85dff449e9e7fb8 /library/cpp/threading/skip_list
parent778e51ba091dc39e7b7fcab2b9cf4dbedfb6f2b5 (diff)
downloadydb-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.h114
-rw-r--r--library/cpp/threading/skip_list/perf/main.cpp586
-rw-r--r--library/cpp/threading/skip_list/skiplist.h642
-rw-r--r--library/cpp/threading/skip_list/skiplist_ut.cpp60
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 {
}
}
-}
+}