[libcxx-commits] [libcxx] [libc++] Optimize multi{map, set}::insert (PR #152691)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Aug 8 04:27:58 PDT 2025


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/152691

```
--------------------------------------------------------------------------------------------------------------------
Benchmark                                                                                        old             new
--------------------------------------------------------------------------------------------------------------------
std::multiset<int>::ctor(iterator, iterator) (unsorted sequence)/0                           14.8 ns         16.0 ns
std::multiset<int>::ctor(iterator, iterator) (unsorted sequence)/32                           513 ns          536 ns
std::multiset<int>::ctor(iterator, iterator) (unsorted sequence)/1024                       50523 ns        49099 ns
std::multiset<int>::ctor(iterator, iterator) (unsorted sequence)/8192                      712956 ns       773317 ns
std::multiset<int>::ctor(iterator, iterator) (sorted sequence)/0                             14.6 ns         16.0 ns
std::multiset<int>::ctor(iterator, iterator) (sorted sequence)/32                             505 ns          404 ns
std::multiset<int>::ctor(iterator, iterator) (sorted sequence)/1024                         24907 ns        14673 ns
std::multiset<int>::ctor(iterator, iterator) (sorted sequence)/8192                        317355 ns       115852 ns
std::multiset<int>::insert(iterator, iterator) (all new keys)/0                               449 ns          458 ns
std::multiset<int>::insert(iterator, iterator) (all new keys)/32                             1036 ns          965 ns
std::multiset<int>::insert(iterator, iterator) (all new keys)/1024                          29504 ns        20190 ns
std::multiset<int>::insert(iterator, iterator) (all new keys)/8192                         313616 ns       150854 ns
std::multiset<int>::insert(iterator, iterator) (half new keys)/0                              456 ns          459 ns
std::multiset<int>::insert(iterator, iterator) (half new keys)/32                            1013 ns          971 ns
std::multiset<int>::insert(iterator, iterator) (half new keys)/1024                         41227 ns        24008 ns
std::multiset<int>::insert(iterator, iterator) (half new keys)/8192                        478994 ns       343475 ns
std::multiset<int>::erase(iterator, iterator) (erase half the container)/0                    459 ns          456 ns
std::multiset<int>::erase(iterator, iterator) (erase half the container)/32                   732 ns          720 ns
std::multiset<int>::erase(iterator, iterator) (erase half the container)/1024               10027 ns         8581 ns
std::multiset<int>::erase(iterator, iterator) (erase half the container)/8192               74175 ns        65850 ns
std::multiset<std::string>::ctor(iterator, iterator) (unsorted sequence)/0                   15.2 ns         17.1 ns
std::multiset<std::string>::ctor(iterator, iterator) (unsorted sequence)/32                  3093 ns         3015 ns
std::multiset<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024              164606 ns       178851 ns
std::multiset<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192             2239105 ns      2458905 ns
std::multiset<std::string>::ctor(iterator, iterator) (sorted sequence)/0                     15.1 ns         16.9 ns
std::multiset<std::string>::ctor(iterator, iterator) (sorted sequence)/32                    1293 ns         2497 ns
std::multiset<std::string>::ctor(iterator, iterator) (sorted sequence)/1024                 94936 ns        68618 ns
std::multiset<std::string>::ctor(iterator, iterator) (sorted sequence)/8192               1252379 ns      1136337 ns
std::multiset<std::string>::insert(iterator, iterator) (all new keys)/0                       456 ns          463 ns
std::multiset<std::string>::insert(iterator, iterator) (all new keys)/32                     2024 ns         1807 ns
std::multiset<std::string>::insert(iterator, iterator) (all new keys)/1024                 159767 ns       105691 ns
std::multiset<std::string>::insert(iterator, iterator) (all new keys)/8192                1424748 ns       762697 ns
std::multiset<std::string>::insert(iterator, iterator) (half new keys)/0                      461 ns          458 ns
std::multiset<std::string>::insert(iterator, iterator) (half new keys)/32                    2013 ns         2040 ns
std::multiset<std::string>::insert(iterator, iterator) (half new keys)/1024                166583 ns       141373 ns
std::multiset<std::string>::insert(iterator, iterator) (half new keys)/8192               1566078 ns      1358940 ns
std::multiset<std::string>::erase(iterator, iterator) (erase half the container)/0            454 ns          454 ns
std::multiset<std::string>::erase(iterator, iterator) (erase half the container)/32           841 ns          813 ns
std::multiset<std::string>::erase(iterator, iterator) (erase half the container)/1024       18878 ns        20836 ns
std::multiset<std::string>::erase(iterator, iterator) (erase half the container)/8192      136735 ns       135926 ns
```


>From 1ab03110adbd078ed636896f0a658862d6d767a9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 8 Aug 2025 12:12:52 +0200
Subject: [PATCH] [libc++] Optimize multi{map,set}::insert

---
 libcxx/include/__tree | 29 +++++++++++++++++++++++++++++
 libcxx/include/map    | 11 ++++-------
 libcxx/include/set    | 11 ++++-------
 3 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 3dd5ae585e1db..fb8f084a76b33 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -995,6 +995,35 @@ public:
     __emplace_hint_multi(__p, std::move(__value));
   }
 
+  template <class _InIter, class _Sent>
+  void __insert_range_multi(_InIter __first, _Sent __last) {
+    if (__first == __last)
+      return;
+
+    if (__root() == nullptr) { // Make sure we always have a root node
+      __node_holder __node = __construct_node(*__first);
+      __end_node()->__left_ = static_cast<__node_base_pointer>(__node.release());
+      __insert_node_at(__end_node(), __end_node()->__left_);
+      ++__first;
+    }
+
+    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    __node_pointer __last_insertion = __root();
+
+    for (; __first != __last; ++__first) {
+      __node_holder __node = __construct_node(*__first);
+      if (!value_comp()(__node->__get_value(), __max_node->__get_value())) { // __node >= __max_val
+        __insert_node_at(__max_node, __max_node->__right_, __node.get());
+        __max_node = __node.release();
+      } else {
+        __end_node_pointer __parent;
+        __node_base_pointer& __child = __find_leaf(++iterator(__last_insertion), __parent, __node->__value_);
+        __last_insertion             = __node.get();
+        __insert_node_at(__parent, __child, __node.release());
+      }
+    }
+  }
+
   _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __node_assign_unique(const value_type& __v, __node_pointer __dest);
 
   _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(__node_pointer __nd);
diff --git a/libcxx/include/map b/libcxx/include/map
index 6378218945ca0..eb833a26d9ae3 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -593,6 +593,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #  include <__memory/unique_ptr.h>
 #  include <__memory_resource/polymorphic_allocator.h>
 #  include <__node_handle>
+#  include <__ranges/access.h>
 #  include <__ranges/concepts.h>
 #  include <__ranges/container_compatible_range.h>
 #  include <__ranges/from_range.h>
@@ -1088,18 +1089,14 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __f, _InputIterator __l) {
-    for (const_iterator __e = cend(); __f != __l; ++__f)
-      insert(__e.__i_, *__f);
+  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+    __tree_.__insert_range_unique(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
   _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
-    const_iterator __end = cend();
-    for (auto&& __element : __range) {
-      insert(__end.__i_, std::forward<decltype(__element)>(__element));
-    }
+    __tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
diff --git a/libcxx/include/set b/libcxx/include/set
index c77345bc5dc1f..18488b56e7dd2 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -530,6 +530,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #  include <__memory/allocator_traits.h>
 #  include <__memory_resource/polymorphic_allocator.h>
 #  include <__node_handle>
+#  include <__ranges/access.h>
 #  include <__ranges/concepts.h>
 #  include <__ranges/container_compatible_range.h>
 #  include <__ranges/from_range.h>
@@ -1205,18 +1206,14 @@ public:
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __f, _InputIterator __l) {
-    for (const_iterator __e = cend(); __f != __l; ++__f)
-      __tree_.__emplace_hint_multi(__e, *__f);
+  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+    __tree_.__insert_range_multi(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
   _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
-    const_iterator __end = cend();
-    for (auto&& __element : __range) {
-      __tree_.__emplace_hint_multi(__end, std::forward<decltype(__element)>(__element));
-    }
+    __tree_.__insert_range_multi(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 



More information about the libcxx-commits mailing list