-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[libc++] Optimize map::insert_or_assign #155816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
2dbac71
to
61e7688
Compare
if constexpr (is_multi_key_container) { | ||
st.PauseTiming(); | ||
for (std::size_t i = 0; i != BatchSize; ++i) { | ||
c[i].erase(inserted[i]); | ||
} | ||
st.ResumeTiming(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is always false.
if constexpr (is_multi_key_container) { | |
st.PauseTiming(); | |
for (std::size_t i = 0; i != BatchSize; ++i) { | |
c[i].erase(inserted[i]); | |
} | |
st.ResumeTiming(); | |
} |
@llvm/pr-subscribers-libcxx Author: Nikolas Klauser (philnik777) Changes
Full diff: https://github.com/llvm/llvm-project/pull/155816.diff 3 Files Affected:
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 <class _Vp>
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool> 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 <class _Vp>
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool> 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 <class _Vp>
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 535a52f0a08ab..3daa7e1843f5c 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -259,6 +259,57 @@ 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<Value> in = make_value_types(generate_unique_keys(size));
+ Value to_insert = in[in.size() / 2]; // pick any existing value
+ std::vector<Container> 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<Container>::get_iterator(c[i].insert_or_assign(to_insert.first, to_insert.second));
+ benchmark::DoNotOptimize(inserted[i]);
+ benchmark::DoNotOptimize(c[i]);
+ benchmark::ClobberMemory();
+ }
+
+ if constexpr (is_multi_key_container) {
+ st.PauseTiming();
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ c[i].erase(inserted[i]);
+ }
+ st.ResumeTiming();
+ }
+ }
+ });
+
+ bench("insert_or_assign(key, value) (new value)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + 1));
+ Value to_insert = in.back();
+ in.pop_back();
+ std::vector<Container> 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) {
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a minor comment, and also please add a short explanation of the optimization in the commit message. This is great!
Uh oh!
There was an error while loading. Please reload this page.