#include #include #include #include #include #include #include #include "CartesianBenchmarks.hpp" #include "GenerateInput.hpp" #include "benchmark/benchmark.h" #include "test_macros.h" namespace { enum class ValueType { Uint32, String }; struct AllValueTypes : EnumValuesAsTuple { static constexpr const char* Names[] = {"uint32", "string"}; }; template using Value = std::conditional_t; enum class Order { Random, Ascending, Descending, SingleElement, PipeOrgan, Heap }; struct AllOrders : EnumValuesAsTuple { static constexpr const char* Names[] = {"Random", "Ascending", "Descending", "SingleElement", "PipeOrgan", "Heap"}; }; void fillValues(std::vector& V, size_t N, Order O) { if (O == Order::SingleElement) { V.resize(N, 0); } else { while (V.size() < N) V.push_back(V.size()); } } void fillValues(std::vector& V, size_t N, Order O) { if (O == Order::SingleElement) { V.resize(N, getRandomString(1024)); } else { while (V.size() < N) V.push_back(getRandomString(1024)); } } template void sortValues(T& V, Order O) { assert(std::is_sorted(V.begin(), V.end())); switch (O) { case Order::Random: { std::random_device R; std::mt19937 M(R()); std::shuffle(V.begin(), V.end(), M); break; } case Order::Ascending: std::sort(V.begin(), V.end()); break; case Order::Descending: std::sort(V.begin(), V.end(), std::greater<>()); break; case Order::SingleElement: // Nothing to do break; case Order::PipeOrgan: std::sort(V.begin(), V.end()); std::reverse(V.begin() + V.size() / 2, V.end()); break; case Order::Heap: std::make_heap(V.begin(), V.end()); break; } } template std::vector > > makeOrderedValues(size_t N, Order O) { // Let's make sure that all random sequences of the same size are the same. // That way we can compare the different algorithms with the same input. static std::map, std::vector > > Cached; auto& Values = Cached[{N, O}]; if (Values.empty()) { fillValues(Values, N, O); sortValues(Values, O); }; const size_t NumCopies = std::max(size_t{1}, 1000 / N); return { NumCopies, Values }; } template TEST_ALWAYS_INLINE void resetCopies(benchmark::State& state, T& Copies, U& Orig) { state.PauseTiming(); for (auto& Copy : Copies) Copy = Orig; state.ResumeTiming(); } template void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O, bool CountElements, F f) { auto Copies = makeOrderedValues(Quantity, O); const auto Orig = Copies[0]; const size_t Batch = CountElements ? Copies.size() * Quantity : Copies.size(); while (state.KeepRunningBatch(Batch)) { for (auto& Copy : Copies) { f(Copy); benchmark::DoNotOptimize(Copy); } resetCopies(state, Copies, Orig); } } template struct Sort { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { std::sort(Copy.begin(), Copy.end()); }); } bool skip() const { return Order() == ::Order::Heap; } std::string name() const { return "BM_Sort" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity); }; }; template struct StableSort { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { std::stable_sort(Copy.begin(), Copy.end()); }); } bool skip() const { return Order() == ::Order::Heap; } std::string name() const { return "BM_StableSort" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity); }; }; template struct MakeHeap { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { std::make_heap(Copy.begin(), Copy.end()); }); } std::string name() const { return "BM_MakeHeap" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity); }; }; template struct SortHeap { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies( state, Quantity, Order::Heap, false, [](auto& Copy) { std::sort_heap(Copy.begin(), Copy.end()); }); } std::string name() const { return "BM_SortHeap" + ValueType::name() + "_" + std::to_string(Quantity); }; }; template struct MakeThenSortHeap { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { std::make_heap(Copy.begin(), Copy.end()); std::sort_heap(Copy.begin(), Copy.end()); }); } std::string name() const { return "BM_MakeThenSortHeap" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity); }; }; template struct PushHeap { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies(state, Quantity, Order(), true, [](auto& Copy) { for (auto I = Copy.begin(), E = Copy.end(); I != E; ++I) { std::push_heap(Copy.begin(), I + 1); } }); } bool skip() const { return Order() == ::Order::Heap; } std::string name() const { return "BM_PushHeap" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity); }; }; template struct PopHeap { size_t Quantity; void run(benchmark::State& state) const { runOpOnCopies(state, Quantity, Order(), true, [](auto& Copy) { for (auto B = Copy.begin(), I = Copy.end(); I != B; --I) { std::pop_heap(B, I); } }); } std::string name() const { return "BM_PopHeap" + ValueType::name() + "_" + std::to_string(Quantity); }; }; } // namespace int main(int argc, char** argv) { benchmark::Initialize(&argc, argv); if (benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; const std::vector Quantities = {1 << 0, 1 << 2, 1 << 4, 1 << 6, 1 << 8, 1 << 10, 1 << 14, 1 << 18}; makeCartesianProductBenchmark(Quantities); makeCartesianProductBenchmark( Quantities); makeCartesianProductBenchmark(Quantities); makeCartesianProductBenchmark(Quantities); makeCartesianProductBenchmark( Quantities); makeCartesianProductBenchmark(Quantities); makeCartesianProductBenchmark(Quantities); benchmark::RunSpecifiedBenchmarks(); }