[libcxx-commits] [libcxx] 3396be2 - [libc++] Optimize vector growth for nothrow move constructible types (#214690)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 25 01:25:19 PDT 2026


Author: Nikolas Klauser
Date: 2026-08-25T10:25:13+02:00
New Revision: 3396be2daea07ceee6a2caa74e01ab0faca227d6

URL: https://github.com/llvm/llvm-project/commit/3396be2daea07ceee6a2caa74e01ab0faca227d6
DIFF: https://github.com/llvm/llvm-project/commit/3396be2daea07ceee6a2caa74e01ab0faca227d6.diff

LOG: [libc++] Optimize vector growth for nothrow move constructible types (#214690)

The optimization is to perform move-and-destroy in a single pass as
opposed to moving everything, and then destroying everything. Performing
move-and-destroy in one pass allows the compiler to better optimize the
combination of move and destroy, which can often be simplified.

Added: 
    

Modified: 
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index e1c6fe25040ea..0b683e59c7902 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -25,6 +25,7 @@
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
@@ -176,7 +177,7 @@ uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __las
 template <class _ValueType, class _ForwardIterator, class _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
-  auto __idx = __first;
+  auto __idx   = __first;
   auto __guard = std::__make_exception_guard([&] { std::__destroy(__first, __idx); });
   for (; __n > 0; ++__idx, (void)--__n)
     ::new (static_cast<void*>(std::addressof(*__idx))) _ValueType;
@@ -218,7 +219,7 @@ uninitialized_value_construct(_ForwardIterator __first, _ForwardIterator __last)
 template <class _ValueType, class _ForwardIterator, class _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 __uninitialized_value_construct_n(_ForwardIterator __first, _Size __n) {
-  auto __idx = __first;
+  auto __idx   = __first;
   auto __guard = std::__make_exception_guard([&] { std::__destroy(__first, __idx); });
   for (; __n > 0; ++__idx, (void)--__n)
     ::new (static_cast<void*>(std::addressof(*__idx))) _ValueType();
@@ -402,32 +403,46 @@ template <class _Alloc, class _ContiguousIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __uninitialized_allocator_relocate(
     _Alloc& __alloc, _ContiguousIterator __first, _ContiguousIterator __last, _ContiguousIterator __result) {
   static_assert(__libcpp_is_contiguous_iterator<_ContiguousIterator>::value, "");
-  using _ValueType = typename iterator_traits<_ContiguousIterator>::value_type;
   static_assert(
       __is_cpp17_move_insertable_v<_Alloc>, "The specified type does not meet the requirements of Cpp17MoveInsertable");
-  if (__libcpp_is_constant_evaluated() || !__is_trivially_relocatable_v<_ValueType> ||
-      !__allocator_has_trivial_move_construct_v<_Alloc, _ValueType> ||
-      !__allocator_has_trivial_destroy_v<_Alloc, _ValueType>) {
+
+  using __value_type = typename iterator_traits<_ContiguousIterator>::value_type;
+
+  if _LIBCPP_CONSTEXPR (__allocator_has_trivial_move_construct_v<_Alloc, __value_type> &&
+                        __allocator_has_trivial_destroy_v<_Alloc, __value_type> &&
+                        __is_trivially_relocatable_v<__value_type>) {
+    if (!__libcpp_is_constant_evaluated()) {
+      // Casting to void* to suppress clang complaining that this is technically UB.
+      __builtin_memcpy(static_cast<void*>(std::__to_address(__result)),
+                       std::__to_address(__first),
+                       sizeof(__value_type) * (__last - __first));
+      return;
+    }
+  }
+
+  using __alloc_traits = allocator_traits<_Alloc>;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (!_LIBCPP_HAS_EXCEPTIONS || is_nothrow_move_constructible<__value_type>::value) {
+    while (__first != __last) {
+      __alloc_traits::construct(__alloc, std::__to_address(__result), std::move(*__first));
+      __alloc_traits::destroy(__alloc, std::__to_address(__first));
+      ++__first;
+      ++__result;
+    }
+  } else
+#endif
+  {
     auto __destruct_first = __result;
     auto __guard          = std::__make_exception_guard(
         _AllocatorDestroyRangeReverse<_Alloc, _ContiguousIterator>(__alloc, __destruct_first, __result));
     auto __iter = __first;
     while (__iter != __last) {
-#if _LIBCPP_HAS_EXCEPTIONS
       allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move_if_noexcept(*__iter));
-#else
-      allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move(*__iter));
-#endif
       ++__iter;
       ++__result;
     }
     __guard.__complete();
     std::__allocator_destroy(__alloc, __first, __last);
-  } else {
-    // Casting to void* to suppress clang complaining that this is technically UB.
-    __builtin_memcpy(static_cast<void*>(std::__to_address(__result)),
-                     std::__to_address(__first),
-                     sizeof(_ValueType) * (__last - __first));
   }
 }
 

diff  --git a/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp b/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
index 599db1d90fa9a..a62a75b68b1bf 100644
--- a/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
+++ b/libcxx/test/benchmarks/containers/sequence/vector.bench.cpp
@@ -14,9 +14,23 @@
 #include "sequence_container_benchmarks.h"
 #include "benchmark/benchmark.h"
 
+struct NothrowMoveConstructible {
+  std::string s;
+};
+
+template <>
+struct Generate<NothrowMoveConstructible> {
+  static NothrowMoveConstructible arbitrary() { return {Generate<std::string>::arbitrary()}; }
+  static NothrowMoveConstructible cheap() { return {Generate<std::string>::cheap()}; }
+  static NothrowMoveConstructible expensive() { return {Generate<std::string>::expensive()}; }
+  static NothrowMoveConstructible random() { return {Generate<std::string>::random()}; }
+};
+
 int main(int argc, char** argv) {
   support::sequence_container_benchmarks<std::vector<int>>("std::vector<int>");
   support::sequence_container_benchmarks<std::vector<std::string>>("std::vector<std::string>");
+  support::sequence_container_benchmarks<std::vector<NothrowMoveConstructible>>(
+      "std::vector<NothrowMoveConstructible>");
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();


        


More information about the libcxx-commits mailing list