[libcxx-commits] [libcxx] [libc++] Optimize vector::emplace_back slow path (PR #222016)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Sep 22 02:20:45 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/222016
>From 9b96bf3e3d5f53653e93b0aa17d903895fa949de 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 | 11 ++++---
libcxx/include/__vector/vector.h | 31 +++++++++++++------
2 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 34f784e4a9cf10..26fe8f1d57a68f 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -26,17 +26,16 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 20
-
-template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_add(_Tp __x, _Tp __y) noexcept {
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_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
if (_Tp __sum; !__builtin_add_overflow(__x, __y, std::addressof(__sum)))
return __sum;
// Handle overflow
- if constexpr (__unsigned_integer<_Tp>) {
+ if constexpr (__is_unsigned_integer_v<_Tp>) {
return std::numeric_limits<_Tp>::max();
} else {
// Signed addition overflow
@@ -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 a93b890e90de1d..fd2900b9340151 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>
@@ -882,9 +883,9 @@ 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);
+ _LIBCPP_ASSUME(__cap <= __ms);
+ 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()
@@ -1059,13 +1060,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