1616#include < string>
1717#include < vector>
1818
19+ #include " count_new.h"
1920#include " benchmark/benchmark.h"
2021#include " ../../GenerateInput.h"
2122
@@ -29,47 +30,151 @@ auto compute_median(auto first, auto last) {
2930int main (int argc, char ** argv) {
3031 auto std_stable_partition = [](auto first, auto last, auto pred) { return std::stable_partition (first, last, pred); };
3132
32- auto bm = []<class Container >(std::string name, auto stable_partition) {
33- benchmark::RegisterBenchmark (
34- name,
35- [stable_partition](auto & st) {
36- std::size_t const size = st.range (0 );
37- using ValueType = typename Container::value_type;
38- Container c;
39- std::generate_n (std::back_inserter (c), size, [] { return Generate<ValueType>::random (); });
40-
41- std::vector<ValueType> yes (size), no (size);
42- ValueType median = compute_median (c.begin (), c.end ());
43- auto pred1 = [median](auto const & element) { return element < median; };
44- auto pred2 = [median](auto const & element) { return element > median; };
45- bool toggle = false ;
46-
47- for ([[maybe_unused]] auto _ : st) {
48- benchmark::DoNotOptimize (c);
49- if (toggle) {
50- auto result = stable_partition (c.begin (), c.end (), pred1);
51- benchmark::DoNotOptimize (result);
52- } else {
53- auto result = stable_partition (c.begin (), c.end (), pred2);
33+ // Benchmark {std,ranges}::stable_partition on a fully unpartitionned sequence, i.e. a lot of elements
34+ // have to be moved around in order to partition the range.
35+ {
36+ auto bm = []<class Container >(std::string name, auto stable_partition) {
37+ benchmark::RegisterBenchmark (
38+ name,
39+ [stable_partition](auto & st) {
40+ std::size_t const size = st.range (0 );
41+ using ValueType = typename Container::value_type;
42+ Container c;
43+ std::generate_n (std::back_inserter (c), size, [] { return Generate<ValueType>::random (); });
44+
45+ ValueType median = compute_median (c.begin (), c.end ());
46+ auto pred1 = [median](auto const & element) { return element < median; };
47+ auto pred2 = [median](auto const & element) { return element > median; };
48+ bool toggle = false ;
49+
50+ for ([[maybe_unused]] auto _ : st) {
51+ benchmark::DoNotOptimize (c);
52+ // By toggling the predicate, we have to move almost all elements in the sequence
53+ // to restore the partition.
54+ if (toggle) {
55+ auto result = stable_partition (c.begin (), c.end (), pred1);
56+ benchmark::DoNotOptimize (result);
57+ } else {
58+ auto result = stable_partition (c.begin (), c.end (), pred2);
59+ benchmark::DoNotOptimize (result);
60+ }
61+ toggle = !toggle;
62+ }
63+ })
64+ ->Arg (32 )
65+ ->Arg (50 ) // non power-of-two
66+ ->Arg (1024 )
67+ ->Arg (8192 );
68+ };
69+
70+ // std::stable_partition
71+ bm.operator ()<std::vector<int >>(" std::stable_partition(vector<int>) (dense)" , std_stable_partition);
72+ bm.operator ()<std::deque<int >>(" std::stable_partition(deque<int>) (dense)" , std_stable_partition);
73+ bm.operator ()<std::list<int >>(" std::stable_partition(list<int>) (dense)" , std_stable_partition);
74+
75+ // ranges::stable_partition
76+ bm.operator ()<std::vector<int >>(" rng::stable_partition(vector<int>) (dense)" , std::ranges::stable_partition);
77+ bm.operator ()<std::deque<int >>(" rng::stable_partition(deque<int>) (dense)" , std::ranges::stable_partition);
78+ bm.operator ()<std::list<int >>(" rng::stable_partition(list<int>) (dense)" , std::ranges::stable_partition);
79+ }
80+
81+ // Benchmark {std,ranges}::stable_partition on a mostly partitioned sequence, i.e. only 10% of the elements
82+ // have to be moved around in order to partition the range.
83+ {
84+ auto bm = []<class Container >(std::string name, auto stable_partition) {
85+ benchmark::RegisterBenchmark (
86+ name,
87+ [stable_partition](auto & st) {
88+ std::size_t const size = st.range (0 );
89+ using ValueType = typename Container::value_type;
90+ Container c;
91+ std::generate_n (std::back_inserter (c), size, [] { return Generate<ValueType>::random (); });
92+ ValueType median = compute_median (c.begin (), c.end ());
93+ auto pred = [median](auto const & element) { return element < median; };
94+ std::partition (c.begin (), c.end (), pred);
95+
96+ // Between iterations, we swap 5% of the elements to the left of the median with 5% of the elements
97+ // to the right of the median. This ensures that the range is slightly unpartitioned.
98+ auto median_it = std::partition_point (c.begin (), c.end (), pred);
99+ auto low = std::next (c.begin (), std::distance (c.begin (), median_it) - (size / 20 ));
100+ auto high = std::next (median_it, size / 20 );
101+ auto shuffle = [&] { std::swap_ranges (low, median_it, high); };
102+ shuffle ();
103+ assert (!std::is_partitioned (c.begin (), c.end (), pred));
104+
105+ for ([[maybe_unused]] auto _ : st) {
106+ benchmark::DoNotOptimize (c);
107+ auto result = stable_partition (c.begin (), c.end (), pred);
54108 benchmark::DoNotOptimize (result);
109+ shuffle ();
55110 }
56- toggle = !toggle;
57- }
58- })
59- ->Arg (32 )
60- ->Arg (1024 )
61- ->Arg (8192 );
62- };
63-
64- // std::stable_partition
65- bm.operator ()<std::vector<int >>(" std::stable_partition(vector<int>)" , std_stable_partition);
66- bm.operator ()<std::deque<int >>(" std::stable_partition(deque<int>)" , std_stable_partition);
67- bm.operator ()<std::list<int >>(" std::stable_partition(list<int>)" , std_stable_partition);
68-
69- // ranges::stable_partition
70- bm.operator ()<std::vector<int >>(" rng::stable_partition(vector<int>)" , std::ranges::stable_partition);
71- bm.operator ()<std::deque<int >>(" rng::stable_partition(deque<int>)" , std::ranges::stable_partition);
72- bm.operator ()<std::list<int >>(" rng::stable_partition(list<int>)" , std::ranges::stable_partition);
111+ })
112+ ->Arg (32 )
113+ ->Arg (50 ) // non power-of-two
114+ ->Arg (1024 )
115+ ->Arg (8192 );
116+ };
117+
118+ // std::stable_partition
119+ bm.operator ()<std::vector<int >>(" std::stable_partition(vector<int>) (sparse)" , std_stable_partition);
120+ bm.operator ()<std::deque<int >>(" std::stable_partition(deque<int>) (sparse)" , std_stable_partition);
121+ bm.operator ()<std::list<int >>(" std::stable_partition(list<int>) (sparse)" , std_stable_partition);
122+
123+ // ranges::stable_partition
124+ bm.operator ()<std::vector<int >>(" rng::stable_partition(vector<int>) (sparse)" , std::ranges::stable_partition);
125+ bm.operator ()<std::deque<int >>(" rng::stable_partition(deque<int>) (sparse)" , std::ranges::stable_partition);
126+ bm.operator ()<std::list<int >>(" rng::stable_partition(list<int>) (sparse)" , std::ranges::stable_partition);
127+ }
128+
129+ // Benchmark {std,ranges}::stable_partition when memory allocation fails. The algorithm must fall back to
130+ // a different algorithm that has different complexity guarantees.
131+ {
132+ auto bm = []<class Container >(std::string name, auto stable_partition) {
133+ benchmark::RegisterBenchmark (
134+ name,
135+ [stable_partition](auto & st) {
136+ std::size_t const size = st.range (0 );
137+ using ValueType = typename Container::value_type;
138+ Container c;
139+ std::generate_n (std::back_inserter (c), size, [] { return Generate<ValueType>::random (); });
140+
141+ ValueType median = compute_median (c.begin (), c.end ());
142+ auto pred1 = [median](auto const & element) { return element < median; };
143+ auto pred2 = [median](auto const & element) { return element > median; };
144+ bool toggle = false ;
145+
146+ for ([[maybe_unused]] auto _ : st) {
147+ benchmark::DoNotOptimize (c);
148+ // Disable the ability to allocate memory inside this block
149+ globalMemCounter.reset ();
150+ globalMemCounter.throw_after = 0 ;
151+
152+ if (toggle) {
153+ auto result = stable_partition (c.begin (), c.end (), pred1);
154+ benchmark::DoNotOptimize (result);
155+ } else {
156+ auto result = stable_partition (c.begin (), c.end (), pred2);
157+ benchmark::DoNotOptimize (result);
158+ }
159+ toggle = !toggle;
160+ }
161+ })
162+ ->Arg (32 )
163+ ->Arg (50 ) // non power-of-two
164+ ->Arg (1024 )
165+ ->Arg (8192 );
166+ };
167+
168+ // std::stable_partition
169+ bm.operator ()<std::vector<int >>(" std::stable_partition(vector<int>) (no alloc)" , std_stable_partition);
170+ bm.operator ()<std::deque<int >>(" std::stable_partition(deque<int>) (no alloc)" , std_stable_partition);
171+ bm.operator ()<std::list<int >>(" std::stable_partition(list<int>) (no alloc)" , std_stable_partition);
172+
173+ // ranges::stable_partition
174+ bm.operator ()<std::vector<int >>(" rng::stable_partition(vector<int>) (no alloc)" , std::ranges::stable_partition);
175+ bm.operator ()<std::deque<int >>(" rng::stable_partition(deque<int>) (no alloc)" , std::ranges::stable_partition);
176+ bm.operator ()<std::list<int >>(" rng::stable_partition(list<int>) (no alloc)" , std::ranges::stable_partition);
177+ }
73178
74179 benchmark::Initialize (&argc, argv);
75180 benchmark::RunSpecifiedBenchmarks ();
0 commit comments