| //===----------------------------------------------------------------------===// |
| // |
| // 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(); |
| } |