123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- #include <library/cpp/threading/skip_list/skiplist.h>
- #include <library/cpp/getopt/small/last_getopt.h>
- #include <library/cpp/charset/ci_string.h>
- #include <util/datetime/base.h>
- #include <util/generic/map.h>
- #include <util/generic/vector.h>
- #include <functional>
- #include <util/memory/pool.h>
- #include <util/random/random.h>
- #include <util/string/join.h>
- #include <util/system/mutex.h>
- #include <util/system/thread.h>
- namespace {
- using namespace NThreading;
- ////////////////////////////////////////////////////////////////////////////////
- IOutputStream& LogInfo() {
- 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));
- }
- };
- ////////////////////////////////////////////////////////////////////////////////
- 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);
- #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;
- }
- }
- 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
- 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;
- return false;
- }
- 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;
- }
- void Run() {
- #if !defined(NDEBUG)
- 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);
- }
- 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_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_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_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()));
- }
- 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[]) {
- TTestSuite suite;
- if (!suite.Init(argc, argv)) {
- return -1;
- }
- suite.Run();
- return 0;
- }
|