[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