[libcxx-commits] [libcxx] [libc++] Optimize vector::emplace_back slow path (PR #222016)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Sep 9 04:44:34 PDT 2026


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

>From 3794dbbd07b35b35d6b7419ec186cdba355b933e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 8 Sep 2026 16:42:49 +0200
Subject: [PATCH] [libc++] Optimize vector::emplace_back slow path

---
 .../include/__numeric/saturation_arithmetic.h |  7 +++--
 libcxx/include/__vector/vector.h              | 30 ++++++++++++-------
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 34f784e4a9cf1..4b09815df67fe 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -26,10 +26,9 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20
-
-template <__signed_or_unsigned_integer _Tp>
+template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_add(_Tp __x, _Tp __y) noexcept {
+  static_assert(__is_signed_integer_v<_Tp> || __is_unsigned_integer_v<_Tp>);
 #  if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2101
   return __builtin_elementwise_add_sat(__x, __y);
 #  else
@@ -50,6 +49,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_add(_Tp __x, _Tp __y) noexcept
 #  endif
 }
 
+#if _LIBCPP_STD_VER >= 20
+
 template <__signed_or_unsigned_integer _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_sub(_Tp __x, _Tp __y) noexcept {
 #  if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2101
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index d37a0c21ebcd5..9e50c6fb94eaf 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -41,6 +41,7 @@
 #include <__memory/swap_allocator.h>
 #include <__memory/temp_value.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__numeric/saturation_arithmetic.h>
 #include <__ranges/access.h>
 #include <__ranges/as_rvalue_view.h>
 #include <__ranges/concepts.h>
@@ -889,9 +890,8 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
   if (__new_size > __ms)
     this->__throw_length_error();
   const size_type __cap = capacity();
-  if (__cap >= __ms / 2)
-    return __ms;
-  return std::max<size_type>(2 * __cap, __new_size);
+  auto __doubled = std::__saturating_add(__cap, __cap);
+  return std::min(std::max(__new_size, __doubled), __ms);
 }
 
 //  Default constructs __n objects starting at __layout_.__end_ptr()
@@ -1068,13 +1068,23 @@ vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
         __self.__emplace_back_assume_capacity(std::forward<_Args>(__largs)...);
       },
       [](vector& __self, _Args&&... __largs) static {
-        _SplitBuffer __v(__self.__recommend(__self.size() + 1), __self.size(), __self.__layout_.__alloc());
-        //    __v.emplace_back(std::forward<_Args>(__args)...);
-        pointer __end = __v.end();
-        __alloc_traits::construct(
-            __self.__layout_.__alloc(), std::__to_address(__end), std::forward<_Args>(__largs)...);
-        __v.__set_sentinel(++__end);
-        __self.__layout_.__relocate(__v);
+        auto& __alloc     = __self.__layout_.__alloc();
+        auto __size       = __self.size();
+        auto __allocation = std::__allocate_at_least(__alloc, __self.__recommend(__size + 1));
+        auto __ptr        = std::__to_address(__allocation.ptr);
+        auto __old_begin  = __self.__layout_.__begin_ptr();
+        __alloc_traits::construct(__alloc, __ptr + __size, std::forward<_Args>(__largs)...);
+        auto __guard = std::__make_exception_guard([&] { __alloc_traits::destroy(__alloc, __ptr + __size); });
+        std::__uninitialized_allocator_relocate(
+            __alloc,
+            std::__to_address(__old_begin),
+            std::__to_address(__self.__layout_.__end_ptr()),
+            __ptr);
+        __guard.__complete();
+        auto __old_cap = __self.capacity();
+        __self.__layout_.__set_layout(__allocation.ptr, __size + 1, __allocation.count);
+        if ((__is_std_allocator_v<allocator_type> && !__libcpp_is_constant_evaluated()) || __old_begin)
+          __alloc_traits::deallocate(__alloc, __old_begin, __old_cap);
       },
       *this,
       std::forward<_Args>(__args)...);



More information about the libcxx-commits mailing list