fix a multiline comment warning
[folly.git] / folly / test / ConcurrentSkipListBenchmark.cpp
1 /*
2  * Copyright 2011-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 // @author: Xin Liu <xliux@fb.com>
17
18 #include <map>
19 #include <memory>
20 #include <random>
21 #include <set>
22 #include <thread>
23
24 #include <folly/Benchmark.h>
25 #include <folly/ConcurrentSkipList.h>
26 #include <folly/hash/Hash.h>
27 #include <folly/portability/GFlags.h>
28 #include <folly/synchronization/RWSpinLock.h>
29 #include <glog/logging.h>
30
31 DEFINE_int32(num_threads, 12, "num concurrent threads to test");
32
33 // In some case, we may want to test worker threads operating on multiple
34 // lists. For example in search, not all threads are visiting the same posting
35 // list, but for the ones with some popular terms, they do get multiple
36 // visitors at the same time.
37 DEFINE_int32(num_sets, 1, "num of set to operate on");
38
39 static const int kInitHeadHeight = 10;
40 static const int kMaxValue = 0x1000000;
41
42 namespace {
43
44 using namespace folly;
45
46 typedef int ValueType;
47 typedef ConcurrentSkipList<ValueType> SkipListType;
48 typedef SkipListType::Accessor SkipListAccessor;
49 typedef std::set<ValueType> SetType;
50
51 static std::vector<ValueType> gData;
52 static void initData() {
53   gData.resize(kMaxValue);
54   for (int i = 0; i < kMaxValue; ++i) {
55     gData[i] = i;
56   }
57   std::shuffle(gData.begin(), gData.end(), std::mt19937{});
58 }
59
60 // single thread benchmarks
61 void BM_IterateOverSet(int iters, int size) {
62   SetType a_set;
63
64   BENCHMARK_SUSPEND {
65     CHECK_GT(size, 0);
66     for (int i = 0; i < size; ++i) {
67       a_set.insert(gData[rand() % kMaxValue]);
68     }
69   }
70
71   int64_t sum = 0;
72   auto iter = a_set.begin();
73   for (int i = 0; i < iters; ++i) {
74     sum += *iter++;
75     if (iter == a_set.end()) {
76       iter = a_set.begin();
77     }
78   }
79   BENCHMARK_SUSPEND {
80     // VLOG(20) << "sum = " << sum;
81   }
82 }
83
84 void BM_IterateSkipList(int iters, int size) {
85   BenchmarkSuspender susp;
86   CHECK_GT(size, 0);
87   auto skipList = SkipListType::create(kInitHeadHeight);
88   for (int i = 0; i < size; ++i) {
89     skipList.add(rand() % kMaxValue);
90   }
91   int64_t sum = 0;
92   susp.dismiss();
93
94   auto iter = skipList.begin();
95   for (int i = 0; i < iters; ++i) {
96     sum += *iter++;
97     if (iter == skipList.end()) {
98       iter = skipList.begin();
99     }
100   }
101
102   BENCHMARK_SUSPEND {
103     // VLOG(20) << "sum = " << sum;
104   }
105 }
106
107 void BM_SetMerge(int iters, int size) {
108   BenchmarkSuspender susp;
109   SetType a_set;
110   SetType b_set;
111   for (int i = 0; i < iters; ++i) {
112     a_set.insert(rand() % kMaxValue);
113   }
114   for (int i = 0; i < size; ++i) {
115     b_set.insert(rand() % kMaxValue);
116   }
117   susp.dismiss();
118
119   int64_t mergedSum = 0;
120   FOR_EACH(it, a_set) {
121     if (b_set.find(*it) != b_set.end()) {
122       mergedSum += *it;
123     }
124   }
125   BENCHMARK_SUSPEND {
126     // VLOG(20) << mergedSum;
127   }
128 }
129
130 void BM_CSLMergeLookup(int iters, int size) {
131   BenchmarkSuspender susp;
132   auto skipList = SkipListType::create(kInitHeadHeight);
133   auto skipList2 = SkipListType::create(kInitHeadHeight);
134
135   for (int i = 0; i < iters; ++i) {
136     skipList.add(rand() % kMaxValue);
137   }
138   for (int i = 0; i < size; ++i) {
139     skipList2.add(rand() % kMaxValue);
140   }
141   int64_t mergedSum = 0;
142   susp.dismiss();
143
144   SkipListType::Skipper skipper(skipList2);
145   FOR_EACH(it, skipList) {
146     if (skipper.to(*it)) {
147       mergedSum += *it;
148     }
149   }
150
151   BENCHMARK_SUSPEND {
152     // VLOG(20) << mergedSum;
153   }
154 }
155
156 // merge by two skippers
157 void BM_CSLMergeIntersection(int iters, int size) {
158   BenchmarkSuspender susp;
159   auto skipList = SkipListType::create(kInitHeadHeight);
160   auto skipList2 = SkipListType::create(kInitHeadHeight);
161   for (int i = 0; i < iters; ++i) {
162     skipList.add(rand() % kMaxValue);
163   }
164   for (int i = 0; i < size; ++i) {
165     skipList2.add(rand() % kMaxValue);
166   }
167   susp.dismiss();
168
169   SkipListType::Skipper s1(skipList);
170   SkipListType::Skipper s2(skipList2);
171
172   int64_t mergedSum = 0;
173
174   while (s1.good() && s2.good()) {
175     int v1 = s1.data();
176     int v2 = s2.data();
177     if (v1 < v2) {
178       s1.to(v2);
179     } else if (v1 > v2) {
180       s2.to(v1);
181     } else {
182       mergedSum += v1;
183       ++s1;
184       ++s2;
185     }
186   }
187
188   BENCHMARK_SUSPEND {
189     // VLOG(20) << mergedSum;
190   }
191 }
192
193 void BM_SetContainsNotFound(int iters, int size) {
194   BenchmarkSuspender susp;
195   SetType aset;
196   CHECK_LT(size, kMaxValue);
197   for (int i = 0; i < size; ++i) {
198     aset.insert(2 * i);
199   }
200   int64_t sum = 0;
201   susp.dismiss();
202
203   for (int i = 0; i < iters; ++i) {
204     sum += (aset.end() == aset.find(2 * i + 1));
205   }
206
207   BENCHMARK_SUSPEND {
208     // VLOG(20) << sum;
209   }
210 }
211
212 void BM_SetContainsFound(int iters, int size) {
213   BenchmarkSuspender susp;
214   SetType aset;
215   CHECK_LT(size, kMaxValue);
216
217   for (int i = 0; i < size; ++i) {
218     aset.insert(i);
219   }
220
221   std::vector<int> values;
222   for (int i = 0; i < iters; ++i) {
223     values.push_back(rand() % size);
224   }
225   int64_t sum = 0;
226   susp.dismiss();
227
228   for (int i = 0; i < iters; ++i) {
229     sum += (aset.end() == aset.find(values[i]));
230   }
231
232   BENCHMARK_SUSPEND {
233     // VLOG(20) << sum;
234   }
235 }
236
237 void BM_CSLContainsFound(int iters, int size) {
238   BenchmarkSuspender susp;
239   auto skipList = SkipListType::create(kInitHeadHeight);
240   CHECK_LT(size, kMaxValue);
241
242   for (int i = 0; i < size; ++i) {
243     skipList.add(i);
244   }
245   std::vector<int> values;
246   for (int i = 0; i < iters; ++i) {
247     values.push_back(rand() % size);
248   }
249   int64_t sum = 0;
250   susp.dismiss();
251
252   for (int i = 0; i < iters; ++i) {
253     sum += skipList.contains(values[i]);
254   }
255
256   BENCHMARK_SUSPEND {
257     // VLOG(20) << sum;
258   }
259 }
260
261 void BM_CSLContainsNotFound(int iters, int size) {
262   BenchmarkSuspender susp;
263   auto skipList = SkipListType::create(kInitHeadHeight);
264   CHECK_LT(size, kMaxValue);
265
266   for (int i = 0; i < size; ++i) {
267     skipList.add(2 * i);
268   }
269   int64_t sum = 0;
270   susp.dismiss();
271
272   for (int i = 0; i < iters; ++i) {
273     sum += skipList.contains(2 * i + 1);
274   }
275
276   BENCHMARK_SUSPEND {
277     // VLOG(20) << sum;
278   }
279 }
280
281 void BM_AddSet(int iters, int size) {
282   BenchmarkSuspender susp;
283   SetType aset;
284   for (int i = 0; i < size; ++i) {
285     aset.insert(gData[i]);
286   }
287   susp.dismiss();
288
289   for (int i = size; i < size + iters; ++i) {
290     aset.insert(gData[i]);
291   }
292 }
293
294 void BM_AddSkipList(int iters, int size) {
295   BenchmarkSuspender susp;
296   auto skipList = SkipListType::create(kInitHeadHeight);
297   for (int i = 0; i < size; ++i) {
298     skipList.add(gData[i]);
299   }
300   susp.dismiss();
301
302   for (int i = size; i < size + iters; ++i) {
303     skipList.add(gData[i]);
304   }
305 }
306
307 BENCHMARK(Accessor, iters) {
308   BenchmarkSuspender susp;
309   auto skiplist = SkipListType::createInstance(kInitHeadHeight);
310   auto sl = skiplist.get();
311
312   susp.dismiss();
313   for (size_t i = 0; i < iters; ++i) {
314     SkipListAccessor accessor(sl);
315   }
316 }
317
318 // a benchmark to estimate the
319 // low bound of doing a ref counting for an Accessor
320 BENCHMARK(accessorBasicRefcounting, iters) {
321   BenchmarkSuspender susp;
322   auto* value = new std::atomic<int32_t>();
323   auto* dirty = new std::atomic<int32_t>();
324   *value = *dirty = 0;
325   folly::MicroSpinLock l;
326   l.init();
327
328   susp.dismiss();
329   for (size_t i = 0; i < iters; ++i) {
330     value->fetch_add(1, std::memory_order_relaxed);
331     if (dirty->load(std::memory_order_acquire) != 0) {
332       folly::MSLGuard g(l);
333     }
334     value->fetch_sub(1, std::memory_order_relaxed);
335   }
336
337   BENCHMARK_SUSPEND {
338     delete dirty;
339     delete value;
340   }
341 }
342
343
344 // Data For testing contention benchmark
345 class ConcurrentAccessData {
346  public:
347   explicit ConcurrentAccessData(int size) :
348     skipList_(SkipListType::create(10)),
349     sets_(FLAGS_num_sets), locks_(FLAGS_num_sets) {
350
351     for (int i = 0; i < size; ++i) {
352       sets_[0].insert(i);
353       skipList_.add(i);
354     }
355
356     for (int i = 0; i < FLAGS_num_sets; ++i) {
357       locks_[i] = new RWSpinLock();
358       if (i > 0) {
359         sets_[i] = sets_[0];
360       }
361     }
362
363 // This requires knowledge of the C++ library internals. Only use it if we're
364 // using the GNU C++ library.
365 #ifdef _GLIBCXX_SYMVER
366     // memory usage
367     int64_t setMemorySize = sets_[0].size() * sizeof(*sets_[0].begin()._M_node);
368     int64_t cslMemorySize = 0;
369     for (auto it = skipList_.begin(); it != skipList_.end(); ++it) {
370       cslMemorySize += it.nodeSize();
371     }
372
373     LOG(INFO) << "size=" << sets_[0].size()
374       << "; std::set memory size=" << setMemorySize
375       << "; csl memory size=" << cslMemorySize;
376 #endif
377
378     readValues_.reserve(size);
379     deleteValues_.reserve(size);
380     writeValues_.reserve(size);
381     for (int i = size; i < 2 * size; ++i) {
382       readValues_.push_back(2 * i);
383       deleteValues_.push_back(2 * i);
384
385       // half new values and half already in the list
386       writeValues_.push_back((rand() % 2) + 2 * i);
387     }
388     std::shuffle(readValues_.begin(), readValues_.end(), std::mt19937{});
389     std::shuffle(deleteValues_.begin(), deleteValues_.end(), std::mt19937{});
390     std::shuffle(writeValues_.begin(), writeValues_.end(), std::mt19937{});
391   }
392
393   ~ConcurrentAccessData() {
394     FOR_EACH(lock, locks_) delete *lock;
395   }
396
397   inline bool skipListFind(int /* idx */, ValueType val) {
398     return skipList_.contains(val);
399   }
400   inline void skipListInsert(int /* idx */, ValueType val) {
401     skipList_.add(val);
402   }
403   inline void skipListErase(int /* idx */, ValueType val) {
404     skipList_.remove(val);
405   }
406
407   inline bool setFind(int idx, ValueType val) {
408     RWSpinLock::ReadHolder g(locks_[idx]);
409     return sets_[idx].find(val) == sets_[idx].end();
410   }
411   inline void setInsert(int idx, ValueType val) {
412     RWSpinLock::WriteHolder g(locks_[idx]);
413     sets_[idx].insert(val);
414   }
415   inline void setErase(int idx, ValueType val) {
416     RWSpinLock::WriteHolder g(locks_[idx]);
417     sets_[idx].erase(val);
418   }
419
420   void runSkipList(int id, size_t iters) {
421     int sum = 0;
422     for (size_t i = 0; i < iters; ++i) {
423       sum += accessSkipList(id, i);
424     }
425     // VLOG(20) << sum;
426   }
427
428   void runSet(size_t id, size_t iters) {
429     int sum = 0;
430     for (size_t i = 0; i < iters; ++i) {
431       sum += accessSet(id, i);
432     }
433     // VLOG(20) << sum;
434   }
435
436   bool accessSkipList(int64_t id, size_t t) {
437     if (t > readValues_.size()) {
438       t = t % readValues_.size();
439     }
440     uint32_t h = folly::hash::twang_32from64(t * id);
441     switch (h % 8) {
442       case 7:   // write
443         if ((h & 0x31) == 0) { // 1/4 chance to delete
444           skipListErase(0, deleteValues_[t]);
445         } else {
446           skipListInsert(0, writeValues_[t]);
447         }
448         return false;
449       default:
450         return skipListFind(0, readValues_[t]);
451     }
452   }
453
454   bool accessSet(int64_t id, size_t t) {
455     if (t > readValues_.size()) {
456       t = t % readValues_.size();
457     }
458     uint32_t h = folly::hash::twang_32from64(t * id);
459     int idx = (h % FLAGS_num_sets);
460     switch (h % 8) {  // 1/8 chance to write
461       case 7:   // write
462         if ((h & 0x31) == 0) { // 1/32 chance to delete
463           setErase(idx, deleteValues_[t]);
464         } else {
465           setInsert(idx, writeValues_[t]);
466         }
467         return false;
468       default:
469         return setFind(idx, readValues_[t]);
470     }
471   }
472
473  private:
474   SkipListType::Accessor skipList_;
475   std::vector<SetType> sets_;
476   std::vector<RWSpinLock*> locks_;
477
478   std::vector<ValueType> readValues_;
479   std::vector<ValueType> writeValues_;
480   std::vector<ValueType> deleteValues_;
481 };
482
483 static std::map<int, std::shared_ptr<ConcurrentAccessData> > g_data;
484
485 static ConcurrentAccessData *mayInitTestData(int size) {
486   auto it = g_data.find(size);
487   if (it == g_data.end()) {
488     auto ptr = std::make_shared<ConcurrentAccessData>(size);
489     g_data[size] = ptr;
490     return ptr.get();
491   }
492   return it->second.get();
493 }
494
495 void BM_ContentionCSL(int iters, int size) {
496   BenchmarkSuspender susp;
497   auto data = mayInitTestData(size);
498   std::vector<std::thread> threads;
499   susp.dismiss();
500
501   for (int i = 0; i < FLAGS_num_threads; ++i) {
502     threads.push_back(std::thread(
503           &ConcurrentAccessData::runSkipList, data, i, iters));
504   }
505   FOR_EACH(t, threads) {
506     (*t).join();
507   }
508 }
509
510 void BM_ContentionStdSet(int iters, int size) {
511   BenchmarkSuspender susp;
512   auto data = mayInitTestData(size);
513   std::vector<std::thread> threads;
514   susp.dismiss();
515
516   for (int i = 0; i < FLAGS_num_threads; ++i) {
517     threads.push_back(std::thread(
518           &ConcurrentAccessData::runSet, data, i, iters));
519   }
520   FOR_EACH(t, threads) {
521     (*t).join();
522   }
523   susp.rehire();
524 }
525
526
527 // Single-thread benchmarking
528
529 BENCHMARK_DRAW_LINE();
530
531 BENCHMARK_PARAM(BM_IterateOverSet,  1000);
532 BENCHMARK_PARAM(BM_IterateSkipList, 1000);
533 BENCHMARK_DRAW_LINE();
534 BENCHMARK_PARAM(BM_IterateOverSet,  1000000);
535 BENCHMARK_PARAM(BM_IterateSkipList, 1000000);
536 BENCHMARK_DRAW_LINE();
537
538 // find with keys in the set
539 BENCHMARK_PARAM(BM_SetContainsFound, 1000);
540 BENCHMARK_PARAM(BM_CSLContainsFound, 1000);
541 BENCHMARK_DRAW_LINE();
542 BENCHMARK_PARAM(BM_SetContainsFound, 100000);
543 BENCHMARK_PARAM(BM_CSLContainsFound, 100000);
544 BENCHMARK_DRAW_LINE();
545 BENCHMARK_PARAM(BM_SetContainsFound, 1000000);
546 BENCHMARK_PARAM(BM_CSLContainsFound, 1000000);
547 BENCHMARK_DRAW_LINE();
548 BENCHMARK_PARAM(BM_SetContainsFound, 10000000);
549 BENCHMARK_PARAM(BM_CSLContainsFound, 10000000);
550 BENCHMARK_DRAW_LINE();
551
552
553 // find with keys not in the set
554 BENCHMARK_PARAM(BM_SetContainsNotFound, 1000);
555 BENCHMARK_PARAM(BM_CSLContainsNotFound, 1000);
556 BENCHMARK_DRAW_LINE();
557 BENCHMARK_PARAM(BM_SetContainsNotFound, 100000);
558 BENCHMARK_PARAM(BM_CSLContainsNotFound, 100000);
559 BENCHMARK_DRAW_LINE();
560 BENCHMARK_PARAM(BM_SetContainsNotFound, 1000000);
561 BENCHMARK_PARAM(BM_CSLContainsNotFound, 1000000);
562 BENCHMARK_DRAW_LINE();
563
564
565 BENCHMARK_PARAM(BM_AddSet,      1000);
566 BENCHMARK_PARAM(BM_AddSkipList, 1000);
567 BENCHMARK_DRAW_LINE();
568
569 BENCHMARK_PARAM(BM_AddSet,      65536);
570 BENCHMARK_PARAM(BM_AddSkipList, 65536);
571 BENCHMARK_DRAW_LINE();
572
573 BENCHMARK_PARAM(BM_AddSet,      1000000);
574 BENCHMARK_PARAM(BM_AddSkipList, 1000000);
575 BENCHMARK_DRAW_LINE();
576
577 BENCHMARK_PARAM(BM_SetMerge,             1000);
578 BENCHMARK_PARAM(BM_CSLMergeIntersection, 1000);
579 BENCHMARK_PARAM(BM_CSLMergeLookup,       1000);
580 BENCHMARK_DRAW_LINE();
581
582 BENCHMARK_PARAM(BM_SetMerge,             65536);
583 BENCHMARK_PARAM(BM_CSLMergeIntersection, 65536);
584 BENCHMARK_PARAM(BM_CSLMergeLookup,       65536);
585 BENCHMARK_DRAW_LINE();
586
587 BENCHMARK_PARAM(BM_SetMerge,             1000000);
588 BENCHMARK_PARAM(BM_CSLMergeIntersection, 1000000);
589 BENCHMARK_PARAM(BM_CSLMergeLookup,       1000000);
590 BENCHMARK_DRAW_LINE();
591
592
593 // multithreaded benchmarking
594
595 BENCHMARK_PARAM(BM_ContentionStdSet, 1024);
596 BENCHMARK_PARAM(BM_ContentionCSL,    1024);
597 BENCHMARK_DRAW_LINE();
598
599 BENCHMARK_PARAM(BM_ContentionStdSet, 65536);
600 BENCHMARK_PARAM(BM_ContentionCSL,    65536);
601 BENCHMARK_DRAW_LINE();
602
603 BENCHMARK_PARAM(BM_ContentionStdSet, 1048576);
604 BENCHMARK_PARAM(BM_ContentionCSL,    1048576);
605 BENCHMARK_DRAW_LINE();
606
607 } // namespace
608
609 int main(int argc, char** argv) {
610   google::InitGoogleLogging(argv[0]);
611   gflags::ParseCommandLineFlags(&argc, &argv, true);
612
613   initData();
614   runBenchmarks();
615   return 0;
616 }
617
618 #if 0
619 /*
620 Benchmark on Intel(R) Xeon(R) CPU X5650 @2.67GHz
621
622 ==============================================================================
623 1 thread Benchmark                     Iters   Total t    t/iter iter/sec
624 ------------------------------------------------------------------------------
625  +37.0% BM_Accessor                    100000  1.958 ms  19.58 ns  48.71 M
626 *       BM_AccessorBasicRefcounting    100000  1.429 ms  14.29 ns  66.74 M
627 ------------------------------------------------------------------------------
628  + 603% BM_IterateOverSet/1000         100000  1.589 ms  15.89 ns  60.02 M
629 *       BM_IterateSkipList/1000        100000    226 us   2.26 ns    422 M
630 ------------------------------------------------------------------------------
631  + 107% BM_IterateOverSet/976.6k       100000  8.324 ms  83.24 ns  11.46 M
632 *       BM_IterateSkipList/976.6k      100000  4.016 ms  40.16 ns  23.75 M
633 ------------------------------------------------------------------------------
634 *       BM_SetContainsFound/1000       100000  7.082 ms  70.82 ns  13.47 M
635  +39.9% BM_CSLContainsFound/1000       100000  9.908 ms  99.08 ns  9.625 M
636 ------------------------------------------------------------------------------
637 *       BM_SetContainsFound/97.66k     100000   23.8 ms    238 ns  4.006 M
638  +5.97% BM_CSLContainsFound/97.66k     100000  25.23 ms  252.3 ns  3.781 M
639 ------------------------------------------------------------------------------
640  +33.6% BM_SetContainsFound/976.6k     100000   64.3 ms    643 ns  1.483 M
641 *       BM_CSLContainsFound/976.6k     100000  48.13 ms  481.3 ns  1.981 M
642 ------------------------------------------------------------------------------
643  +30.3% BM_SetContainsFound/9.537M     100000  115.1 ms  1.151 us  848.6 k
644 *       BM_CSLContainsFound/9.537M     100000  88.33 ms  883.3 ns   1.08 M
645 ------------------------------------------------------------------------------
646 *       BM_SetContainsNotFound/1000    100000  2.081 ms  20.81 ns  45.83 M
647  +76.2% BM_CSLContainsNotFound/1000    100000  3.667 ms  36.67 ns  26.01 M
648 ------------------------------------------------------------------------------
649 *       BM_SetContainsNotFound/97.66k  100000  6.049 ms  60.49 ns  15.77 M
650  +32.7% BM_CSLContainsNotFound/97.66k  100000  8.025 ms  80.25 ns  11.88 M
651 ------------------------------------------------------------------------------
652 *       BM_SetContainsNotFound/976.6k  100000  7.464 ms  74.64 ns  12.78 M
653  +12.8% BM_CSLContainsNotFound/976.6k  100000  8.417 ms  84.17 ns  11.33 M
654 ------------------------------------------------------------------------------
655 *       BM_AddSet/1000                 100000  29.26 ms  292.6 ns  3.259 M
656  +70.0% BM_AddSkipList/1000            100000  49.75 ms  497.5 ns  1.917 M
657 ------------------------------------------------------------------------------
658 *       BM_AddSet/64k                  100000  38.73 ms  387.3 ns  2.462 M
659  +55.7% BM_AddSkipList/64k             100000   60.3 ms    603 ns  1.581 M
660 ------------------------------------------------------------------------------
661 *       BM_AddSet/976.6k               100000  75.71 ms  757.1 ns   1.26 M
662  +33.6% BM_AddSkipList/976.6k          100000  101.2 ms  1.012 us  965.3 k
663 ------------------------------------------------------------------------------
664  + 716% BM_SetMerge/1000               100000  6.872 ms  68.72 ns  13.88 M
665 *       BM_CSLMergeIntersection/1000   100000    842 us   8.42 ns  113.3 M
666  + 268% BM_CSLMergeLookup/1000         100000    3.1 ms     31 ns  30.76 M
667 ------------------------------------------------------------------------------
668  +36.3% BM_SetMerge/64k                100000  14.03 ms  140.3 ns  6.798 M
669  +39.4% BM_CSLMergeIntersection/64k    100000  14.35 ms  143.5 ns  6.645 M
670 *       BM_CSLMergeLookup/64k          100000  10.29 ms  102.9 ns  9.266 M
671 ------------------------------------------------------------------------------
672  +10.3% BM_SetMerge/976.6k             100000  46.24 ms  462.4 ns  2.062 M
673  +25.1% BM_CSLMergeIntersection/976.6k 100000  52.47 ms  524.7 ns  1.818 M
674 *       BM_CSLMergeLookup/976.6k       100000  41.94 ms  419.3 ns  2.274 M
675 ------------------------------------------------------------------------------
676
677
678 ==============================================================================
679 Contention benchmark 7/8 find, 3/32 insert, 1/32 erase
680
681  4 threads Benchmark                   Iters   Total t    t/iter iter/sec
682 ------------------------------------------------------------------------------
683  + 269% BM_ContentionStdSet/1k         100000  75.66 ms  756.6 ns   1.26 M
684 *       BM_ContentionCSL/1k            100000  20.47 ms  204.7 ns  4.658 M
685 ------------------------------------------------------------------------------
686  + 228% BM_ContentionStdSet/64k        100000  105.6 ms  1.056 us  924.9 k
687 *       BM_ContentionCSL/64k           100000  32.18 ms  321.8 ns  2.963 M
688 ------------------------------------------------------------------------------
689  + 224% BM_ContentionStdSet/1M         100000  117.4 ms  1.174 us  832.2 k
690 *       BM_ContentionCSL/1M            100000  36.18 ms  361.8 ns  2.636 M
691 ------------------------------------------------------------------------------
692
693
694 12 threads Benchmark                   Iters   Total t    t/iter iter/sec
695 ------------------------------------------------------------------------------
696  + 697% BM_ContentionStdSet/1k         100000  455.3 ms  4.553 us  214.5 k
697 *       BM_ContentionCSL/1k            100000  57.12 ms  571.2 ns   1.67 M
698 ------------------------------------------------------------------------------
699  +1257% BM_ContentionStdSet/64k        100000  654.9 ms  6.549 us  149.1 k
700 *       BM_ContentionCSL/64k           100000  48.24 ms  482.4 ns  1.977 M
701 ------------------------------------------------------------------------------
702  +1262% BM_ContentionStdSet/1M         100000  657.3 ms  6.573 us  148.6 k
703 *       BM_ContentionCSL/1M            100000  48.25 ms  482.5 ns  1.977 M
704 ------------------------------------------------------------------------------
705
706 */
707 #endif