9
9
// UNSUPPORTED: c++03, c++11, c++14, c++17
10
10
11
11
#include < algorithm>
12
+ #include < cassert>
12
13
#include < cstddef>
13
14
#include < deque>
14
15
#include < forward_list>
@@ -38,8 +39,34 @@ int main(int argc, char** argv) {
38
39
});
39
40
};
40
41
41
- // Benchmark {std,ranges}::find_end where the subsequence is found
42
- // 25% into the sequence
42
+ auto register_benchmarks = [&](auto bm, std::string comment) {
43
+ // {std,ranges}::find_end(it1, it1, it2, it2)
44
+ bm.template operator ()<std::vector<int >>(" std::find_end(vector<int>) (" + comment + " )" , std_find_end);
45
+ bm.template operator ()<std::deque<int >>(" std::find_end(deque<int>) (" + comment + " )" , std_find_end);
46
+ bm.template operator ()<std::list<int >>(" std::find_end(list<int>) (" + comment + " )" , std_find_end);
47
+ bm.template operator ()<std::forward_list<int >>(" std::find_end(forward_list<int>) (" + comment + " )" , std_find_end);
48
+ bm.template operator ()<std::vector<int >>(" rng::find_end(vector<int>) (" + comment + " )" , std::ranges::find_end);
49
+ bm.template operator ()<std::deque<int >>(" rng::find_end(deque<int>) (" + comment + " )" , std::ranges::find_end);
50
+ bm.template operator ()<std::list<int >>(" rng::find_end(list<int>) (" + comment + " )" , std::ranges::find_end);
51
+ bm.template operator ()<std::forward_list<int >>(
52
+ " rng::find_end(forward_list<int>) (" + comment + " )" , std::ranges::find_end);
53
+
54
+ // {std,ranges}::find_end(it1, it1, it2, it2, pred)
55
+ bm.template operator ()<std::vector<int >>(" std::find_end(vector<int>, pred) (" + comment + " )" , std_find_end_pred);
56
+ bm.template operator ()<std::deque<int >>(" std::find_end(deque<int>, pred) (" + comment + " )" , std_find_end_pred);
57
+ bm.template operator ()<std::list<int >>(" std::find_end(list<int>, pred) (" + comment + " )" , std_find_end_pred);
58
+ bm.template operator ()<std::forward_list<int >>(
59
+ " std::find_end(forward_list<int>, pred) (" + comment + " )" , std_find_end_pred);
60
+ bm.template operator ()<std::vector<int >>(
61
+ " rng::find_end(vector<int>, pred) (" + comment + " )" , ranges_find_end_pred);
62
+ bm.template operator ()<std::deque<int >>(" rng::find_end(deque<int>, pred) (" + comment + " )" , ranges_find_end_pred);
63
+ bm.template operator ()<std::list<int >>(" rng::find_end(list<int>, pred) (" + comment + " )" , ranges_find_end_pred);
64
+ bm.template operator ()<std::forward_list<int >>(
65
+ " rng::find_end(forward_list<int>, pred) (" + comment + " )" , ranges_find_end_pred);
66
+ };
67
+
68
+ // Benchmark {std,ranges}::find_end where we never find the needle, which is the
69
+ // worst case.
43
70
{
44
71
auto bm = []<class Container >(std::string name, auto find_end) {
45
72
benchmark::RegisterBenchmark (
@@ -49,16 +76,15 @@ int main(int argc, char** argv) {
49
76
using ValueType = typename Container::value_type;
50
77
ValueType x = Generate<ValueType>::random ();
51
78
ValueType y = random_different_from ({x});
52
- Container c (size, x);
53
- Container subrange (size / 10 , y); // subrange of length 10% of the full range
79
+ Container haystack (size, x);
80
+ std::size_t n = size / 10 ; // needle size is 10% of the haystack, but we'll never find it
81
+ assert (n > 0 );
82
+ Container needle (n, y);
54
83
55
- // put the element we're searching for at 25% of the sequence
56
- std::ranges::copy (subrange, std::next (c.begin (), size / 4 ));
57
-
58
- for ([[maybe_unused]] auto _ : st) {
59
- benchmark::DoNotOptimize (c);
60
- benchmark::DoNotOptimize (subrange);
61
- auto result = find_end (c.begin (), c.end (), subrange.begin (), subrange.end ());
84
+ for (auto _ : st) {
85
+ benchmark::DoNotOptimize (haystack);
86
+ benchmark::DoNotOptimize (needle);
87
+ auto result = find_end (haystack.begin (), haystack.end (), needle.begin (), needle.end ());
62
88
benchmark::DoNotOptimize (result);
63
89
}
64
90
})
@@ -67,29 +93,10 @@ int main(int argc, char** argv) {
67
93
->Arg (8192 )
68
94
->Arg (1 << 20 );
69
95
};
70
- // {std,ranges}::find_end(it1, it1, it2, it2)
71
- bm.operator ()<std::vector<int >>(" std::find_end(vector<int>) (bail 25%)" , std_find_end);
72
- bm.operator ()<std::deque<int >>(" std::find_end(deque<int>) (bail 25%)" , std_find_end);
73
- bm.operator ()<std::list<int >>(" std::find_end(list<int>) (bail 25%)" , std_find_end);
74
- bm.operator ()<std::forward_list<int >>(" std::find_end(forward_list<int>) (bail 25%)" , std_find_end);
75
- bm.operator ()<std::vector<int >>(" rng::find_end(vector<int>) (bail 25%)" , std::ranges::find_end);
76
- bm.operator ()<std::deque<int >>(" rng::find_end(deque<int>) (bail 25%)" , std::ranges::find_end);
77
- bm.operator ()<std::list<int >>(" rng::find_end(list<int>) (bail 25%)" , std::ranges::find_end);
78
- bm.operator ()<std::forward_list<int >>(" rng::find_end(forward_list<int>) (bail 25%)" , std::ranges::find_end);
79
-
80
- // {std,ranges}::find_end(it1, it1, it2, it2, pred)
81
- bm.operator ()<std::vector<int >>(" std::find_end(vector<int>, pred) (bail 25%)" , std_find_end_pred);
82
- bm.operator ()<std::deque<int >>(" std::find_end(deque<int>, pred) (bail 25%)" , std_find_end_pred);
83
- bm.operator ()<std::list<int >>(" std::find_end(list<int>, pred) (bail 25%)" , std_find_end_pred);
84
- bm.operator ()<std::forward_list<int >>(" std::find_end(forward_list<int>, pred) (bail 25%)" , std_find_end_pred);
85
- bm.operator ()<std::vector<int >>(" rng::find_end(vector<int>, pred) (bail 25%)" , ranges_find_end_pred);
86
- bm.operator ()<std::deque<int >>(" rng::find_end(deque<int>, pred) (bail 25%)" , ranges_find_end_pred);
87
- bm.operator ()<std::list<int >>(" rng::find_end(list<int>, pred) (bail 25%)" , ranges_find_end_pred);
88
- bm.operator ()<std::forward_list<int >>(" rng::find_end(forward_list<int>, pred) (bail 25%)" , ranges_find_end_pred);
96
+ register_benchmarks (bm, " process all" );
89
97
}
90
98
91
- // Benchmark {std,ranges}::find_end where the subsequence is found
92
- // 90% into the sequence (i.e. near the end)
99
+ // Benchmark {std,ranges}::find_end where we intersperse "near matches" inside the haystack.
93
100
{
94
101
auto bm = []<class Container >(std::string name, auto find_end) {
95
102
benchmark::RegisterBenchmark (
@@ -99,43 +106,117 @@ int main(int argc, char** argv) {
99
106
using ValueType = typename Container::value_type;
100
107
ValueType x = Generate<ValueType>::random ();
101
108
ValueType y = random_different_from ({x});
102
- Container c (size, x);
103
- Container subrange (size / 10 , y); // subrange of length 10% of the full range
109
+ Container haystack (size, x);
110
+ std::size_t n = size / 10 ; // needle size is 10% of the haystack
111
+ assert (n > 0 );
112
+ Container needle (n, y);
104
113
105
- // put the element we're searching for at 90% of the sequence
106
- std::ranges::copy (subrange, std::next (c.begin (), (9 * size) / 10 ));
114
+ // intersperse near-matches inside the haystack
115
+ {
116
+ auto first = haystack.begin ();
117
+ for (int i = 0 ; i != 10 ; ++i) {
118
+ first = std::copy_n (needle.begin (), n - 1 , first);
119
+ ++first; // this causes the subsequence not to match because it has length n-1
120
+ }
121
+ }
107
122
108
123
for ([[maybe_unused]] auto _ : st) {
109
- benchmark::DoNotOptimize (c );
110
- benchmark::DoNotOptimize (subrange );
111
- auto result = find_end (c .begin (), c .end (), subrange .begin (), subrange .end ());
124
+ benchmark::DoNotOptimize (haystack );
125
+ benchmark::DoNotOptimize (needle );
126
+ auto result = find_end (haystack .begin (), haystack .end (), needle .begin (), needle .end ());
112
127
benchmark::DoNotOptimize (result);
113
128
}
114
129
})
115
130
->Arg (1000 ) // non power-of-two
116
131
->Arg (1024 )
117
- ->Arg (8192 )
118
- ->Arg (1 << 20 );
132
+ ->Arg (8192 );
119
133
};
120
- // {std,ranges}::find_end(it1, it1, it2, it2)
121
- bm.operator ()<std::vector<int >>(" std::find_end(vector<int>) (bail 90%)" , std_find_end);
122
- bm.operator ()<std::deque<int >>(" std::find_end(deque<int>) (bail 90%)" , std_find_end);
123
- bm.operator ()<std::list<int >>(" std::find_end(list<int>) (bail 90%)" , std_find_end);
124
- bm.operator ()<std::forward_list<int >>(" std::find_end(forward_list<int>) (bail 90%)" , std_find_end);
125
- bm.operator ()<std::vector<int >>(" rng::find_end(vector<int>) (bail 90%)" , std::ranges::find_end);
126
- bm.operator ()<std::deque<int >>(" rng::find_end(deque<int>) (bail 90%)" , std::ranges::find_end);
127
- bm.operator ()<std::list<int >>(" rng::find_end(list<int>) (bail 90%)" , std::ranges::find_end);
128
- bm.operator ()<std::forward_list<int >>(" rng::find_end(forward_list<int>) (bail 90%)" , std::ranges::find_end);
134
+ register_benchmarks (bm, " near matches" );
135
+ }
129
136
130
- // {std,ranges}::find_end(it1, it1, it2, it2, pred)
131
- bm.operator ()<std::vector<int >>(" std::find_end(vector<int>, pred) (bail 90%)" , std_find_end_pred);
132
- bm.operator ()<std::deque<int >>(" std::find_end(deque<int>, pred) (bail 90%)" , std_find_end_pred);
133
- bm.operator ()<std::list<int >>(" std::find_end(list<int>, pred) (bail 90%)" , std_find_end_pred);
134
- bm.operator ()<std::forward_list<int >>(" std::find_end(forward_list<int>, pred) (bail 90%)" , std_find_end_pred);
135
- bm.operator ()<std::vector<int >>(" rng::find_end(vector<int>, pred) (bail 90%)" , ranges_find_end_pred);
136
- bm.operator ()<std::deque<int >>(" rng::find_end(deque<int>, pred) (bail 90%)" , ranges_find_end_pred);
137
- bm.operator ()<std::list<int >>(" rng::find_end(list<int>, pred) (bail 90%)" , ranges_find_end_pred);
138
- bm.operator ()<std::forward_list<int >>(" rng::find_end(forward_list<int>, pred) (bail 90%)" , ranges_find_end_pred);
137
+ // Special case: the two ranges are the same length (and they are equal, which is the worst case).
138
+ {
139
+ auto bm = []<class Container >(std::string name, auto find_end) {
140
+ benchmark::RegisterBenchmark (
141
+ name,
142
+ [find_end](auto & st) {
143
+ std::size_t const size = st.range (0 );
144
+ using ValueType = typename Container::value_type;
145
+ ValueType x = Generate<ValueType>::random ();
146
+ Container haystack (size, x);
147
+ Container needle (size, x);
148
+
149
+ for (auto _ : st) {
150
+ benchmark::DoNotOptimize (haystack);
151
+ benchmark::DoNotOptimize (needle);
152
+ auto result = find_end (haystack.begin (), haystack.end (), needle.begin (), needle.end ());
153
+ benchmark::DoNotOptimize (result);
154
+ }
155
+ })
156
+ ->Arg (1000 ) // non power-of-two
157
+ ->Arg (1024 )
158
+ ->Arg (8192 );
159
+ };
160
+ register_benchmarks (bm, " same length" );
161
+ }
162
+
163
+ // Special case: the needle contains a single element (which we never find, i.e. the worst case).
164
+ {
165
+ auto bm = []<class Container >(std::string name, auto find_end) {
166
+ benchmark::RegisterBenchmark (
167
+ name,
168
+ [find_end](auto & st) {
169
+ std::size_t const size = st.range (0 );
170
+ using ValueType = typename Container::value_type;
171
+ ValueType x = Generate<ValueType>::random ();
172
+ ValueType y = random_different_from ({x});
173
+ Container haystack (size, x);
174
+ Container needle (1 , y);
175
+
176
+ for (auto _ : st) {
177
+ benchmark::DoNotOptimize (haystack);
178
+ benchmark::DoNotOptimize (needle);
179
+ auto result = find_end (haystack.begin (), haystack.end (), needle.begin (), needle.end ());
180
+ benchmark::DoNotOptimize (result);
181
+ }
182
+ })
183
+ ->Arg (1000 ) // non power-of-two
184
+ ->Arg (1024 )
185
+ ->Arg (8192 );
186
+ };
187
+ register_benchmarks (bm, " single element" );
188
+ }
189
+
190
+ // Special case: we have a match close to the end of the haystack (ideal case if we start searching from the end).
191
+ {
192
+ auto bm = []<class Container >(std::string name, auto find_end) {
193
+ benchmark::RegisterBenchmark (
194
+ name,
195
+ [find_end](auto & st) {
196
+ std::size_t const size = st.range (0 );
197
+ using ValueType = typename Container::value_type;
198
+ ValueType x = Generate<ValueType>::random ();
199
+ ValueType y = random_different_from ({x});
200
+ Container haystack (size, x);
201
+ std::size_t n = size / 10 ; // needle size is 10% of the haystack
202
+ assert (n > 0 );
203
+ Container needle (n, y);
204
+
205
+ // put the needle at 90% of the haystack
206
+ std::ranges::copy (needle, std::next (haystack.begin (), (9 * size) / 10 ));
207
+
208
+ for (auto _ : st) {
209
+ benchmark::DoNotOptimize (haystack);
210
+ benchmark::DoNotOptimize (needle);
211
+ auto result = find_end (haystack.begin (), haystack.end (), needle.begin (), needle.end ());
212
+ benchmark::DoNotOptimize (result);
213
+ }
214
+ })
215
+ ->Arg (1000 ) // non power-of-two
216
+ ->Arg (1024 )
217
+ ->Arg (8192 );
218
+ };
219
+ register_benchmarks (bm, " match near end" );
139
220
}
140
221
141
222
benchmark::Initialize (&argc, argv);
0 commit comments