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

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Oct 8 05:05:36 PDT 2026


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

>From f776f7983c9e757e1e6d7a7d9fac377e6f9323cf 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       | 43 +++++++++++++++++++
 libcxx/include/__type_traits/is_relocatable.h |  7 +++
 libcxx/include/__vector/vector.h              | 28 ++++++++++++
 .../destroy_elements.pass.cpp                 |  2 +
 4 files changed, 80 insertions(+)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index e16331f3d2131..72aecb3cfdb23 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -13,6 +13,7 @@
 #include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__config>
+#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -482,6 +483,48 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __uninitialized_allocat
   }
 }
 
+// __uninitialized_relocate_backward 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 _Alloc, class _ContiguousIterator>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __uninitialized_allocator_relocate_backward(
+    _Alloc& __alloc, _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() || !__allocator_has_trivial_move_construct_v<_Alloc, _ValueType> ||
+      !__allocator_has_trivial_destroy_v<_Alloc, _ValueType> || !__is_trivially_relocatable_v<_ValueType>) {
+    size_t __count = __last - __first;
+    __result += __count;
+    size_t __i = 0;
+
+    using __alloc_traits = allocator_traits<_Alloc>;
+    auto __guard = std::__make_exception_guard([&] {
+      for (size_t __j = 0; __j != __i; ++__j)
+        __alloc_traits::destroy(__alloc, __result);
+      for (; __first != __last; ++__first)
+        __alloc_traits::destroy(__alloc, __first);
+    });
+
+    for (; __i != __count; ++__i) {
+      __alloc_traits::construct(__alloc, std::__to_address(--__result), std::move(*--__last));
+      __alloc_traits::destroy(__alloc, 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));
+  }
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
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 aa8441e3f9554..41e8e1a2312a7 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>
@@ -1112,6 +1113,32 @@ _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()) {
+#ifndef _LIBCPP_CXX03_LANG // In C++03 the _Union trickery isn't allowed
+    union _Union {
+      constexpr _Union() {}
+      _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_Union() {}
+
+      value_type __tmp_;
+    } __tmp;
+
+    auto& __alloc = __layout_.__alloc();
+
+    __alloc_traits::construct(__alloc, 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_allocator_relocate_backward(
+        __alloc, __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_allocator_relocate_backward(
+        __alloc, std::addressof(__tmp.__tmp_), std::addressof(__tmp.__tmp_) + 1, __raw_pos);
+    __elem_guard.__complete();
+    __layout_.__set_bound_using_pointer(__layout_.__end_ptr() + 1);
+#else
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
@@ -1120,6 +1147,7 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
       __move_range(__p, __end, __p + 1);
       *__p = std::move(__tmp.get());
     }
+#endif
   } else {
     _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
     __v.emplace_back(std::forward<_Args>(__args)...);
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