blob: e5dd37f06e6e5f161a21b5403534df74246f125b [file] [log] [blame]
Samuel Benzaquen8e11d692018-11-20 17:15:17 +00001
2#include <algorithm>
Eric Fiselier9e3e1372016-07-19 23:07:03 +00003#include <cstdint>
Samuel Benzaquen8e11d692018-11-20 17:15:17 +00004#include <map>
5#include <random>
6#include <string>
7#include <utility>
8#include <vector>
Eric Fiselier9e3e1372016-07-19 23:07:03 +00009
Nico Weberfa647f82019-08-21 01:59:12 +000010#include "CartesianBenchmarks.h"
11#include "GenerateInput.h"
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000012#include "benchmark/benchmark.h"
13#include "test_macros.h"
Eric Fiselier9e3e1372016-07-19 23:07:03 +000014
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000015namespace {
Eric Fiselier9e3e1372016-07-19 23:07:03 +000016
MinJae Hwanga79586c2020-07-06 18:29:38 -040017enum class ValueType { Uint32, Uint64, Pair, Tuple, String };
18struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 5> {
19 static constexpr const char* Names[] = {
20 "uint32", "uint64", "pair<uint32, uint32>",
21 "tuple<uint32, uint64, uint32>", "string"};
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000022};
23
24template <class V>
MinJae Hwanga79586c2020-07-06 18:29:38 -040025using Value = std::conditional_t<
26 V() == ValueType::Uint32, uint32_t,
27 std::conditional_t<
28 V() == ValueType::Uint64, uint64_t,
29 std::conditional_t<
30 V() == ValueType::Pair, std::pair<uint32_t, uint32_t>,
31 std::conditional_t<V() == ValueType::Tuple,
32 std::tuple<uint32_t, uint64_t, uint32_t>,
33 std::string> > > >;
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000034
35enum class Order {
36 Random,
37 Ascending,
38 Descending,
39 SingleElement,
40 PipeOrgan,
Nilay Vaishada38722021-11-16 11:37:55 -050041 Heap,
42 QuickSortAdversary,
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000043};
Nilay Vaishada38722021-11-16 11:37:55 -050044struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 7> {
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000045 static constexpr const char* Names[] = {"Random", "Ascending",
46 "Descending", "SingleElement",
Nilay Vaishada38722021-11-16 11:37:55 -050047 "PipeOrgan", "Heap",
48 "QuickSortAdversary"};
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000049};
50
Nilay Vaishada38722021-11-16 11:37:55 -050051// fillAdversarialQuickSortInput fills the input vector with N int-like values.
52// These values are arranged in such a way that they would invoke O(N^2)
53// behavior on any quick sort implementation that satisifies certain conditions.
54// Details are available in the following paper:
55// "A Killer Adversary for Quicksort", M. D. McIlroy, Software—Practice &
56// ExperienceVolume 29 Issue 4 April 10, 1999 pp 341–344.
57// https://dl.acm.org/doi/10.5555/311868.311871.
58template <class T>
59void fillAdversarialQuickSortInput(T& V, size_t N) {
60 assert(N > 0);
61 // If an element is equal to gas, it indicates that the value of the element
62 // is still to be decided and may change over the course of time.
63 const int gas = N - 1;
64 V.resize(N);
65 for (int i = 0; i < N; ++i) {
66 V[i] = gas;
67 }
68 // Candidate for the pivot position.
69 int candidate = 0;
70 int nsolid = 0;
71 // Populate all positions in the generated input to gas.
72 std::vector<int> ascVals(V.size());
73 // Fill up with ascending values from 0 to V.size()-1. These will act as
74 // indices into V.
75 std::iota(ascVals.begin(), ascVals.end(), 0);
76 std::sort(ascVals.begin(), ascVals.end(), [&](int x, int y) {
77 if (V[x] == gas && V[y] == gas) {
78 // We are comparing two inputs whose value is still to be decided.
79 if (x == candidate) {
80 V[x] = nsolid++;
81 } else {
82 V[y] = nsolid++;
83 }
84 }
85 if (V[x] == gas) {
86 candidate = x;
87 } else if (V[y] == gas) {
88 candidate = y;
89 }
90 return V[x] < V[y];
91 });
92}
93
MinJae Hwanga79586c2020-07-06 18:29:38 -040094template <typename T>
95void fillValues(std::vector<T>& V, size_t N, Order O) {
Samuel Benzaquen8e11d692018-11-20 17:15:17 +000096 if (O == Order::SingleElement) {
97 V.resize(N, 0);
Nilay Vaishada38722021-11-16 11:37:55 -050098 } else if (O == Order::QuickSortAdversary) {
99 fillAdversarialQuickSortInput(V, N);
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000100 } else {
101 while (V.size() < N)
102 V.push_back(V.size());
103 }
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000104}
105
MinJae Hwanga79586c2020-07-06 18:29:38 -0400106template <typename T>
107void fillValues(std::vector<std::pair<T, T> >& V, size_t N, Order O) {
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000108 if (O == Order::SingleElement) {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400109 V.resize(N, std::make_pair(0, 0));
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000110 } else {
111 while (V.size() < N)
MinJae Hwanga79586c2020-07-06 18:29:38 -0400112 // Half of array will have the same first element.
113 if (V.size() % 2) {
114 V.push_back(std::make_pair(V.size(), V.size()));
115 } else {
116 V.push_back(std::make_pair(0, V.size()));
117 }
118 }
119}
120
121template <typename T1, typename T2, typename T3>
122void fillValues(std::vector<std::tuple<T1, T2, T3> >& V, size_t N, Order O) {
123 if (O == Order::SingleElement) {
124 V.resize(N, std::make_tuple(0, 0, 0));
125 } else {
126 while (V.size() < N)
127 // One third of array will have the same first element.
128 // One third of array will have the same first element and the same second element.
129 switch (V.size() % 3) {
130 case 0:
131 V.push_back(std::make_tuple(V.size(), V.size(), V.size()));
132 break;
133 case 1:
134 V.push_back(std::make_tuple(0, V.size(), V.size()));
135 break;
136 case 2:
137 V.push_back(std::make_tuple(0, 0, V.size()));
138 break;
139 }
140 }
141}
142
143void fillValues(std::vector<std::string>& V, size_t N, Order O) {
144 if (O == Order::SingleElement) {
145 V.resize(N, getRandomString(64));
146 } else {
147 while (V.size() < N)
148 V.push_back(getRandomString(64));
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000149 }
150}
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000151
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000152template <class T>
153void sortValues(T& V, Order O) {
154 assert(std::is_sorted(V.begin(), V.end()));
155 switch (O) {
156 case Order::Random: {
157 std::random_device R;
158 std::mt19937 M(R());
159 std::shuffle(V.begin(), V.end(), M);
160 break;
161 }
162 case Order::Ascending:
163 std::sort(V.begin(), V.end());
164 break;
165 case Order::Descending:
166 std::sort(V.begin(), V.end(), std::greater<>());
167 break;
168 case Order::SingleElement:
169 // Nothing to do
170 break;
171 case Order::PipeOrgan:
172 std::sort(V.begin(), V.end());
173 std::reverse(V.begin() + V.size() / 2, V.end());
174 break;
175 case Order::Heap:
176 std::make_heap(V.begin(), V.end());
177 break;
Nilay Vaishada38722021-11-16 11:37:55 -0500178 case Order::QuickSortAdversary:
179 // Nothing to do
180 break;
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000181 }
182}
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000183
MinJae Hwanga79586c2020-07-06 18:29:38 -0400184constexpr size_t TestSetElements =
185#if !TEST_HAS_FEATURE(memory_sanitizer)
186 1 << 18;
187#else
188 1 << 14;
189#endif
190
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000191template <class ValueType>
192std::vector<std::vector<Value<ValueType> > > makeOrderedValues(size_t N,
193 Order O) {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400194 std::vector<std::vector<Value<ValueType> > > Ret;
195 const size_t NumCopies = std::max(size_t{1}, TestSetElements / N);
196 Ret.resize(NumCopies);
197 for (auto& V : Ret) {
198 fillValues(V, N, O);
199 sortValues(V, O);
200 }
201 return Ret;
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000202}
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000203
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000204template <class T, class U>
205TEST_ALWAYS_INLINE void resetCopies(benchmark::State& state, T& Copies,
206 U& Orig) {
207 state.PauseTiming();
208 for (auto& Copy : Copies)
209 Copy = Orig;
210 state.ResumeTiming();
211}
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000212
MinJae Hwanga79586c2020-07-06 18:29:38 -0400213enum class BatchSize {
214 CountElements,
215 CountBatch,
216};
217
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000218template <class ValueType, class F>
219void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O,
MinJae Hwanga79586c2020-07-06 18:29:38 -0400220 BatchSize Count, F Body) {
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000221 auto Copies = makeOrderedValues<ValueType>(Quantity, O);
MinJae Hwanga79586c2020-07-06 18:29:38 -0400222 auto Orig = Copies;
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000223
MinJae Hwanga79586c2020-07-06 18:29:38 -0400224 const size_t Batch = Count == BatchSize::CountElements
225 ? Copies.size() * Quantity
226 : Copies.size();
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000227 while (state.KeepRunningBatch(Batch)) {
228 for (auto& Copy : Copies) {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400229 Body(Copy);
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000230 benchmark::DoNotOptimize(Copy);
231 }
MinJae Hwanga79586c2020-07-06 18:29:38 -0400232 state.PauseTiming();
233 Copies = Orig;
234 state.ResumeTiming();
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000235 }
236}
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000237
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000238template <class ValueType, class Order>
239struct Sort {
240 size_t Quantity;
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000241
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000242 void run(benchmark::State& state) const {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400243 runOpOnCopies<ValueType>(
244 state, Quantity, Order(), BatchSize::CountElements,
245 [](auto& Copy) { std::sort(Copy.begin(), Copy.end()); });
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000246 }
Eric Fiselier9e3e1372016-07-19 23:07:03 +0000247
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000248 bool skip() const { return Order() == ::Order::Heap; }
249
250 std::string name() const {
251 return "BM_Sort" + ValueType::name() + Order::name() + "_" +
252 std::to_string(Quantity);
253 };
254};
255
256template <class ValueType, class Order>
257struct StableSort {
258 size_t Quantity;
259
260 void run(benchmark::State& state) const {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400261 runOpOnCopies<ValueType>(
262 state, Quantity, Order(), BatchSize::CountElements,
263 [](auto& Copy) { std::stable_sort(Copy.begin(), Copy.end()); });
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000264 }
265
266 bool skip() const { return Order() == ::Order::Heap; }
267
268 std::string name() const {
269 return "BM_StableSort" + ValueType::name() + Order::name() + "_" +
270 std::to_string(Quantity);
271 };
272};
273
274template <class ValueType, class Order>
275struct MakeHeap {
276 size_t Quantity;
277
278 void run(benchmark::State& state) const {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400279 runOpOnCopies<ValueType>(
280 state, Quantity, Order(), BatchSize::CountElements,
281 [](auto& Copy) { std::make_heap(Copy.begin(), Copy.end()); });
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000282 }
283
284 std::string name() const {
285 return "BM_MakeHeap" + ValueType::name() + Order::name() + "_" +
286 std::to_string(Quantity);
287 };
288};
289
290template <class ValueType>
291struct SortHeap {
292 size_t Quantity;
293
294 void run(benchmark::State& state) const {
295 runOpOnCopies<ValueType>(
MinJae Hwanga79586c2020-07-06 18:29:38 -0400296 state, Quantity, Order::Heap, BatchSize::CountElements,
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000297 [](auto& Copy) { std::sort_heap(Copy.begin(), Copy.end()); });
298 }
299
300 std::string name() const {
301 return "BM_SortHeap" + ValueType::name() + "_" + std::to_string(Quantity);
302 };
303};
304
305template <class ValueType, class Order>
306struct MakeThenSortHeap {
307 size_t Quantity;
308
309 void run(benchmark::State& state) const {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400310 runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountElements,
311 [](auto& Copy) {
312 std::make_heap(Copy.begin(), Copy.end());
313 std::sort_heap(Copy.begin(), Copy.end());
314 });
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000315 }
316
317 std::string name() const {
318 return "BM_MakeThenSortHeap" + ValueType::name() + Order::name() + "_" +
319 std::to_string(Quantity);
320 };
321};
322
323template <class ValueType, class Order>
324struct PushHeap {
325 size_t Quantity;
326
327 void run(benchmark::State& state) const {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400328 runOpOnCopies<ValueType>(
329 state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
330 for (auto I = Copy.begin(), E = Copy.end(); I != E; ++I) {
331 std::push_heap(Copy.begin(), I + 1);
332 }
333 });
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000334 }
335
336 bool skip() const { return Order() == ::Order::Heap; }
337
338 std::string name() const {
339 return "BM_PushHeap" + ValueType::name() + Order::name() + "_" +
340 std::to_string(Quantity);
341 };
342};
343
344template <class ValueType>
345struct PopHeap {
346 size_t Quantity;
347
348 void run(benchmark::State& state) const {
MinJae Hwanga79586c2020-07-06 18:29:38 -0400349 runOpOnCopies<ValueType>(
350 state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
351 for (auto B = Copy.begin(), I = Copy.end(); I != B; --I) {
352 std::pop_heap(B, I);
353 }
354 });
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000355 }
356
357 std::string name() const {
358 return "BM_PopHeap" + ValueType::name() + "_" + std::to_string(Quantity);
359 };
360};
361
362} // namespace
363
364int main(int argc, char** argv) {
365 benchmark::Initialize(&argc, argv);
366 if (benchmark::ReportUnrecognizedArguments(argc, argv))
367 return 1;
368
369 const std::vector<size_t> Quantities = {1 << 0, 1 << 2, 1 << 4, 1 << 6,
Eric Fiselierbb3de8e2019-08-21 00:14:48 +0000370 1 << 8, 1 << 10, 1 << 14,
371 // Running each benchmark in parallel consumes too much memory with MSAN
372 // and can lead to the test process being killed.
373#if !TEST_HAS_FEATURE(memory_sanitizer)
374 1 << 18
375#endif
376 };
Samuel Benzaquen8e11d692018-11-20 17:15:17 +0000377 makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
378 makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(
379 Quantities);
380 makeCartesianProductBenchmark<MakeHeap, AllValueTypes, AllOrders>(Quantities);
381 makeCartesianProductBenchmark<SortHeap, AllValueTypes>(Quantities);
382 makeCartesianProductBenchmark<MakeThenSortHeap, AllValueTypes, AllOrders>(
383 Quantities);
384 makeCartesianProductBenchmark<PushHeap, AllValueTypes, AllOrders>(Quantities);
385 makeCartesianProductBenchmark<PopHeap, AllValueTypes>(Quantities);
386 benchmark::RunSpecifiedBenchmarks();
Kazu Hirata13c4f492021-10-23 08:45:29 -0700387}