249 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <cstdint>
 | |
| #include <memory>
 | |
| #include <random>
 | |
| #include <set>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #include "CartesianBenchmarks.h"
 | |
| #include "benchmark/benchmark.h"
 | |
| #include "test_macros.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| enum class HitType { Hit, Miss };
 | |
| 
 | |
| struct AllHitTypes : EnumValuesAsTuple<AllHitTypes, HitType, 2> {
 | |
|   static constexpr const char* Names[] = {"Hit", "Miss"};
 | |
| };
 | |
| 
 | |
| enum class AccessPattern { Ordered, Random };
 | |
| 
 | |
| struct AllAccessPattern
 | |
|     : EnumValuesAsTuple<AllAccessPattern, AccessPattern, 2> {
 | |
|   static constexpr const char* Names[] = {"Ordered", "Random"};
 | |
| };
 | |
| 
 | |
| void sortKeysBy(std::vector<uint64_t>& Keys, AccessPattern AP) {
 | |
|   if (AP == AccessPattern::Random) {
 | |
|     std::random_device R;
 | |
|     std::mt19937 M(R());
 | |
|     std::shuffle(std::begin(Keys), std::end(Keys), M);
 | |
|   }
 | |
| }
 | |
| 
 | |
| struct TestSets {
 | |
|   std::vector<std::set<uint64_t> > Sets;
 | |
|   std::vector<uint64_t> Keys;
 | |
| };
 | |
| 
 | |
| TestSets makeTestingSets(size_t TableSize, size_t NumTables, HitType Hit,
 | |
|                          AccessPattern Access) {
 | |
|   TestSets R;
 | |
|   R.Sets.resize(1);
 | |
| 
 | |
|   for (uint64_t I = 0; I < TableSize; ++I) {
 | |
|     R.Sets[0].insert(2 * I);
 | |
|     R.Keys.push_back(Hit == HitType::Hit ? 2 * I : 2 * I + 1);
 | |
|   }
 | |
|   R.Sets.resize(NumTables, R.Sets[0]);
 | |
|   sortKeysBy(R.Keys, Access);
 | |
| 
 | |
|   return R;
 | |
| }
 | |
| 
 | |
| struct Base {
 | |
|   size_t TableSize;
 | |
|   size_t NumTables;
 | |
|   Base(size_t T, size_t N) : TableSize(T), NumTables(N) {}
 | |
| 
 | |
|   bool skip() const {
 | |
|     size_t Total = TableSize * NumTables;
 | |
|     return Total < 100 || Total > 1000000;
 | |
|   }
 | |
| 
 | |
|   std::string baseName() const {
 | |
|     return "_TableSize" + std::to_string(TableSize) + "_NumTables" +
 | |
|            std::to_string(NumTables);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class Access>
 | |
| struct Create : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     std::vector<uint64_t> Keys(TableSize);
 | |
|     std::iota(Keys.begin(), Keys.end(), uint64_t{0});
 | |
|     sortKeysBy(Keys, Access());
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       std::vector<std::set<uint64_t>> Sets(NumTables);
 | |
|       for (auto K : Keys) {
 | |
|         for (auto& Set : Sets) {
 | |
|           benchmark::DoNotOptimize(Set.insert(K));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const {
 | |
|     return "BM_Create" + Access::name() + baseName();
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class Hit, class Access>
 | |
| struct Find : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     auto Data = makeTestingSets(TableSize, NumTables, Hit(), Access());
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       for (auto K : Data.Keys) {
 | |
|         for (auto& Set : Data.Sets) {
 | |
|           benchmark::DoNotOptimize(Set.find(K));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const {
 | |
|     return "BM_Find" + Hit::name() + Access::name() + baseName();
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class Hit, class Access>
 | |
| struct FindNeEnd : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     auto Data = makeTestingSets(TableSize, NumTables, Hit(), Access());
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       for (auto K : Data.Keys) {
 | |
|         for (auto& Set : Data.Sets) {
 | |
|           benchmark::DoNotOptimize(Set.find(K) != Set.end());
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const {
 | |
|     return "BM_FindNeEnd" + Hit::name() + Access::name() + baseName();
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class Access>
 | |
| struct InsertHit : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     auto Data = makeTestingSets(TableSize, NumTables, HitType::Hit, Access());
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       for (auto K : Data.Keys) {
 | |
|         for (auto& Set : Data.Sets) {
 | |
|           benchmark::DoNotOptimize(Set.insert(K));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const {
 | |
|     return "BM_InsertHit" + Access::name() + baseName();
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class Access>
 | |
| struct InsertMissAndErase : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     auto Data = makeTestingSets(TableSize, NumTables, HitType::Miss, Access());
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       for (auto K : Data.Keys) {
 | |
|         for (auto& Set : Data.Sets) {
 | |
|           benchmark::DoNotOptimize(Set.erase(Set.insert(K).first));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const {
 | |
|     return "BM_InsertMissAndErase" + Access::name() + baseName();
 | |
|   }
 | |
| };
 | |
| 
 | |
| struct IterateRangeFor : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     auto Data = makeTestingSets(TableSize, NumTables, HitType::Miss,
 | |
|                                 AccessPattern::Ordered);
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       for (auto& Set : Data.Sets) {
 | |
|         for (auto& V : Set) {
 | |
|           benchmark::DoNotOptimize(V);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const { return "BM_IterateRangeFor" + baseName(); }
 | |
| };
 | |
| 
 | |
| struct IterateBeginEnd : Base {
 | |
|   using Base::Base;
 | |
| 
 | |
|   void run(benchmark::State& State) const {
 | |
|     auto Data = makeTestingSets(TableSize, NumTables, HitType::Miss,
 | |
|                                 AccessPattern::Ordered);
 | |
| 
 | |
|     while (State.KeepRunningBatch(TableSize * NumTables)) {
 | |
|       for (auto& Set : Data.Sets) {
 | |
|         for (auto it = Set.begin(); it != Set.end(); ++it) {
 | |
|           benchmark::DoNotOptimize(*it);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string name() const { return "BM_IterateBeginEnd" + baseName(); }
 | |
| };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| int main(int argc, char** argv) {
 | |
|   benchmark::Initialize(&argc, argv);
 | |
|   if (benchmark::ReportUnrecognizedArguments(argc, argv))
 | |
|     return 1;
 | |
| 
 | |
|   const std::vector<size_t> TableSize{1, 10, 100, 1000, 10000, 100000, 1000000};
 | |
|   const std::vector<size_t> NumTables{1, 10, 100, 1000, 10000, 100000, 1000000};
 | |
| 
 | |
|   makeCartesianProductBenchmark<Create, AllAccessPattern>(TableSize, NumTables);
 | |
|   makeCartesianProductBenchmark<Find, AllHitTypes, AllAccessPattern>(
 | |
|       TableSize, NumTables);
 | |
|   makeCartesianProductBenchmark<FindNeEnd, AllHitTypes, AllAccessPattern>(
 | |
|       TableSize, NumTables);
 | |
|   makeCartesianProductBenchmark<InsertHit, AllAccessPattern>(
 | |
|       TableSize, NumTables);
 | |
|   makeCartesianProductBenchmark<InsertMissAndErase, AllAccessPattern>(
 | |
|       TableSize, NumTables);
 | |
|   makeCartesianProductBenchmark<IterateRangeFor>(TableSize, NumTables);
 | |
|   makeCartesianProductBenchmark<IterateBeginEnd>(TableSize, NumTables);
 | |
|   benchmark::RunSpecifiedBenchmarks();
 | |
| }
 |