[libcxx-commits] [libcxx] [libc++] Optimize vector::emplace (PR #218665)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 25 05:09:54 PDT 2026


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

None

>From 71aacdfdd6f3c96f254ec58404e89f78c68dd59a Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 25 Aug 2026 14:09:33 +0200
Subject: [PATCH] [libc++] Optimize vector::emplace

---
 .../__memory/uninitialized_algorithms.h       | 38 ++++++++++++++++
 libcxx/include/__type_traits/is_relocatable.h |  7 +++
 libcxx/include/__vector/vector.h              | 43 ++++++++++++++++---
 .../destroy_elements.pass.cpp                 |  2 +
 4 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 82a7a20be4007..5a69632b81cbf 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -337,6 +337,44 @@ __uninitialized_relocate(_ContiguousIterator __first, _ContiguousIterator __last
   }
 }
 
+// __uninitialized_relocate relocates the objects in [__first, __last) into __result element-by-element from __first to
+// __last. Relocation means that the objects in [__first, __last) are placed into __result as-if by move-construct and
+// destroy, except that the move constructor and destructor may never be called if they are known to be equivalent to a
+// memcpy.
+//
+// Preconditions:  At __result there is no object and [__first, __last) contains objects
+// Postconditions: If no exceptions were thrown, __result contains the objects from [__first, __last), otherwise it
+//                 doesn't contain any objects.
+//                 [__first, __last) doesn't contain any objects.
+template <class _ContiguousIterator>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
+__uninitialized_relocate_backward(_ContiguousIterator __first, _ContiguousIterator __last, _ContiguousIterator __result) {
+  static_assert(__libcpp_is_contiguous_iterator<_ContiguousIterator>::value, "");
+  using _ValueType = typename iterator_traits<_ContiguousIterator>::value_type;
+
+  if (__libcpp_is_constant_evaluated() || !__is_trivially_relocatable_v<_ValueType>) {
+    size_t __count = __last - __first;
+    __result += __count;
+    size_t __i = 0;
+
+    auto __guard = std::__make_exception_guard([&] {
+      std::__destroy(__result, __result + __i);
+      std::__destroy(__first, __last);
+    });
+
+    for (; __i != __count; ++__i) {
+      std::__construct_at(std::__to_address(--__result), std::move(*--__last));
+      std::__destroy_at(std::__to_address(__last));
+    }
+    __guard.__complete();
+  } else {
+    // Casting to void* to suppress clang complaining that this is technically UB.
+    __builtin_memmove(static_cast<void*>(std::__to_address(__result)),
+                      std::__to_address(__first),
+                      sizeof(_ValueType) * (__last - __first));
+  }
+}
+
 template <class _Alloc, class _Iter>
 class _AllocatorDestroyRangeReverse {
 public:
diff --git a/libcxx/include/__type_traits/is_relocatable.h b/libcxx/include/__type_traits/is_relocatable.h
index cfbe1966a67b2..e9b6ab53e9047 100644
--- a/libcxx/include/__type_traits/is_relocatable.h
+++ b/libcxx/include/__type_traits/is_relocatable.h
@@ -11,6 +11,8 @@
 
 #include <__config>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_nothrow_destructible.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 
@@ -43,6 +45,11 @@ inline const bool
         true;
 #endif
 
+template <class _Tp>
+inline const bool __is_nothrow_relocatable_v =
+    __is_trivially_relocatable_v<_Tp> ||
+    (is_nothrow_move_constructible<_Tp>::value && is_nothrow_destructible<_Tp>::value);
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_IS_RELOCATABLE_H
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index e23bd8999e48a..7cd497fa946b0 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -36,6 +36,7 @@
 #include <__memory/allocator.h>
 #include <__memory/allocator_traits.h>
 #include <__memory/compressed_pair.h>
+#include <__memory/construct_at.h>
 #include <__memory/noexcept_move_assign_container.h>
 #include <__memory/pointer_traits.h>
 #include <__memory/swap_allocator.h>
@@ -1142,13 +1143,41 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
   if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
-    } else {
-      __temp_value<value_type, _Allocator> __tmp(this->__layout_.__alloc(), std::forward<_Args>(__args)...);
-      __move_range(__p, __end, __p + 1);
-      *__p = std::move(__tmp.get());
+#ifndef _LIBCPP_CXX03_LANG
+    if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                  __allocator_has_trivial_destroy_v<allocator_type, value_type> &&
+                  (__is_nothrow_relocatable_v<value_type> || !is_nothrow_move_assignable<value_type>::value)) {
+      union _Union {
+        constexpr _Union() {}
+        constexpr ~_Union() {}
+
+        value_type __tmp_;
+      } __tmp;
+
+      std::__construct_at(std::addressof(__tmp.__tmp_), std::forward<_Args>(__args)...);
+      auto __raw_pos = std::__to_address(__p);
+
+      auto __tail_guard = std::__make_exception_guard([&] { __layout_.__set_bound_using_pointer(__p); });
+      std::__uninitialized_relocate_backward(__raw_pos, std::__to_address(__layout_.__end_ptr()), __raw_pos + 1);
+      __tail_guard.__complete();
+
+      auto __elem_guard = std::__make_exception_guard([&] {
+        std::__destroy(__raw_pos + 1, std::__to_address(__layout_.__end_ptr() + 1));
+      });
+      std::__uninitialized_relocate_backward(std::addressof(__tmp.__tmp_), std::addressof(__tmp.__tmp_) + 1, __raw_pos);
+      __elem_guard.__complete();
+      __layout_.__set_bound_using_pointer(__layout_.__end_ptr() + 1);
+    } else
+#endif
+    {
+      pointer __end = __layout_.__end_ptr();
+      if (__p == __end) {
+        __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
+      } else {
+        __temp_value<value_type, _Allocator> __tmp(this->__layout_.__alloc(), std::forward<_Args>(__args)...);
+        __move_range(__p, __end, __p + 1);
+        *__p = std::move(__tmp.get());
+      }
     }
   } else {
     _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/destroy_elements.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/destroy_elements.pass.cpp
index 179c5ac955f05..c2f828dd02b33 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/destroy_elements.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/destroy_elements.pass.cpp
@@ -10,6 +10,8 @@
 
 // UNSUPPORTED: c++03
 
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
+
 #include <algorithm>
 #include <array>
 #include <cassert>



More information about the libcxx-commits mailing list