[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