1*4d6fc14bSjoerg
2*4d6fc14bSjoerg #include <algorithm>
3*4d6fc14bSjoerg #include <cstdint>
4*4d6fc14bSjoerg #include <map>
5*4d6fc14bSjoerg #include <random>
6*4d6fc14bSjoerg #include <string>
7*4d6fc14bSjoerg #include <utility>
8*4d6fc14bSjoerg #include <vector>
9*4d6fc14bSjoerg
10*4d6fc14bSjoerg #include "CartesianBenchmarks.h"
11*4d6fc14bSjoerg #include "GenerateInput.h"
12*4d6fc14bSjoerg #include "benchmark/benchmark.h"
13*4d6fc14bSjoerg #include "test_macros.h"
14*4d6fc14bSjoerg
15*4d6fc14bSjoerg namespace {
16*4d6fc14bSjoerg
17*4d6fc14bSjoerg enum class ValueType { Uint32, Uint64, Pair, Tuple, String };
18*4d6fc14bSjoerg struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 5> {
19*4d6fc14bSjoerg static constexpr const char* Names[] = {
20*4d6fc14bSjoerg "uint32", "uint64", "pair<uint32, uint32>",
21*4d6fc14bSjoerg "tuple<uint32, uint64, uint32>", "string"};
22*4d6fc14bSjoerg };
23*4d6fc14bSjoerg
24*4d6fc14bSjoerg template <class V>
25*4d6fc14bSjoerg using Value = std::conditional_t<
26*4d6fc14bSjoerg V() == ValueType::Uint32, uint32_t,
27*4d6fc14bSjoerg std::conditional_t<
28*4d6fc14bSjoerg V() == ValueType::Uint64, uint64_t,
29*4d6fc14bSjoerg std::conditional_t<
30*4d6fc14bSjoerg V() == ValueType::Pair, std::pair<uint32_t, uint32_t>,
31*4d6fc14bSjoerg std::conditional_t<V() == ValueType::Tuple,
32*4d6fc14bSjoerg std::tuple<uint32_t, uint64_t, uint32_t>,
33*4d6fc14bSjoerg std::string> > > >;
34*4d6fc14bSjoerg
35*4d6fc14bSjoerg enum class Order {
36*4d6fc14bSjoerg Random,
37*4d6fc14bSjoerg Ascending,
38*4d6fc14bSjoerg Descending,
39*4d6fc14bSjoerg SingleElement,
40*4d6fc14bSjoerg PipeOrgan,
41*4d6fc14bSjoerg Heap
42*4d6fc14bSjoerg };
43*4d6fc14bSjoerg struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 6> {
44*4d6fc14bSjoerg static constexpr const char* Names[] = {"Random", "Ascending",
45*4d6fc14bSjoerg "Descending", "SingleElement",
46*4d6fc14bSjoerg "PipeOrgan", "Heap"};
47*4d6fc14bSjoerg };
48*4d6fc14bSjoerg
49*4d6fc14bSjoerg template <typename T>
fillValues(std::vector<T> & V,size_t N,Order O)50*4d6fc14bSjoerg void fillValues(std::vector<T>& V, size_t N, Order O) {
51*4d6fc14bSjoerg if (O == Order::SingleElement) {
52*4d6fc14bSjoerg V.resize(N, 0);
53*4d6fc14bSjoerg } else {
54*4d6fc14bSjoerg while (V.size() < N)
55*4d6fc14bSjoerg V.push_back(V.size());
56*4d6fc14bSjoerg }
57*4d6fc14bSjoerg }
58*4d6fc14bSjoerg
59*4d6fc14bSjoerg template <typename T>
fillValues(std::vector<std::pair<T,T>> & V,size_t N,Order O)60*4d6fc14bSjoerg void fillValues(std::vector<std::pair<T, T> >& V, size_t N, Order O) {
61*4d6fc14bSjoerg if (O == Order::SingleElement) {
62*4d6fc14bSjoerg V.resize(N, std::make_pair(0, 0));
63*4d6fc14bSjoerg } else {
64*4d6fc14bSjoerg while (V.size() < N)
65*4d6fc14bSjoerg // Half of array will have the same first element.
66*4d6fc14bSjoerg if (V.size() % 2) {
67*4d6fc14bSjoerg V.push_back(std::make_pair(V.size(), V.size()));
68*4d6fc14bSjoerg } else {
69*4d6fc14bSjoerg V.push_back(std::make_pair(0, V.size()));
70*4d6fc14bSjoerg }
71*4d6fc14bSjoerg }
72*4d6fc14bSjoerg }
73*4d6fc14bSjoerg
74*4d6fc14bSjoerg template <typename T1, typename T2, typename T3>
fillValues(std::vector<std::tuple<T1,T2,T3>> & V,size_t N,Order O)75*4d6fc14bSjoerg void fillValues(std::vector<std::tuple<T1, T2, T3> >& V, size_t N, Order O) {
76*4d6fc14bSjoerg if (O == Order::SingleElement) {
77*4d6fc14bSjoerg V.resize(N, std::make_tuple(0, 0, 0));
78*4d6fc14bSjoerg } else {
79*4d6fc14bSjoerg while (V.size() < N)
80*4d6fc14bSjoerg // One third of array will have the same first element.
81*4d6fc14bSjoerg // One third of array will have the same first element and the same second element.
82*4d6fc14bSjoerg switch (V.size() % 3) {
83*4d6fc14bSjoerg case 0:
84*4d6fc14bSjoerg V.push_back(std::make_tuple(V.size(), V.size(), V.size()));
85*4d6fc14bSjoerg break;
86*4d6fc14bSjoerg case 1:
87*4d6fc14bSjoerg V.push_back(std::make_tuple(0, V.size(), V.size()));
88*4d6fc14bSjoerg break;
89*4d6fc14bSjoerg case 2:
90*4d6fc14bSjoerg V.push_back(std::make_tuple(0, 0, V.size()));
91*4d6fc14bSjoerg break;
92*4d6fc14bSjoerg }
93*4d6fc14bSjoerg }
94*4d6fc14bSjoerg }
95*4d6fc14bSjoerg
fillValues(std::vector<std::string> & V,size_t N,Order O)96*4d6fc14bSjoerg void fillValues(std::vector<std::string>& V, size_t N, Order O) {
97*4d6fc14bSjoerg if (O == Order::SingleElement) {
98*4d6fc14bSjoerg V.resize(N, getRandomString(64));
99*4d6fc14bSjoerg } else {
100*4d6fc14bSjoerg while (V.size() < N)
101*4d6fc14bSjoerg V.push_back(getRandomString(64));
102*4d6fc14bSjoerg }
103*4d6fc14bSjoerg }
104*4d6fc14bSjoerg
105*4d6fc14bSjoerg template <class T>
sortValues(T & V,Order O)106*4d6fc14bSjoerg void sortValues(T& V, Order O) {
107*4d6fc14bSjoerg assert(std::is_sorted(V.begin(), V.end()));
108*4d6fc14bSjoerg switch (O) {
109*4d6fc14bSjoerg case Order::Random: {
110*4d6fc14bSjoerg std::random_device R;
111*4d6fc14bSjoerg std::mt19937 M(R());
112*4d6fc14bSjoerg std::shuffle(V.begin(), V.end(), M);
113*4d6fc14bSjoerg break;
114*4d6fc14bSjoerg }
115*4d6fc14bSjoerg case Order::Ascending:
116*4d6fc14bSjoerg std::sort(V.begin(), V.end());
117*4d6fc14bSjoerg break;
118*4d6fc14bSjoerg case Order::Descending:
119*4d6fc14bSjoerg std::sort(V.begin(), V.end(), std::greater<>());
120*4d6fc14bSjoerg break;
121*4d6fc14bSjoerg case Order::SingleElement:
122*4d6fc14bSjoerg // Nothing to do
123*4d6fc14bSjoerg break;
124*4d6fc14bSjoerg case Order::PipeOrgan:
125*4d6fc14bSjoerg std::sort(V.begin(), V.end());
126*4d6fc14bSjoerg std::reverse(V.begin() + V.size() / 2, V.end());
127*4d6fc14bSjoerg break;
128*4d6fc14bSjoerg case Order::Heap:
129*4d6fc14bSjoerg std::make_heap(V.begin(), V.end());
130*4d6fc14bSjoerg break;
131*4d6fc14bSjoerg }
132*4d6fc14bSjoerg }
133*4d6fc14bSjoerg
134*4d6fc14bSjoerg constexpr size_t TestSetElements =
135*4d6fc14bSjoerg #if !TEST_HAS_FEATURE(memory_sanitizer)
136*4d6fc14bSjoerg 1 << 18;
137*4d6fc14bSjoerg #else
138*4d6fc14bSjoerg 1 << 14;
139*4d6fc14bSjoerg #endif
140*4d6fc14bSjoerg
141*4d6fc14bSjoerg template <class ValueType>
makeOrderedValues(size_t N,Order O)142*4d6fc14bSjoerg std::vector<std::vector<Value<ValueType> > > makeOrderedValues(size_t N,
143*4d6fc14bSjoerg Order O) {
144*4d6fc14bSjoerg std::vector<std::vector<Value<ValueType> > > Ret;
145*4d6fc14bSjoerg const size_t NumCopies = std::max(size_t{1}, TestSetElements / N);
146*4d6fc14bSjoerg Ret.resize(NumCopies);
147*4d6fc14bSjoerg for (auto& V : Ret) {
148*4d6fc14bSjoerg fillValues(V, N, O);
149*4d6fc14bSjoerg sortValues(V, O);
150*4d6fc14bSjoerg }
151*4d6fc14bSjoerg return Ret;
152*4d6fc14bSjoerg }
153*4d6fc14bSjoerg
154*4d6fc14bSjoerg template <class T, class U>
resetCopies(benchmark::State & state,T & Copies,U & Orig)155*4d6fc14bSjoerg TEST_ALWAYS_INLINE void resetCopies(benchmark::State& state, T& Copies,
156*4d6fc14bSjoerg U& Orig) {
157*4d6fc14bSjoerg state.PauseTiming();
158*4d6fc14bSjoerg for (auto& Copy : Copies)
159*4d6fc14bSjoerg Copy = Orig;
160*4d6fc14bSjoerg state.ResumeTiming();
161*4d6fc14bSjoerg }
162*4d6fc14bSjoerg
163*4d6fc14bSjoerg enum class BatchSize {
164*4d6fc14bSjoerg CountElements,
165*4d6fc14bSjoerg CountBatch,
166*4d6fc14bSjoerg };
167*4d6fc14bSjoerg
168*4d6fc14bSjoerg template <class ValueType, class F>
runOpOnCopies(benchmark::State & state,size_t Quantity,Order O,BatchSize Count,F Body)169*4d6fc14bSjoerg void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O,
170*4d6fc14bSjoerg BatchSize Count, F Body) {
171*4d6fc14bSjoerg auto Copies = makeOrderedValues<ValueType>(Quantity, O);
172*4d6fc14bSjoerg auto Orig = Copies;
173*4d6fc14bSjoerg
174*4d6fc14bSjoerg const size_t Batch = Count == BatchSize::CountElements
175*4d6fc14bSjoerg ? Copies.size() * Quantity
176*4d6fc14bSjoerg : Copies.size();
177*4d6fc14bSjoerg while (state.KeepRunningBatch(Batch)) {
178*4d6fc14bSjoerg for (auto& Copy : Copies) {
179*4d6fc14bSjoerg Body(Copy);
180*4d6fc14bSjoerg benchmark::DoNotOptimize(Copy);
181*4d6fc14bSjoerg }
182*4d6fc14bSjoerg state.PauseTiming();
183*4d6fc14bSjoerg Copies = Orig;
184*4d6fc14bSjoerg state.ResumeTiming();
185*4d6fc14bSjoerg }
186*4d6fc14bSjoerg }
187*4d6fc14bSjoerg
188*4d6fc14bSjoerg template <class ValueType, class Order>
189*4d6fc14bSjoerg struct Sort {
190*4d6fc14bSjoerg size_t Quantity;
191*4d6fc14bSjoerg
run__anonc233ceb90111::Sort192*4d6fc14bSjoerg void run(benchmark::State& state) const {
193*4d6fc14bSjoerg runOpOnCopies<ValueType>(
194*4d6fc14bSjoerg state, Quantity, Order(), BatchSize::CountElements,
195*4d6fc14bSjoerg [](auto& Copy) { std::sort(Copy.begin(), Copy.end()); });
196*4d6fc14bSjoerg }
197*4d6fc14bSjoerg
skip__anonc233ceb90111::Sort198*4d6fc14bSjoerg bool skip() const { return Order() == ::Order::Heap; }
199*4d6fc14bSjoerg
name__anonc233ceb90111::Sort200*4d6fc14bSjoerg std::string name() const {
201*4d6fc14bSjoerg return "BM_Sort" + ValueType::name() + Order::name() + "_" +
202*4d6fc14bSjoerg std::to_string(Quantity);
203*4d6fc14bSjoerg };
204*4d6fc14bSjoerg };
205*4d6fc14bSjoerg
206*4d6fc14bSjoerg template <class ValueType, class Order>
207*4d6fc14bSjoerg struct StableSort {
208*4d6fc14bSjoerg size_t Quantity;
209*4d6fc14bSjoerg
run__anonc233ceb90111::StableSort210*4d6fc14bSjoerg void run(benchmark::State& state) const {
211*4d6fc14bSjoerg runOpOnCopies<ValueType>(
212*4d6fc14bSjoerg state, Quantity, Order(), BatchSize::CountElements,
213*4d6fc14bSjoerg [](auto& Copy) { std::stable_sort(Copy.begin(), Copy.end()); });
214*4d6fc14bSjoerg }
215*4d6fc14bSjoerg
skip__anonc233ceb90111::StableSort216*4d6fc14bSjoerg bool skip() const { return Order() == ::Order::Heap; }
217*4d6fc14bSjoerg
name__anonc233ceb90111::StableSort218*4d6fc14bSjoerg std::string name() const {
219*4d6fc14bSjoerg return "BM_StableSort" + ValueType::name() + Order::name() + "_" +
220*4d6fc14bSjoerg std::to_string(Quantity);
221*4d6fc14bSjoerg };
222*4d6fc14bSjoerg };
223*4d6fc14bSjoerg
224*4d6fc14bSjoerg template <class ValueType, class Order>
225*4d6fc14bSjoerg struct MakeHeap {
226*4d6fc14bSjoerg size_t Quantity;
227*4d6fc14bSjoerg
run__anonc233ceb90111::MakeHeap228*4d6fc14bSjoerg void run(benchmark::State& state) const {
229*4d6fc14bSjoerg runOpOnCopies<ValueType>(
230*4d6fc14bSjoerg state, Quantity, Order(), BatchSize::CountElements,
231*4d6fc14bSjoerg [](auto& Copy) { std::make_heap(Copy.begin(), Copy.end()); });
232*4d6fc14bSjoerg }
233*4d6fc14bSjoerg
name__anonc233ceb90111::MakeHeap234*4d6fc14bSjoerg std::string name() const {
235*4d6fc14bSjoerg return "BM_MakeHeap" + ValueType::name() + Order::name() + "_" +
236*4d6fc14bSjoerg std::to_string(Quantity);
237*4d6fc14bSjoerg };
238*4d6fc14bSjoerg };
239*4d6fc14bSjoerg
240*4d6fc14bSjoerg template <class ValueType>
241*4d6fc14bSjoerg struct SortHeap {
242*4d6fc14bSjoerg size_t Quantity;
243*4d6fc14bSjoerg
run__anonc233ceb90111::SortHeap244*4d6fc14bSjoerg void run(benchmark::State& state) const {
245*4d6fc14bSjoerg runOpOnCopies<ValueType>(
246*4d6fc14bSjoerg state, Quantity, Order::Heap, BatchSize::CountElements,
247*4d6fc14bSjoerg [](auto& Copy) { std::sort_heap(Copy.begin(), Copy.end()); });
248*4d6fc14bSjoerg }
249*4d6fc14bSjoerg
name__anonc233ceb90111::SortHeap250*4d6fc14bSjoerg std::string name() const {
251*4d6fc14bSjoerg return "BM_SortHeap" + ValueType::name() + "_" + std::to_string(Quantity);
252*4d6fc14bSjoerg };
253*4d6fc14bSjoerg };
254*4d6fc14bSjoerg
255*4d6fc14bSjoerg template <class ValueType, class Order>
256*4d6fc14bSjoerg struct MakeThenSortHeap {
257*4d6fc14bSjoerg size_t Quantity;
258*4d6fc14bSjoerg
run__anonc233ceb90111::MakeThenSortHeap259*4d6fc14bSjoerg void run(benchmark::State& state) const {
260*4d6fc14bSjoerg runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountElements,
261*4d6fc14bSjoerg [](auto& Copy) {
262*4d6fc14bSjoerg std::make_heap(Copy.begin(), Copy.end());
263*4d6fc14bSjoerg std::sort_heap(Copy.begin(), Copy.end());
264*4d6fc14bSjoerg });
265*4d6fc14bSjoerg }
266*4d6fc14bSjoerg
name__anonc233ceb90111::MakeThenSortHeap267*4d6fc14bSjoerg std::string name() const {
268*4d6fc14bSjoerg return "BM_MakeThenSortHeap" + ValueType::name() + Order::name() + "_" +
269*4d6fc14bSjoerg std::to_string(Quantity);
270*4d6fc14bSjoerg };
271*4d6fc14bSjoerg };
272*4d6fc14bSjoerg
273*4d6fc14bSjoerg template <class ValueType, class Order>
274*4d6fc14bSjoerg struct PushHeap {
275*4d6fc14bSjoerg size_t Quantity;
276*4d6fc14bSjoerg
run__anonc233ceb90111::PushHeap277*4d6fc14bSjoerg void run(benchmark::State& state) const {
278*4d6fc14bSjoerg runOpOnCopies<ValueType>(
279*4d6fc14bSjoerg state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
280*4d6fc14bSjoerg for (auto I = Copy.begin(), E = Copy.end(); I != E; ++I) {
281*4d6fc14bSjoerg std::push_heap(Copy.begin(), I + 1);
282*4d6fc14bSjoerg }
283*4d6fc14bSjoerg });
284*4d6fc14bSjoerg }
285*4d6fc14bSjoerg
skip__anonc233ceb90111::PushHeap286*4d6fc14bSjoerg bool skip() const { return Order() == ::Order::Heap; }
287*4d6fc14bSjoerg
name__anonc233ceb90111::PushHeap288*4d6fc14bSjoerg std::string name() const {
289*4d6fc14bSjoerg return "BM_PushHeap" + ValueType::name() + Order::name() + "_" +
290*4d6fc14bSjoerg std::to_string(Quantity);
291*4d6fc14bSjoerg };
292*4d6fc14bSjoerg };
293*4d6fc14bSjoerg
294*4d6fc14bSjoerg template <class ValueType>
295*4d6fc14bSjoerg struct PopHeap {
296*4d6fc14bSjoerg size_t Quantity;
297*4d6fc14bSjoerg
run__anonc233ceb90111::PopHeap298*4d6fc14bSjoerg void run(benchmark::State& state) const {
299*4d6fc14bSjoerg runOpOnCopies<ValueType>(
300*4d6fc14bSjoerg state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
301*4d6fc14bSjoerg for (auto B = Copy.begin(), I = Copy.end(); I != B; --I) {
302*4d6fc14bSjoerg std::pop_heap(B, I);
303*4d6fc14bSjoerg }
304*4d6fc14bSjoerg });
305*4d6fc14bSjoerg }
306*4d6fc14bSjoerg
name__anonc233ceb90111::PopHeap307*4d6fc14bSjoerg std::string name() const {
308*4d6fc14bSjoerg return "BM_PopHeap" + ValueType::name() + "_" + std::to_string(Quantity);
309*4d6fc14bSjoerg };
310*4d6fc14bSjoerg };
311*4d6fc14bSjoerg
312*4d6fc14bSjoerg } // namespace
313*4d6fc14bSjoerg
main(int argc,char ** argv)314*4d6fc14bSjoerg int main(int argc, char** argv) {
315*4d6fc14bSjoerg benchmark::Initialize(&argc, argv);
316*4d6fc14bSjoerg if (benchmark::ReportUnrecognizedArguments(argc, argv))
317*4d6fc14bSjoerg return 1;
318*4d6fc14bSjoerg
319*4d6fc14bSjoerg const std::vector<size_t> Quantities = {1 << 0, 1 << 2, 1 << 4, 1 << 6,
320*4d6fc14bSjoerg 1 << 8, 1 << 10, 1 << 14,
321*4d6fc14bSjoerg // Running each benchmark in parallel consumes too much memory with MSAN
322*4d6fc14bSjoerg // and can lead to the test process being killed.
323*4d6fc14bSjoerg #if !TEST_HAS_FEATURE(memory_sanitizer)
324*4d6fc14bSjoerg 1 << 18
325*4d6fc14bSjoerg #endif
326*4d6fc14bSjoerg };
327*4d6fc14bSjoerg makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
328*4d6fc14bSjoerg makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(
329*4d6fc14bSjoerg Quantities);
330*4d6fc14bSjoerg makeCartesianProductBenchmark<MakeHeap, AllValueTypes, AllOrders>(Quantities);
331*4d6fc14bSjoerg makeCartesianProductBenchmark<SortHeap, AllValueTypes>(Quantities);
332*4d6fc14bSjoerg makeCartesianProductBenchmark<MakeThenSortHeap, AllValueTypes, AllOrders>(
333*4d6fc14bSjoerg Quantities);
334*4d6fc14bSjoerg makeCartesianProductBenchmark<PushHeap, AllValueTypes, AllOrders>(Quantities);
335*4d6fc14bSjoerg makeCartesianProductBenchmark<PopHeap, AllValueTypes>(Quantities);
336*4d6fc14bSjoerg benchmark::RunSpecifiedBenchmarks();
337*4d6fc14bSjoerg }