|  | 
 | #include <algorithm> | 
 | #include <cstdint> | 
 | #include <map> | 
 | #include <random> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "CartesianBenchmarks.hpp" | 
 | #include "GenerateInput.hpp" | 
 | #include "benchmark/benchmark.h" | 
 | #include "test_macros.h" | 
 |  | 
 | namespace { | 
 |  | 
 | enum class ValueType { Uint32, String }; | 
 | struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 2> { | 
 |   static constexpr const char* Names[] = {"uint32", "string"}; | 
 | }; | 
 |  | 
 | template <class V> | 
 | using Value = | 
 |     std::conditional_t<V() == ValueType::Uint32, uint32_t, std::string>; | 
 |  | 
 | enum class Order { | 
 |   Random, | 
 |   Ascending, | 
 |   Descending, | 
 |   SingleElement, | 
 |   PipeOrgan, | 
 |   Heap | 
 | }; | 
 | struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 6> { | 
 |   static constexpr const char* Names[] = {"Random",     "Ascending", | 
 |                                           "Descending", "SingleElement", | 
 |                                           "PipeOrgan",  "Heap"}; | 
 | }; | 
 |  | 
 | void fillValues(std::vector<uint32_t>& 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<std::string>& 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 <class T> | 
 | 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 <class ValueType> | 
 | std::vector<std::vector<Value<ValueType> > > 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::pair<size_t, Order>, std::vector<Value<ValueType> > > | 
 |       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 <class T, class U> | 
 | TEST_ALWAYS_INLINE void resetCopies(benchmark::State& state, T& Copies, | 
 |                                     U& Orig) { | 
 |   state.PauseTiming(); | 
 |   for (auto& Copy : Copies) | 
 |     Copy = Orig; | 
 |   state.ResumeTiming(); | 
 | } | 
 |  | 
 | template <class ValueType, class F> | 
 | void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O, | 
 |                    bool CountElements, F f) { | 
 |   auto Copies = makeOrderedValues<ValueType>(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 <class ValueType, class Order> | 
 | struct Sort { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>(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 <class ValueType, class Order> | 
 | struct StableSort { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>(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 <class ValueType, class Order> | 
 | struct MakeHeap { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>(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 <class ValueType> | 
 | struct SortHeap { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>( | 
 |         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 <class ValueType, class Order> | 
 | struct MakeThenSortHeap { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>(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 <class ValueType, class Order> | 
 | struct PushHeap { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>(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 <class ValueType> | 
 | struct PopHeap { | 
 |   size_t Quantity; | 
 |  | 
 |   void run(benchmark::State& state) const { | 
 |     runOpOnCopies<ValueType>(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<size_t> Quantities = {1 << 0, 1 << 2,  1 << 4,  1 << 6, | 
 |                                           1 << 8, 1 << 10, 1 << 14, 1 << 18}; | 
 |   makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities); | 
 |   makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>( | 
 |       Quantities); | 
 |   makeCartesianProductBenchmark<MakeHeap, AllValueTypes, AllOrders>(Quantities); | 
 |   makeCartesianProductBenchmark<SortHeap, AllValueTypes>(Quantities); | 
 |   makeCartesianProductBenchmark<MakeThenSortHeap, AllValueTypes, AllOrders>( | 
 |       Quantities); | 
 |   makeCartesianProductBenchmark<PushHeap, AllValueTypes, AllOrders>(Quantities); | 
 |   makeCartesianProductBenchmark<PopHeap, AllValueTypes>(Quantities); | 
 |   benchmark::RunSpecifiedBenchmarks(); | 
 | } |