Home | History | Annotate | Download | only in benchmarks
      1 
      2 #include <algorithm>
      3 #include <cstdint>
      4 #include <map>
      5 #include <random>
      6 #include <string>
      7 #include <utility>
      8 #include <vector>
      9 
     10 #include "CartesianBenchmarks.hpp"
     11 #include "GenerateInput.hpp"
     12 #include "benchmark/benchmark.h"
     13 #include "test_macros.h"
     14 
     15 namespace {
     16 
     17 enum class ValueType { Uint32, String };
     18 struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 2> {
     19   static constexpr const char* Names[] = {"uint32", "string"};
     20 };
     21 
     22 template <class V>
     23 using Value =
     24     std::conditional_t<V() == ValueType::Uint32, uint32_t, std::string>;
     25 
     26 enum class Order {
     27   Random,
     28   Ascending,
     29   Descending,
     30   SingleElement,
     31   PipeOrgan,
     32   Heap
     33 };
     34 struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 6> {
     35   static constexpr const char* Names[] = {"Random",     "Ascending",
     36                                           "Descending", "SingleElement",
     37                                           "PipeOrgan",  "Heap"};
     38 };
     39 
     40 void fillValues(std::vector<uint32_t>& V, size_t N, Order O) {
     41   if (O == Order::SingleElement) {
     42     V.resize(N, 0);
     43   } else {
     44     while (V.size() < N)
     45       V.push_back(V.size());
     46   }
     47 }
     48 
     49 void fillValues(std::vector<std::string>& V, size_t N, Order O) {
     50 
     51   if (O == Order::SingleElement) {
     52     V.resize(N, getRandomString(1024));
     53   } else {
     54     while (V.size() < N)
     55       V.push_back(getRandomString(1024));
     56   }
     57 }
     58 
     59 template <class T>
     60 void sortValues(T& V, Order O) {
     61   assert(std::is_sorted(V.begin(), V.end()));
     62   switch (O) {
     63   case Order::Random: {
     64     std::random_device R;
     65     std::mt19937 M(R());
     66     std::shuffle(V.begin(), V.end(), M);
     67     break;
     68   }
     69   case Order::Ascending:
     70     std::sort(V.begin(), V.end());
     71     break;
     72   case Order::Descending:
     73     std::sort(V.begin(), V.end(), std::greater<>());
     74     break;
     75   case Order::SingleElement:
     76     // Nothing to do
     77     break;
     78   case Order::PipeOrgan:
     79     std::sort(V.begin(), V.end());
     80     std::reverse(V.begin() + V.size() / 2, V.end());
     81     break;
     82   case Order::Heap:
     83     std::make_heap(V.begin(), V.end());
     84     break;
     85   }
     86 }
     87 
     88 template <class ValueType>
     89 std::vector<std::vector<Value<ValueType> > > makeOrderedValues(size_t N,
     90                                                                Order O) {
     91   // Let's make sure that all random sequences of the same size are the same.
     92   // That way we can compare the different algorithms with the same input.
     93   static std::map<std::pair<size_t, Order>, std::vector<Value<ValueType> > >
     94       Cached;
     95 
     96   auto& Values = Cached[{N, O}];
     97   if (Values.empty()) {
     98     fillValues(Values, N, O);
     99     sortValues(Values, O);
    100   };
    101   const size_t NumCopies = std::max(size_t{1}, 1000 / N);
    102   return { NumCopies, Values };
    103 }
    104 
    105 template <class T, class U>
    106 TEST_ALWAYS_INLINE void resetCopies(benchmark::State& state, T& Copies,
    107                                     U& Orig) {
    108   state.PauseTiming();
    109   for (auto& Copy : Copies)
    110     Copy = Orig;
    111   state.ResumeTiming();
    112 }
    113 
    114 template <class ValueType, class F>
    115 void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O,
    116                    bool CountElements, F f) {
    117   auto Copies = makeOrderedValues<ValueType>(Quantity, O);
    118   const auto Orig = Copies[0];
    119 
    120   const size_t Batch = CountElements ? Copies.size() * Quantity : Copies.size();
    121   while (state.KeepRunningBatch(Batch)) {
    122     for (auto& Copy : Copies) {
    123       f(Copy);
    124       benchmark::DoNotOptimize(Copy);
    125     }
    126     resetCopies(state, Copies, Orig);
    127   }
    128 }
    129 
    130 template <class ValueType, class Order>
    131 struct Sort {
    132   size_t Quantity;
    133 
    134   void run(benchmark::State& state) const {
    135     runOpOnCopies<ValueType>(state, Quantity, Order(), false, [](auto& Copy) {
    136       std::sort(Copy.begin(), Copy.end());
    137     });
    138   }
    139 
    140   bool skip() const { return Order() == ::Order::Heap; }
    141 
    142   std::string name() const {
    143     return "BM_Sort" + ValueType::name() + Order::name() + "_" +
    144            std::to_string(Quantity);
    145   };
    146 };
    147 
    148 template <class ValueType, class Order>
    149 struct StableSort {
    150   size_t Quantity;
    151 
    152   void run(benchmark::State& state) const {
    153     runOpOnCopies<ValueType>(state, Quantity, Order(), false, [](auto& Copy) {
    154       std::stable_sort(Copy.begin(), Copy.end());
    155     });
    156   }
    157 
    158   bool skip() const { return Order() == ::Order::Heap; }
    159 
    160   std::string name() const {
    161     return "BM_StableSort" + ValueType::name() + Order::name() + "_" +
    162            std::to_string(Quantity);
    163   };
    164 };
    165 
    166 template <class ValueType, class Order>
    167 struct MakeHeap {
    168   size_t Quantity;
    169 
    170   void run(benchmark::State& state) const {
    171     runOpOnCopies<ValueType>(state, Quantity, Order(), false, [](auto& Copy) {
    172       std::make_heap(Copy.begin(), Copy.end());
    173     });
    174   }
    175 
    176   std::string name() const {
    177     return "BM_MakeHeap" + ValueType::name() + Order::name() + "_" +
    178            std::to_string(Quantity);
    179   };
    180 };
    181 
    182 template <class ValueType>
    183 struct SortHeap {
    184   size_t Quantity;
    185 
    186   void run(benchmark::State& state) const {
    187     runOpOnCopies<ValueType>(
    188         state, Quantity, Order::Heap, false,
    189         [](auto& Copy) { std::sort_heap(Copy.begin(), Copy.end()); });
    190   }
    191 
    192   std::string name() const {
    193     return "BM_SortHeap" + ValueType::name() + "_" + std::to_string(Quantity);
    194   };
    195 };
    196 
    197 template <class ValueType, class Order>
    198 struct MakeThenSortHeap {
    199   size_t Quantity;
    200 
    201   void run(benchmark::State& state) const {
    202     runOpOnCopies<ValueType>(state, Quantity, Order(), false, [](auto& Copy) {
    203       std::make_heap(Copy.begin(), Copy.end());
    204       std::sort_heap(Copy.begin(), Copy.end());
    205     });
    206   }
    207 
    208   std::string name() const {
    209     return "BM_MakeThenSortHeap" + ValueType::name() + Order::name() + "_" +
    210            std::to_string(Quantity);
    211   };
    212 };
    213 
    214 template <class ValueType, class Order>
    215 struct PushHeap {
    216   size_t Quantity;
    217 
    218   void run(benchmark::State& state) const {
    219     runOpOnCopies<ValueType>(state, Quantity, Order(), true, [](auto& Copy) {
    220       for (auto I = Copy.begin(), E = Copy.end(); I != E; ++I) {
    221         std::push_heap(Copy.begin(), I + 1);
    222       }
    223     });
    224   }
    225 
    226   bool skip() const { return Order() == ::Order::Heap; }
    227 
    228   std::string name() const {
    229     return "BM_PushHeap" + ValueType::name() + Order::name() + "_" +
    230            std::to_string(Quantity);
    231   };
    232 };
    233 
    234 template <class ValueType>
    235 struct PopHeap {
    236   size_t Quantity;
    237 
    238   void run(benchmark::State& state) const {
    239     runOpOnCopies<ValueType>(state, Quantity, Order(), true, [](auto& Copy) {
    240       for (auto B = Copy.begin(), I = Copy.end(); I != B; --I) {
    241         std::pop_heap(B, I);
    242       }
    243     });
    244   }
    245 
    246   std::string name() const {
    247     return "BM_PopHeap" + ValueType::name() + "_" + std::to_string(Quantity);
    248   };
    249 };
    250 
    251 } // namespace
    252 
    253 int main(int argc, char** argv) {
    254   benchmark::Initialize(&argc, argv);
    255   if (benchmark::ReportUnrecognizedArguments(argc, argv))
    256     return 1;
    257 
    258   const std::vector<size_t> Quantities = {1 << 0, 1 << 2,  1 << 4,  1 << 6,
    259                                           1 << 8, 1 << 10, 1 << 14, 1 << 18};
    260   makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
    261   makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(
    262       Quantities);
    263   makeCartesianProductBenchmark<MakeHeap, AllValueTypes, AllOrders>(Quantities);
    264   makeCartesianProductBenchmark<SortHeap, AllValueTypes>(Quantities);
    265   makeCartesianProductBenchmark<MakeThenSortHeap, AllValueTypes, AllOrders>(
    266       Quantities);
    267   makeCartesianProductBenchmark<PushHeap, AllValueTypes, AllOrders>(Quantities);
    268   makeCartesianProductBenchmark<PopHeap, AllValueTypes>(Quantities);
    269   benchmark::RunSpecifiedBenchmarks();
    270 }
    271