[libcxx-commits] [libcxx] d4205a3 - [libc++] Optimize vector::erase (#213916)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 25 02:46:30 PDT 2026
Author: Nikolas Klauser
Date: 2026-08-25T11:46:25+02:00
New Revision: d4205a3ba03b59d0ff43c57b1cb80c06b93ca1a4
URL: https://github.com/llvm/llvm-project/commit/d4205a3ba03b59d0ff43c57b1cb80c06b93ca1a4
DIFF: https://github.com/llvm/llvm-project/commit/d4205a3ba03b59d0ff43c57b1cb80c06b93ca1a4.diff
LOG: [libc++] Optimize vector::erase (#213916)
This optimization is for trivially relocatable types. Instead of move
assigning the tail and then destroying the last N elements, we now
destroy the elements that are erased and then relocate the tail forward.
Added:
Modified:
libcxx/include/__memory/uninitialized_algorithms.h
libcxx/include/__vector/vector.h
Removed:
################################################################################
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 0b683e59c7902..fb648d7b355a3 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -303,6 +303,40 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
#endif // _LIBCPP_STD_VER >= 17
+// __uninitialized_relocate 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 _ContiguousIterator>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
+__uninitialized_relocate(_ContiguousIterator __first, _ContiguousIterator __last, _ContiguousIterator __result) {
+ static_assert(__libcpp_is_contiguous_iterator<_ContiguousIterator>::value, "");
+ using _ValueType = typename iterator_traits<_ContiguousIterator>::value_type;
+
+ // TODO: Accept non-trivially relocatable types
+ static_assert(__is_trivially_relocatable_v<_ValueType>);
+
+ if (__libcpp_is_constant_evaluated()) {
+ auto __iter = __first;
+ while (__iter != __last) {
+ std::__construct_at(std::__to_address(__result), std::move(*__iter));
+ std::__destroy_at(std::__to_address(__iter));
+ ++__iter;
+ ++__result;
+ }
+ } 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));
+ }
+}
+
template <class _Alloc, class _Iter>
class _AllocatorDestroyRangeReverse {
public:
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 70e42e8204687..ebcfcc17d4b6d 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -558,8 +558,39 @@ class _LIBCPP_WARN_UNUSED vector {
}
#endif
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __position);
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last);
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __position) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __position != end(), "vector::erase(iterator) called with a non-dereferenceable iterator");
+ return erase(__position, __position + 1);
+ }
+
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
+ _LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "vector::erase(first, last) called with invalid range");
+ pointer __pos = this->__layout_.__begin_ptr() + (__first - begin());
+ auto __hole_size = __last - __first;
+ if (__first != __last) {
+#ifndef _LIBCPP_CXX03_LANG
+ // If the value_type is trivially relocatable, we destroy the range being erased and we relocate the tail
+ // of the vector into the created gap. Otherwise, we use the standard technique with move-assignments.
+ //
+ // We really only need is_nothrow_relocatable, but vector::erase is currently overspecified to use assignment.
+ // We use is_trivially_relocatable unil that is relaxed.
+ if constexpr (__is_trivially_relocatable_v<value_type> &&
+ __allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+ __allocator_has_trivial_destroy_v<allocator_type, value_type>) {
+ auto __raw_pos = std::__to_address(__pos);
+ auto __old_size = size();
+ std::__destroy(__raw_pos, __raw_pos + __hole_size);
+ std::__uninitialized_relocate(__raw_pos + __hole_size, std::__to_address(__layout_.__end_ptr()), __raw_pos);
+ __layout_.__set_bound_using_pointer(__layout_.__end_ptr() - __hole_size);
+ __annotate_shrink(__old_size);
+ return __make_iter(__pos);
+ }
+#endif
+ this->__destruct_at_end(std::move(__pos + __hole_size, __layout_.__end_ptr(), __pos));
+ }
+ return __make_iter(__pos);
+ }
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT {
__destruct_at_end(__layout_.__begin_ptr());
@@ -1090,28 +1121,6 @@ vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
#endif
}
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::erase(const_iterator __position) {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __position != end(), "vector::erase(iterator) called with a non-dereferenceable iterator");
-
diff erence_type __ps = __position - cbegin();
- pointer __p = this->__layout_.__begin_ptr() + __ps;
- this->__destruct_at_end(std::move(__p + 1, __layout_.__end_ptr(), __p));
- return __make_iter(__p);
-}
-
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::erase(const_iterator __first, const_iterator __last) {
- _LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "vector::erase(first, last) called with invalid range");
- pointer __p = this->__layout_.__begin_ptr() + (__first - begin());
- if (__first != __last) {
- this->__destruct_at_end(std::move(__p + (__last - __first), __layout_.__end_ptr(), __p));
- }
- return __make_iter(__p);
-}
-
template <class _Tp, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 void
vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
More information about the libcxx-commits
mailing list