main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. #include <library/cpp/threading/skip_list/skiplist.h>
  2. #include <library/cpp/getopt/small/last_getopt.h>
  3. #include <library/cpp/charset/ci_string.h>
  4. #include <util/datetime/base.h>
  5. #include <util/generic/map.h>
  6. #include <util/generic/vector.h>
  7. #include <functional>
  8. #include <util/memory/pool.h>
  9. #include <util/random/random.h>
  10. #include <util/string/join.h>
  11. #include <util/system/mutex.h>
  12. #include <util/system/thread.h>
  13. namespace {
  14. using namespace NThreading;
  15. ////////////////////////////////////////////////////////////////////////////////
  16. IOutputStream& LogInfo() {
  17. return Cerr << TInstant::Now() << " INFO: ";
  18. }
  19. IOutputStream& LogError() {
  20. return Cerr << TInstant::Now() << " ERROR: ";
  21. }
  22. ////////////////////////////////////////////////////////////////////////////////
  23. struct TListItem {
  24. TStringBuf Key;
  25. TStringBuf Value;
  26. TListItem(const TStringBuf& key, const TStringBuf& value)
  27. : Key(key)
  28. , Value(value)
  29. {
  30. }
  31. int Compare(const TListItem& other) const {
  32. return Key.compare(other.Key);
  33. }
  34. };
  35. using TListType = TSkipList<TListItem>;
  36. ////////////////////////////////////////////////////////////////////////////////
  37. class TRandomData {
  38. private:
  39. TVector<char> Buffer;
  40. public:
  41. TRandomData()
  42. : Buffer(1024 * 1024)
  43. {
  44. for (size_t i = 0; i < Buffer.size(); ++i) {
  45. Buffer[i] = RandomNumber<char>();
  46. }
  47. }
  48. TStringBuf GetString(size_t len) const {
  49. size_t start = RandomNumber(Buffer.size() - len);
  50. return TStringBuf(&Buffer[start], len);
  51. }
  52. TStringBuf GetString(size_t min, size_t max) const {
  53. return GetString(min + RandomNumber(max - min));
  54. }
  55. };
  56. ////////////////////////////////////////////////////////////////////////////////
  57. class TWorkerThread: public ISimpleThread {
  58. private:
  59. std::function<void()> Func;
  60. TDuration Time;
  61. public:
  62. TWorkerThread(std::function<void()> func)
  63. : Func(func)
  64. {
  65. }
  66. TDuration GetTime() const {
  67. return Time;
  68. }
  69. private:
  70. void* ThreadProc() noexcept override {
  71. TInstant started = TInstant::Now();
  72. Func();
  73. Time = TInstant::Now() - started;
  74. return nullptr;
  75. }
  76. };
  77. inline TAutoPtr<TWorkerThread> StartThread(std::function<void()> func) {
  78. TAutoPtr<TWorkerThread> thread = new TWorkerThread(func);
  79. thread->Start();
  80. return thread;
  81. }
  82. ////////////////////////////////////////////////////////////////////////////////
  83. typedef std::function<void()> TTestFunc;
  84. struct TTest {
  85. TString Name;
  86. TTestFunc Func;
  87. TTest() {
  88. }
  89. TTest(const TString& name, const TTestFunc& func)
  90. : Name(name)
  91. , Func(func)
  92. {
  93. }
  94. };
  95. ////////////////////////////////////////////////////////////////////////////////
  96. class TTestSuite {
  97. private:
  98. size_t Iterations = 1000000;
  99. size_t KeyLen = 10;
  100. size_t ValueLen = 100;
  101. size_t NumReaders = 4;
  102. size_t NumWriters = 1;
  103. size_t BatchSize = 20;
  104. TMemoryPool MemoryPool;
  105. TListType List;
  106. TMutex Mutex;
  107. TRandomData Random;
  108. TMap<TCiString, TTest> AllTests;
  109. TVector<TTest> Tests;
  110. public:
  111. TTestSuite()
  112. : MemoryPool(64 * 1024)
  113. , List(MemoryPool)
  114. {
  115. }
  116. bool Init(int argc, const char* argv[]) {
  117. TVector<TString> tests;
  118. try {
  119. NLastGetopt::TOpts opts;
  120. opts.AddHelpOption();
  121. #define OPTION(opt, x) \
  122. opts.AddLongOption(opt, #x) \
  123. .Optional() \
  124. .DefaultValue(ToString(x)) \
  125. .StoreResult(&x) // end of OPTION
  126. OPTION('i', Iterations);
  127. OPTION('k', KeyLen);
  128. OPTION('v', ValueLen);
  129. OPTION('r', NumReaders);
  130. OPTION('w', NumWriters);
  131. OPTION('b', BatchSize);
  132. #undef OPTION
  133. NLastGetopt::TOptsParseResultException optsRes(&opts, argc, argv);
  134. for (const auto& opt : opts.Opts_) {
  135. const NLastGetopt::TOptParseResult* r = optsRes.FindOptParseResult(opt.Get(), true);
  136. if (r) {
  137. LogInfo() << "[-" << opt->GetChar() << "] " << opt->GetName() << ": " << r->Back() << Endl;
  138. }
  139. }
  140. tests = optsRes.GetFreeArgs();
  141. } catch (...) {
  142. LogError() << CurrentExceptionMessage() << Endl;
  143. return false;
  144. }
  145. #define TEST(type) \
  146. AddTest(#type, std::bind(&TTestSuite::Y_CAT(TEST_, type), this)) // end of TEST
  147. TEST(Clear);
  148. TEST(InsertRandom);
  149. TEST(InsertSequential);
  150. TEST(InsertSequentialSimple);
  151. TEST(LookupRandom);
  152. TEST(Concurrent);
  153. #undef TEST
  154. if (tests.empty()) {
  155. LogError() << "no tests specified, choose from: " << PrintTests() << Endl;
  156. return false;
  157. }
  158. for (size_t i = 0; i < tests.size(); ++i) {
  159. if (!AllTests.contains(tests[i])) {
  160. LogError() << "unknown test name: " << tests[i] << Endl;
  161. return false;
  162. }
  163. Tests.push_back(AllTests[tests[i]]);
  164. }
  165. return true;
  166. }
  167. void Run() {
  168. #if !defined(NDEBUG)
  169. LogInfo() << "*** DEBUG build! ***" << Endl;
  170. #endif
  171. for (const TTest& test : Tests) {
  172. LogInfo() << "Starting test " << test.Name << Endl;
  173. TInstant started = TInstant::Now();
  174. try {
  175. test.Func();
  176. } catch (...) {
  177. LogError() << "test " << test.Name
  178. << " failed: " << CurrentExceptionMessage()
  179. << Endl;
  180. }
  181. LogInfo() << "List size = " << List.GetSize() << Endl;
  182. TDuration duration = TInstant::Now() - started;
  183. LogInfo() << "test " << test.Name
  184. << " duration: " << duration
  185. << " (" << (double)duration.MicroSeconds() / (Iterations * NumWriters) << "us per iteration)"
  186. << Endl;
  187. LogInfo() << "Finished test " << test.Name << Endl;
  188. }
  189. }
  190. private:
  191. void AddTest(const char* name, TTestFunc func) {
  192. AllTests[name] = TTest(name, func);
  193. }
  194. TString PrintTests() const {
  195. TVector<TString> names;
  196. for (const auto& it : AllTests) {
  197. names.push_back(it.first);
  198. }
  199. return JoinSeq(", ", names);
  200. }
  201. void TEST_Clear() {
  202. List.Clear();
  203. }
  204. void TEST_InsertRandom() {
  205. for (size_t i = 0; i < Iterations; ++i) {
  206. List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen)));
  207. }
  208. }
  209. void TEST_InsertSequential() {
  210. TString key;
  211. for (size_t i = 0; i < Iterations;) {
  212. key.assign(Random.GetString(KeyLen));
  213. size_t batch = BatchSize / 2 + RandomNumber(BatchSize);
  214. for (size_t j = 0; j < batch; ++j, ++i) {
  215. key.resize(KeyLen - 1);
  216. key.append((char)j);
  217. List.Insert(TListItem(key, Random.GetString(ValueLen)));
  218. }
  219. }
  220. }
  221. void TEST_InsertSequentialSimple() {
  222. for (size_t i = 0; i < Iterations; ++i) {
  223. List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen)));
  224. }
  225. }
  226. void TEST_LookupRandom() {
  227. for (size_t i = 0; i < Iterations; ++i) {
  228. List.SeekTo(TListItem(Random.GetString(KeyLen), TStringBuf()));
  229. }
  230. }
  231. void TEST_Concurrent() {
  232. LogInfo() << "starting producers..." << Endl;
  233. TVector<TAutoPtr<TWorkerThread>> producers(NumWriters);
  234. for (size_t i1 = 0; i1 < producers.size(); ++i1) {
  235. producers[i1] = StartThread([&] {
  236. TInstant started = TInstant::Now();
  237. for (size_t i2 = 0; i2 < Iterations; ++i2) {
  238. {
  239. TGuard<TMutex> guard(Mutex);
  240. List.Insert(TListItem(Random.GetString(KeyLen), Random.GetString(ValueLen)));
  241. }
  242. }
  243. TDuration duration = TInstant::Now() - started;
  244. LogInfo()
  245. << "Average time for producer = "
  246. << (double)duration.MicroSeconds() / Iterations << "us per iteration"
  247. << Endl;
  248. });
  249. }
  250. LogInfo() << "starting consumers..." << Endl;
  251. TVector<TAutoPtr<TWorkerThread>> consumers(NumReaders);
  252. for (size_t i1 = 0; i1 < consumers.size(); ++i1) {
  253. consumers[i1] = StartThread([&] {
  254. TInstant started = TInstant::Now();
  255. for (size_t i2 = 0; i2 < Iterations; ++i2) {
  256. List.SeekTo(TListItem(Random.GetString(KeyLen), TStringBuf()));
  257. }
  258. TDuration duration = TInstant::Now() - started;
  259. LogInfo()
  260. << "Average time for consumer = "
  261. << (double)duration.MicroSeconds() / Iterations << "us per iteration"
  262. << Endl;
  263. });
  264. }
  265. LogInfo() << "wait for producers..." << Endl;
  266. TDuration producerTime;
  267. for (size_t i = 0; i < producers.size(); ++i) {
  268. producers[i]->Join();
  269. producerTime += producers[i]->GetTime();
  270. }
  271. LogInfo() << "wait for consumers..." << Endl;
  272. TDuration consumerTime;
  273. for (size_t i = 0; i < consumers.size(); ++i) {
  274. consumers[i]->Join();
  275. consumerTime += consumers[i]->GetTime();
  276. }
  277. LogInfo() << "average producer time: "
  278. << producerTime.SecondsFloat() / producers.size() << " seconds"
  279. << Endl;
  280. LogInfo() << "average consumer time: "
  281. << consumerTime.SecondsFloat() / consumers.size() << " seconds"
  282. << Endl;
  283. }
  284. };
  285. }
  286. ////////////////////////////////////////////////////////////////////////////////
  287. int main(int argc, const char* argv[]) {
  288. TTestSuite suite;
  289. if (!suite.Init(argc, argv)) {
  290. return -1;
  291. }
  292. suite.Run();
  293. return 0;
  294. }