algorithms.bench.cpp 7.5 KB

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