|
17 | 17 |
|
18 | 18 | #include "benchmark/benchmark.h"
|
19 | 19 | #include "common.h"
|
| 20 | +#include "count_new.h" |
20 | 21 |
|
21 | 22 | int main(int argc, char** argv) {
|
22 | 23 | auto std_stable_sort = [](auto first, auto last) { return std::stable_sort(first, last); };
|
@@ -84,6 +85,75 @@ int main(int argc, char** argv) {
|
84 | 85 | BENCH(support::heap_data, "heap");
|
85 | 86 | BENCH(support::shuffled_data, "shuffled");
|
86 | 87 | BENCH(support::single_element_data, "repeated");
|
| 88 | +#undef BENCH |
| 89 | + } |
| 90 | + |
| 91 | + // Benchmark {std,ranges}::stable_sort when memory allocation fails. The algorithm must fall back to |
| 92 | + // a different algorithm that has different complexity guarantees. |
| 93 | + { |
| 94 | + auto bm = []<class Container>(std::string name, auto stable_sort, auto generate_data) { |
| 95 | + benchmark::RegisterBenchmark( |
| 96 | + name, |
| 97 | + [stable_sort, generate_data](auto& st) { |
| 98 | + std::size_t const size = st.range(0); |
| 99 | + constexpr std::size_t BatchSize = 32; |
| 100 | + using ValueType = typename Container::value_type; |
| 101 | + std::vector<ValueType> data = generate_data(size); |
| 102 | + std::array<Container, BatchSize> c; |
| 103 | + std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end())); |
| 104 | + |
| 105 | + while (st.KeepRunningBatch(BatchSize)) { |
| 106 | + for (std::size_t i = 0; i != BatchSize; ++i) { |
| 107 | + benchmark::DoNotOptimize(c[i]); |
| 108 | + // Disable the ability to allocate memory inside this block |
| 109 | + globalMemCounter.throw_after = 0; |
| 110 | + |
| 111 | + stable_sort(c[i].begin(), c[i].end()); |
| 112 | + benchmark::DoNotOptimize(c[i]); |
| 113 | + |
| 114 | + globalMemCounter.reset(); |
| 115 | + } |
| 116 | + |
| 117 | + st.PauseTiming(); |
| 118 | + for (std::size_t i = 0; i != BatchSize; ++i) { |
| 119 | + std::copy(data.begin(), data.end(), c[i].begin()); |
| 120 | + } |
| 121 | + st.ResumeTiming(); |
| 122 | + } |
| 123 | + }) |
| 124 | + ->Arg(8) |
| 125 | + ->Arg(1024) |
| 126 | + ->Arg(8192); |
| 127 | + }; |
| 128 | +#define BENCH(generate_data, name) \ |
| 129 | + do { \ |
| 130 | + auto gen1 = [](auto size) { return generate_data<int>(size); }; \ |
| 131 | + auto gen2 = [](auto size) { \ |
| 132 | + auto data = generate_data<int>(size); \ |
| 133 | + std::vector<support::NonIntegral> real_data(data.begin(), data.end()); \ |
| 134 | + return real_data; \ |
| 135 | + }; \ |
| 136 | + bm.operator()<std::vector<int>>("std::stable_sort(vector<int>) (alloc fails, " #name ")", std_stable_sort, gen1); \ |
| 137 | + bm.operator()<std::vector<support::NonIntegral>>( \ |
| 138 | + "std::stable_sort(vector<NonIntegral>) (alloc fails, " #name ")", std_stable_sort, gen2); \ |
| 139 | + bm.operator()<std::deque<int>>("std::stable_sort(deque<int>) (alloc fails, " #name ")", std_stable_sort, gen1); \ |
| 140 | + \ |
| 141 | + bm.operator()<std::vector<int>>( \ |
| 142 | + "rng::stable_sort(vector<int>) (alloc fails, " #name ")", std::ranges::stable_sort, gen1); \ |
| 143 | + bm.operator()<std::vector<support::NonIntegral>>( \ |
| 144 | + "rng::stable_sort(vector<NonIntegral>) (alloc fails, " #name ")", std::ranges::stable_sort, gen2); \ |
| 145 | + bm.operator()<std::deque<int>>( \ |
| 146 | + "rng::stable_sort(deque<int>) (alloc fails, " #name ")", std::ranges::stable_sort, gen1); \ |
| 147 | + } while (false) |
| 148 | + |
| 149 | + BENCH(support::quicksort_adversarial_data, "qsort adversarial"); |
| 150 | + BENCH(support::ascending_sorted_data, "ascending"); |
| 151 | + BENCH(support::descending_sorted_data, "descending"); |
| 152 | + BENCH(support::pipe_organ_data, "pipe-organ"); |
| 153 | + BENCH(support::heap_data, "heap"); |
| 154 | + BENCH(support::shuffled_data, "shuffled"); |
| 155 | + BENCH(support::single_element_data, "repeated"); |
| 156 | +#undef BENCH |
87 | 157 | }
|
88 | 158 |
|
89 | 159 | benchmark::Initialize(&argc, argv);
|
|
0 commit comments