|
13 | 13 | #include <iterator> |
14 | 14 | #include <random> |
15 | 15 | #include <string> |
| 16 | +#include <type_traits> |
| 17 | +#include <utility> |
16 | 18 | #include <vector> |
17 | 19 |
|
18 | 20 | #include "benchmark/benchmark.h" |
@@ -58,6 +60,12 @@ void associative_container_benchmarks(std::string container) { |
58 | 60 | benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(32)->Arg(1024)->Arg(8192); |
59 | 61 | }; |
60 | 62 |
|
| 63 | + static constexpr bool is_multi_key_container = |
| 64 | + !std::is_same_v<typename adapt_operations<Container>::InsertionResult, |
| 65 | + std::pair<typename Container::iterator, bool>>; |
| 66 | + |
| 67 | + static constexpr bool is_ordered_container = requires(Container c, Key k) { c.lower_bound(k); }; |
| 68 | + |
61 | 69 | // These benchmarks are structured to perform the operation being benchmarked |
62 | 70 | // a small number of times at each iteration, in order to offset the cost of |
63 | 71 | // PauseTiming() and ResumeTiming(). |
@@ -168,26 +176,23 @@ void associative_container_benchmarks(std::string container) { |
168 | 176 | std::vector<Value> in = make_value_types(generate_unique_keys(size)); |
169 | 177 | Value to_insert = in[in.size() / 2]; // pick any existing value |
170 | 178 | std::vector<Container> c(BatchSize, Container(in.begin(), in.end())); |
| 179 | + typename Container::iterator inserted[BatchSize]; |
171 | 180 |
|
172 | 181 | while (st.KeepRunningBatch(BatchSize)) { |
173 | 182 | for (std::size_t i = 0; i != BatchSize; ++i) { |
174 | | - auto result = c[i].insert(to_insert); |
175 | | - benchmark::DoNotOptimize(result); |
| 183 | + inserted[i] = adapt_operations<Container>::get_iterator(c[i].insert(to_insert)); |
| 184 | + benchmark::DoNotOptimize(inserted[i]); |
176 | 185 | benchmark::DoNotOptimize(c[i]); |
177 | 186 | benchmark::ClobberMemory(); |
178 | 187 | } |
179 | 188 |
|
180 | | - st.PauseTiming(); |
181 | | - // Unique-key containers will not insert above, but multi-key containers will insert |
182 | | - // the key a second time. Restore the invariant that there's a single copy of each |
183 | | - // key in the container. |
184 | | - for (std::size_t i = 0; i != BatchSize; ++i) { |
185 | | - auto const& key = get_key(to_insert); |
186 | | - if (c[i].count(key) > 1) { |
187 | | - c[i].erase(key); |
| 189 | + if constexpr (is_multi_key_container) { |
| 190 | + st.PauseTiming(); |
| 191 | + for (std::size_t i = 0; i != BatchSize; ++i) { |
| 192 | + c[i].erase(inserted[i]); |
188 | 193 | } |
| 194 | + st.ResumeTiming(); |
189 | 195 | } |
190 | | - st.ResumeTiming(); |
191 | 196 | } |
192 | 197 | }); |
193 | 198 |
|
@@ -216,7 +221,7 @@ void associative_container_benchmarks(std::string container) { |
216 | 221 |
|
217 | 222 | // The insert(hint, ...) methods are only relevant for ordered containers, and we lack |
218 | 223 | // a good way to compute a hint for unordered ones. |
219 | | - if constexpr (requires(Container c, Key k) { c.lower_bound(k); }) { |
| 224 | + if constexpr (is_ordered_container) { |
220 | 225 | bench("insert(hint, value) (good hint)", [=](auto& st) { |
221 | 226 | const std::size_t size = st.range(0); |
222 | 227 | std::vector<Value> in = make_value_types(generate_unique_keys(size + 1)); |
@@ -499,8 +504,7 @@ void associative_container_benchmarks(std::string container) { |
499 | 504 | return c.contains(get_key(element)); |
500 | 505 | })); |
501 | 506 |
|
502 | | - // Only for ordered containers |
503 | | - if constexpr (requires(Container c, Key k) { c.lower_bound(k); }) { |
| 507 | + if constexpr (is_ordered_container) { |
504 | 508 | bench("lower_bound(key) (existent)", bench_with_existent_key([=](Container const& c, Value const& element) { |
505 | 509 | return c.lower_bound(get_key(element)); |
506 | 510 | })); |
|
0 commit comments