diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 4ad5452cc29cc..cd8171dc5d1e8 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -57,6 +57,7 @@ Improvements and New Features has been improved by up to 3x - The performance of ``insert(iterator, iterator)`` of ``multimap`` and ``multiset`` has been improved by up to 2.5x - The performance of ``erase(iterator, iterator)`` in the unordered containers has been improved by up to 1.9x +- The performance of ``map::insert_or_assign`` has been improved by up to 2x - ``ofstream::write`` has been optimized to pass through large strings to system calls directly instead of copying them in chunks into a buffer. diff --git a/libcxx/include/map b/libcxx/include/map index b53e1c4213487..395b434fe4a5b 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1144,22 +1144,20 @@ public: template _LIBCPP_HIDE_FROM_ABI pair insert_or_assign(const key_type& __k, _Vp&& __v) { - iterator __p = lower_bound(__k); - if (__p != end() && !key_comp()(__k, __p->first)) { - __p->second = std::forward<_Vp>(__v); - return std::make_pair(__p, false); - } - return std::make_pair(emplace_hint(__p, __k, std::forward<_Vp>(__v)), true); + auto __result = __tree_.__emplace_unique(__k, std::forward<_Vp>(__v)); + auto& [__iter, __inserted] = __result; + if (!__inserted) + __iter->second = std::forward<_Vp>(__v); + return __result; } template _LIBCPP_HIDE_FROM_ABI pair insert_or_assign(key_type&& __k, _Vp&& __v) { - iterator __p = lower_bound(__k); - if (__p != end() && !key_comp()(__k, __p->first)) { - __p->second = std::forward<_Vp>(__v); - return std::make_pair(__p, false); - } - return std::make_pair(emplace_hint(__p, std::move(__k), std::forward<_Vp>(__v)), true); + auto __result = __tree_.__emplace_unique(std::move(__k), std::forward<_Vp>(__v)); + auto& [__iter, __inserted] = __result; + if (!__inserted) + __iter->second = std::forward<_Vp>(__v); + return __result; } template diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h index 535a52f0a08ab..b39569812640b 100644 --- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h +++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h @@ -259,6 +259,49 @@ void associative_container_benchmarks(std::string container) { } }); + if constexpr (is_map_like && !is_multi_key_container) { + bench("insert_or_assign(key, value) (already present)", [=](auto& st) { + const std::size_t size = st.range(0) ? st.range(0) : 1; + std::vector in = make_value_types(generate_unique_keys(size)); + Value to_insert = in[in.size() / 2]; // pick any existing value + std::vector c(BatchSize, Container(in.begin(), in.end())); + typename Container::iterator inserted[BatchSize]; + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + inserted[i] = + adapt_operations::get_iterator(c[i].insert_or_assign(to_insert.first, to_insert.second)); + benchmark::DoNotOptimize(inserted[i]); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + } + }); + + bench("insert_or_assign(key, value) (new value)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = make_value_types(generate_unique_keys(size + 1)); + Value to_insert = in.back(); + in.pop_back(); + std::vector c(BatchSize, Container(in.begin(), in.end())); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + auto result = c[i].insert_or_assign(to_insert.first, to_insert.second); + benchmark::DoNotOptimize(result); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].erase(get_key(to_insert)); + } + st.ResumeTiming(); + } + }); + } + // The insert(hint, ...) methods are only relevant for ordered containers, and we lack // a good way to compute a hint for unordered ones. if constexpr (is_ordered_container) {