* [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h>
@ 2024-06-27 10:39 Jonathan Wakely
2024-06-27 10:39 ` [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset Jonathan Wakely
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Jonathan Wakely @ 2024-06-27 10:39 UTC (permalink / raw)
To: libstdc++, gcc-patches
This refactoring to use RAII doesn't seem to make any difference in
benchmarks, although the generated code for some std::vector operations
seems to be slightly larger. Maybe it will be faster (or slower) in some
cases I didn't test?
I think I like the change anyway - any other opinions on whether it's an
improvement?
Tested x86_64-linux.
-- >8 --
This adds an _UninitDestroyGuard class template, similar to
ranges::_DestroyGuard used in <bits/ranges_uninitialized.h>. This allows
us to remove all the try-catch blocks and rethrows, because any required
cleanup gets done in the guard destructor.
libstdc++-v3/ChangeLog:
* include/bits/stl_uninitialized.h (_UninitDestroyGuard): New
class template and partial specialization.
(__do_uninit_copy, __do_uninit_fill, __do_uninit_fill_n)
(__uninitialized_copy_a, __uninitialized_fill_a)
(__uninitialized_fill_n_a, __uninitialized_copy_move)
(__uninitialized_move_copy, __uninitialized_fill_move)
(__uninitialized_move_fill, __uninitialized_default_1)
(__uninitialized_default_n_a, __uninitialized_default_novalue_1)
(__uninitialized_default_novalue_n_1, __uninitialized_copy_n)
(__uninitialized_copy_n_pair): Use it.
---
libstdc++-v3/include/bits/stl_uninitialized.h | 365 ++++++++----------
1 file changed, 156 insertions(+), 209 deletions(-)
diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
index 3c405d8fbe8..a9965f26269 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -107,24 +107,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__is_trivial(T) && __is_assignable(T&, U)
#endif
+ template<typename _ForwardIterator, typename _Alloc = void>
+ struct _UninitDestroyGuard
+ {
+ _GLIBCXX20_CONSTEXPR
+ explicit
+ _UninitDestroyGuard(_ForwardIterator& __first, _Alloc& __a)
+ : _M_first(__first), _M_cur(__builtin_addressof(__first)), _M_alloc(__a)
+ { }
+
+ _GLIBCXX20_CONSTEXPR
+ ~_UninitDestroyGuard()
+ {
+ if (__builtin_expect(_M_cur != 0, 0))
+ std::_Destroy(_M_first, *_M_cur, _M_alloc);
+ }
+
+ _GLIBCXX20_CONSTEXPR
+ void release() { _M_cur = 0; }
+
+ private:
+ _ForwardIterator const _M_first;
+ _ForwardIterator* _M_cur;
+ _Alloc& _M_alloc;
+
+ _UninitDestroyGuard(const _UninitDestroyGuard&);
+ };
+
+ template<typename _ForwardIterator>
+ struct _UninitDestroyGuard<_ForwardIterator, void>
+ {
+ _GLIBCXX20_CONSTEXPR
+ explicit
+ _UninitDestroyGuard(_ForwardIterator& __first)
+ : _M_first(__first), _M_cur(__builtin_addressof(__first))
+ { }
+
+ _GLIBCXX20_CONSTEXPR
+ ~_UninitDestroyGuard()
+ {
+ if (__builtin_expect(_M_cur != 0, 0))
+ std::_Destroy(_M_first, *_M_cur);
+ }
+
+ _GLIBCXX20_CONSTEXPR
+ void release() { _M_cur = 0; }
+
+ _ForwardIterator const _M_first;
+ _ForwardIterator* _M_cur;
+
+ private:
+ _UninitDestroyGuard(const _UninitDestroyGuard&);
+ };
+
template<typename _InputIterator, typename _ForwardIterator>
_GLIBCXX20_CONSTEXPR
_ForwardIterator
__do_uninit_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
{
- _ForwardIterator __cur = __result;
- __try
- {
- for (; __first != __last; ++__first, (void)++__cur)
- std::_Construct(std::__addressof(*__cur), *__first);
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__result, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__result);
+ for (; __first != __last; ++__first, (void)++__result)
+ std::_Construct(std::__addressof(*__result), *__first);
+ __guard.release();
+ return __result;
}
template<bool _TrivialValueTypes>
@@ -192,17 +238,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__do_uninit_fill(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __x)
{
- _ForwardIterator __cur = __first;
- __try
- {
- for (; __cur != __last; ++__cur)
- std::_Construct(std::__addressof(*__cur), __x);
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __first != __last; ++__first)
+ std::_Construct(std::__addressof(*__first), __x);
+ __guard.release();
}
template<bool _TrivialValueType>
@@ -260,18 +299,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_ForwardIterator
__do_uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
{
- _ForwardIterator __cur = __first;
- __try
- {
- for (; __n > 0; --__n, (void) ++__cur)
- std::_Construct(std::__addressof(*__cur), __x);
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __n > 0; --__n, (void) ++__first)
+ std::_Construct(std::__addressof(*__first), __x);
+ __guard.release();
+ return __first;
}
template<bool _TrivialValueType>
@@ -344,19 +376,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__uninitialized_copy_a(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result, _Allocator& __alloc)
{
- _ForwardIterator __cur = __result;
- __try
- {
- typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
- for (; __first != __last; ++__first, (void)++__cur)
- __traits::construct(__alloc, std::__addressof(*__cur), *__first);
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__result, __cur, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator>
+ __guard(__result, __alloc);
+
+ typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
+ for (; __first != __last; ++__first, (void)++__result)
+ __traits::construct(__alloc, std::__addressof(*__result), *__first);
+ __guard.release();
+ return __result;
}
#if _GLIBCXX_HOSTED
@@ -406,18 +433,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__uninitialized_fill_a(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __x, _Allocator& __alloc)
{
- _ForwardIterator __cur = __first;
- __try
- {
- typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
- for (; __cur != __last; ++__cur)
- __traits::construct(__alloc, std::__addressof(*__cur), __x);
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator>
+ __guard(__first, __alloc);
+
+ typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
+ for (; __first != __last; ++__first)
+ __traits::construct(__alloc, std::__addressof(*__first), __x);
+
+ __guard.release();
}
#if _GLIBCXX_HOSTED
@@ -442,19 +465,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__uninitialized_fill_n_a(_ForwardIterator __first, _Size __n,
const _Tp& __x, _Allocator& __alloc)
{
- _ForwardIterator __cur = __first;
- __try
- {
- typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
- for (; __n > 0; --__n, (void) ++__cur)
- __traits::construct(__alloc, std::__addressof(*__cur), __x);
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator>
+ __guard(__first, __alloc);
+ typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
+ for (; __n > 0; --__n, (void) ++__first)
+ __traits::construct(__alloc, std::__addressof(*__first), __x);
+ __guard.release();
+ return __first;
}
#if _GLIBCXX_HOSTED
@@ -493,17 +510,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Allocator& __alloc)
{
_ForwardIterator __mid = std::__uninitialized_copy_a(__first1, __last1,
- __result,
- __alloc);
- __try
- {
- return std::__uninitialized_move_a(__first2, __last2, __mid, __alloc);
- }
- __catch(...)
- {
- std::_Destroy(__result, __mid, __alloc);
- __throw_exception_again;
- }
+ __result, __alloc);
+ _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__result,
+ __alloc);
+ __result = __mid; // Everything up to __mid is now guarded.
+ __result = std::__uninitialized_move_a(__first2, __last2, __mid, __alloc);
+ __guard.release();
+ return __result;
}
// __uninitialized_move_copy
@@ -521,17 +534,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Allocator& __alloc)
{
_ForwardIterator __mid = std::__uninitialized_move_a(__first1, __last1,
- __result,
- __alloc);
- __try
- {
- return std::__uninitialized_copy_a(__first2, __last2, __mid, __alloc);
- }
- __catch(...)
- {
- std::_Destroy(__result, __mid, __alloc);
- __throw_exception_again;
- }
+ __result, __alloc);
+ _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__result,
+ __alloc);
+ __result = __mid; // Everything up to __mid is now guarded.
+ __result = std::__uninitialized_copy_a(__first2, __last2, __mid, __alloc);
+ __guard.release();
}
// __uninitialized_fill_move
@@ -545,15 +553,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_InputIterator __last, _Allocator& __alloc)
{
std::__uninitialized_fill_a(__result, __mid, __x, __alloc);
- __try
- {
- return std::__uninitialized_move_a(__first, __last, __mid, __alloc);
- }
- __catch(...)
- {
- std::_Destroy(__result, __mid, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__result,
+ __alloc);
+ __result = __mid; // Everything up to __mid is now guarded.
+ __result = std::__uninitialized_move_a(__first, __last, __mid, __alloc);
+ __guard.release();
+ return __result;
}
// __uninitialized_move_fill
@@ -570,15 +575,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_ForwardIterator __mid2 = std::__uninitialized_move_a(__first1, __last1,
__first2,
__alloc);
- __try
- {
- std::__uninitialized_fill_a(__mid2, __last2, __x, __alloc);
- }
- __catch(...)
- {
- std::_Destroy(__first2, __mid2, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__first2,
+ __alloc);
+ __first2 = __mid2; // Everything up to __mid2 is now guarded.
+ std::__uninitialized_fill_a(__mid2, __last2, __x, __alloc);
+ __guard.release();
}
/// @endcond
@@ -596,17 +597,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static void
__uninit_default(_ForwardIterator __first, _ForwardIterator __last)
{
- _ForwardIterator __cur = __first;
- __try
- {
- for (; __cur != __last; ++__cur)
- std::_Construct(std::__addressof(*__cur));
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __first != __last; ++__first)
+ std::_Construct(std::__addressof(*__first));
+ __guard.release();
}
};
@@ -636,18 +630,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static _ForwardIterator
__uninit_default_n(_ForwardIterator __first, _Size __n)
{
- _ForwardIterator __cur = __first;
- __try
- {
- for (; __n > 0; --__n, (void) ++__cur)
- std::_Construct(std::__addressof(*__cur));
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __n > 0; --__n, (void) ++__first)
+ std::_Construct(std::__addressof(*__first));
+ __guard.release();
+ return __first;
}
};
@@ -722,18 +709,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_ForwardIterator __last,
_Allocator& __alloc)
{
- _ForwardIterator __cur = __first;
- __try
- {
- typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
- for (; __cur != __last; ++__cur)
- __traits::construct(__alloc, std::__addressof(*__cur));
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__first,
+ __alloc);
+ typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
+ for (; __first != __last; ++__first)
+ __traits::construct(__alloc, std::__addressof(*__first));
+ __guard.release();
}
#if _GLIBCXX_HOSTED
@@ -753,19 +734,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__uninitialized_default_n_a(_ForwardIterator __first, _Size __n,
_Allocator& __alloc)
{
- _ForwardIterator __cur = __first;
- __try
- {
- typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
- for (; __n > 0; --__n, (void) ++__cur)
- __traits::construct(__alloc, std::__addressof(*__cur));
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur, __alloc);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__first,
+ __alloc);
+ typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
+ for (; __n > 0; --__n, (void) ++__first)
+ __traits::construct(__alloc, std::__addressof(*__first));
+ __guard.release();
+ return __first;
}
#if _GLIBCXX_HOSTED
@@ -787,17 +762,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__uninit_default_novalue(_ForwardIterator __first,
_ForwardIterator __last)
{
- _ForwardIterator __cur = __first;
- __try
- {
- for (; __cur != __last; ++__cur)
- std::_Construct_novalue(std::__addressof(*__cur));
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __first != __last; ++__first)
+ std::_Construct_novalue(std::__addressof(*__first));
+ __guard.release();
}
};
@@ -818,18 +786,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static _ForwardIterator
__uninit_default_novalue_n(_ForwardIterator __first, _Size __n)
{
- _ForwardIterator __cur = __first;
- __try
- {
- for (; __n > 0; --__n, (void) ++__cur)
- std::_Construct_novalue(std::__addressof(*__cur));
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__first, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __n > 0; --__n, (void) ++__first)
+ std::_Construct_novalue(std::__addressof(*__first));
+ __guard.release();
+ return __first;
}
};
@@ -877,18 +838,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__uninitialized_copy_n(_InputIterator __first, _Size __n,
_ForwardIterator __result, input_iterator_tag)
{
- _ForwardIterator __cur = __result;
- __try
- {
- for (; __n > 0; --__n, (void) ++__first, ++__cur)
- std::_Construct(std::__addressof(*__cur), *__first);
- return __cur;
- }
- __catch(...)
- {
- std::_Destroy(__result, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__result);
+ for (; __n > 0; --__n, (void) ++__first, ++__result)
+ std::_Construct(std::__addressof(*__result), *__first);
+ __guard.release();
+ return __result;
}
template<typename _RandomAccessIterator, typename _Size,
@@ -903,20 +857,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typename _ForwardIterator>
pair<_InputIterator, _ForwardIterator>
__uninitialized_copy_n_pair(_InputIterator __first, _Size __n,
- _ForwardIterator __result, input_iterator_tag)
+ _ForwardIterator __result, input_iterator_tag)
{
- _ForwardIterator __cur = __result;
- __try
- {
- for (; __n > 0; --__n, (void) ++__first, ++__cur)
- std::_Construct(std::__addressof(*__cur), *__first);
- return {__first, __cur};
- }
- __catch(...)
- {
- std::_Destroy(__result, __cur);
- __throw_exception_again;
- }
+ _UninitDestroyGuard<_ForwardIterator> __guard(__result);
+ for (; __n > 0; --__n, (void) ++__first, ++__result)
+ std::_Construct(std::__addressof(*__result), *__first);
+ __guard.release();
+ return {__first, __result};
}
template<typename _RandomAccessIterator, typename _Size,
--
2.45.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset
2024-06-27 10:39 [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
@ 2024-06-27 10:39 ` Jonathan Wakely
2024-06-27 11:46 ` Jonathan Wakely
2024-06-27 10:39 ` [PATCH 3/3] libstdc++: Use std::__uninitialized_default for ranges::uninitialized_value_construct Jonathan Wakely
2024-07-03 15:32 ` [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
2 siblings, 1 reply; 6+ messages in thread
From: Jonathan Wakely @ 2024-06-27 10:39 UTC (permalink / raw)
To: libstdc++, gcc-patches
For trivial types std::__uninitialized_default (which is used by
std::uninitialized_value_construct) value-initializes the first element
then copies that to the rest of the range using std::fill.
Tamar is working on improved vectorization for std::fill, but for this
value-initialized case where we just want to fill with zeros it seems
sensible to just ... fill with zeros. We can use memset to do that.
Tested x86_64-linux.
-- >8 --
The current optimized path for __uninitialized_default and
__uninitialized_default_n will use std::fill for trivial types, but we
can just use memset to fill them with zeros instead.
Because these functions are not defined for C++98 at all, we can use
if-constexpr to simplify them and remove the dispatching to members of
class template specializations.
libstdc++-v3/ChangeLog:
* include/bits/stl_uninitialized.h (__uninitialized_default_1)
(__uninitialized_default_n_1): Remove.
(__uninitialized_default, __uninitialized_default_n): Use memset
for contiguous ranges of trivial types.
* testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc:
Check negative size.
---
libstdc++-v3/include/bits/stl_uninitialized.h | 159 ++++++++----------
.../uninitialized_value_construct_n/sizes.cc | 13 ++
2 files changed, 87 insertions(+), 85 deletions(-)
diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
index a9965f26269..1216b319f66 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -61,6 +61,7 @@
#endif
#include <bits/stl_algobase.h> // copy
+#include <bits/ptr_traits.h> // __to_address
#include <ext/alloc_traits.h> // __alloc_traits
#if __cplusplus >= 201703L
@@ -590,89 +591,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Extensions: __uninitialized_default, __uninitialized_default_n,
// __uninitialized_default_a, __uninitialized_default_n_a.
- template<bool _TrivialValueType>
- struct __uninitialized_default_1
- {
- template<typename _ForwardIterator>
- static void
- __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
- {
- _UninitDestroyGuard<_ForwardIterator> __guard(__first);
- for (; __first != __last; ++__first)
- std::_Construct(std::__addressof(*__first));
- __guard.release();
- }
- };
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions"
- template<>
- struct __uninitialized_default_1<true>
+ // If we can value-initialize *__first using memset then return
+ // std::to_address(__first), otherwise return nullptr.
+ template<typename _ForwardIterator>
+ _GLIBCXX20_CONSTEXPR
+ inline void*
+ __ptr_for_trivial_zero_init(_ForwardIterator __first)
{
- template<typename _ForwardIterator>
- static void
- __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
- {
- if (__first == __last)
- return;
+#ifdef __cpp_lib_is_constant_evaluated
+ if (std::is_constant_evaluated())
+ return nullptr; // Cannot memset during constant evaluation.
+#endif
- typename iterator_traits<_ForwardIterator>::value_type* __val
- = std::__addressof(*__first);
- std::_Construct(__val);
- if (++__first != __last)
- std::fill(__first, __last, *__val);
- }
- };
-
- template<bool _TrivialValueType>
- struct __uninitialized_default_n_1
- {
- template<typename _ForwardIterator, typename _Size>
- _GLIBCXX20_CONSTEXPR
- static _ForwardIterator
- __uninit_default_n(_ForwardIterator __first, _Size __n)
- {
- _UninitDestroyGuard<_ForwardIterator> __guard(__first);
- for (; __n > 0; --__n, (void) ++__first)
- std::_Construct(std::__addressof(*__first));
- __guard.release();
- return __first;
- }
- };
-
- template<>
- struct __uninitialized_default_n_1<true>
- {
- template<typename _ForwardIterator, typename _Size>
- _GLIBCXX20_CONSTEXPR
- static _ForwardIterator
- __uninit_default_n(_ForwardIterator __first, _Size __n)
- {
- if (__n > 0)
+#if __cpp_lib_concepts
+ if constexpr (!contiguous_iterator<_ForwardIterator>)
+ return nullptr; // Need a raw pointer for memset.
+#else
+ if constexpr (!is_pointer<_ForwardIterator>::value)
+ return nullptr;
+#endif
+ else
+ {
+ using _ValueType
+ = typename iterator_traits<_ForwardIterator>::value_type;
+ // Need value-init to be equivalent to zero-init.
+ using __value_init_is_zero_init
+ = __and_<is_trivial<_ValueType>,
+ is_trivially_constructible<_ValueType>>;
+ if constexpr (__value_init_is_zero_init::value)
{
- typename iterator_traits<_ForwardIterator>::value_type* __val
- = std::__addressof(*__first);
- std::_Construct(__val);
- ++__first;
- __first = std::fill_n(__first, __n - 1, *__val);
+ using _Ptr = decltype(std::__to_address(__first));
+ // Cannot use memset if _Ptr is cv-qualified.
+ if constexpr (is_convertible<_Ptr, void*>::value)
+ return std::__to_address(__first);
}
- return __first;
}
- };
+ return nullptr;
+ }
// __uninitialized_default
// Fills [first, last) with value-initialized value_types.
template<typename _ForwardIterator>
inline void
- __uninitialized_default(_ForwardIterator __first,
- _ForwardIterator __last)
+ __uninitialized_default(_ForwardIterator __first, _ForwardIterator __last)
{
- typedef typename iterator_traits<_ForwardIterator>::value_type
- _ValueType;
- // trivial types can have deleted assignment
- const bool __assignable = is_copy_assignable<_ValueType>::value;
+ if constexpr (__is_random_access_iter<_ForwardIterator>::__value)
+ if (void* __ptr = std::__ptr_for_trivial_zero_init(__first))
+ {
+ using _ValueType
+ = typename iterator_traits<_ForwardIterator>::value_type;
+ if (auto __dist = __last - __first)
+ {
+ __glibcxx_assert(__dist > 0);
+ const size_t __n = __dist;
+ __glibcxx_assert(__n < __SIZE_MAX__ / sizeof(_ValueType));
+ __builtin_memset(__ptr, 0, __n * sizeof(_ValueType));
+ }
+ return;
+ }
- std::__uninitialized_default_1<__is_trivial(_ValueType)
- && __assignable>::
- __uninit_default(__first, __last);
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __first != __last; ++__first)
+ std::_Construct(std::__addressof(*__first));
+ __guard.release();
}
// __uninitialized_default_n
@@ -682,23 +666,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
inline _ForwardIterator
__uninitialized_default_n(_ForwardIterator __first, _Size __n)
{
-#ifdef __cpp_lib_is_constant_evaluated
- if (std::is_constant_evaluated())
- return __uninitialized_default_n_1<false>::
- __uninit_default_n(__first, __n);
-#endif
+ if constexpr (is_integral<_Size>::value)
+ if constexpr (__is_random_access_iter<_ForwardIterator>::__value)
+ if (void* __ptr = std::__ptr_for_trivial_zero_init(__first))
+ {
+ using _ValueType
+ = typename iterator_traits<_ForwardIterator>::value_type;
+ if (__n <= 0)
+ return __first;
+ else if (size_t(__n) < __SIZE_MAX__ / sizeof(_ValueType))
+ {
+ __builtin_memset(__ptr, 0, __n * sizeof(_ValueType));
+ return __first + __n;
+ }
+ }
- typedef typename iterator_traits<_ForwardIterator>::value_type
- _ValueType;
- // See uninitialized_fill_n for the conditions for using std::fill_n.
- constexpr bool __can_fill
- = __and_<is_integral<_Size>, is_copy_assignable<_ValueType>>::value;
-
- return __uninitialized_default_n_1<__is_trivial(_ValueType)
- && __can_fill>::
- __uninit_default_n(__first, __n);
+ _UninitDestroyGuard<_ForwardIterator> __guard(__first);
+ for (; __n > 0; --__n, (void) ++__first)
+ std::_Construct(std::__addressof(*__first));
+ __guard.release();
+ return __first;
}
-
+#pragma GCC diagnostic pop
// __uninitialized_default_a
// Fills [first, last) with value_types constructed by the allocator
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc
index 7705c6813e3..9c4198c1a98 100644
--- a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc
@@ -52,9 +52,22 @@ test02()
VERIFY( i[4] == 5 );
}
+void
+test03()
+{
+ int i[3] = { 1, 2, 3 };
+ // The standard defines it in terms of a loop which only runs for positive n.
+ auto j = std::uninitialized_value_construct_n(i+1, -5);
+ VERIFY( j == i+1 );
+ VERIFY( i[0] == 1 );
+ VERIFY( i[1] == 2 );
+ VERIFY( i[2] == 3 );
+}
+
int
main()
{
test01();
test02();
+ test03();
}
--
2.45.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset
2024-06-27 10:39 ` [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset Jonathan Wakely
@ 2024-06-27 11:46 ` Jonathan Wakely
0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Wakely @ 2024-06-27 11:46 UTC (permalink / raw)
To: libstdc++, gcc-patches
On Thu, 27 Jun 2024 at 11:53, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> For trivial types std::__uninitialized_default (which is used by
> std::uninitialized_value_construct) value-initializes the first element
> then copies that to the rest of the range using std::fill.
>
> Tamar is working on improved vectorization for std::fill, but for this
> value-initialized case where we just want to fill with zeros it seems
> sensible to just ... fill with zeros. We can use memset to do that.
Oops, I misremembered. Tamar is working on std::find not std::fill, so
that isn't relevant to this change to use memset for all trivial
types.
The current optimization to use std::fill in
std::uninitialized_value_construct only helps for trivial types of
size 1, because otherwise std::fill isn't optimized to memset. So an
alternative solution for std::uninitialized_value_construct of trivial
types would be to convert the contiguous iterators to char* (via
reinterpret_cast) and then use std::fill(f, l, '\0') which would then
use memset. Rather than relying on that detail of std::fill, I think
it makes sense just to do the memset directly where we know it's valid
for trivial types of any size. And that can be generalized for
non-common ranges, so can be used for
ranges::uninitialized_value_construct, which isn't the case if we
defer to std::fill.
>
> Tested x86_64-linux.
>
> -- >8 --
>
> The current optimized path for __uninitialized_default and
> __uninitialized_default_n will use std::fill for trivial types, but we
> can just use memset to fill them with zeros instead.
>
> Because these functions are not defined for C++98 at all, we can use
> if-constexpr to simplify them and remove the dispatching to members of
> class template specializations.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/stl_uninitialized.h (__uninitialized_default_1)
> (__uninitialized_default_n_1): Remove.
> (__uninitialized_default, __uninitialized_default_n): Use memset
> for contiguous ranges of trivial types.
> * testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc:
> Check negative size.
> ---
> libstdc++-v3/include/bits/stl_uninitialized.h | 159 ++++++++----------
> .../uninitialized_value_construct_n/sizes.cc | 13 ++
> 2 files changed, 87 insertions(+), 85 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
> index a9965f26269..1216b319f66 100644
> --- a/libstdc++-v3/include/bits/stl_uninitialized.h
> +++ b/libstdc++-v3/include/bits/stl_uninitialized.h
> @@ -61,6 +61,7 @@
> #endif
>
> #include <bits/stl_algobase.h> // copy
> +#include <bits/ptr_traits.h> // __to_address
> #include <ext/alloc_traits.h> // __alloc_traits
>
> #if __cplusplus >= 201703L
> @@ -590,89 +591,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // Extensions: __uninitialized_default, __uninitialized_default_n,
> // __uninitialized_default_a, __uninitialized_default_n_a.
>
> - template<bool _TrivialValueType>
> - struct __uninitialized_default_1
> - {
> - template<typename _ForwardIterator>
> - static void
> - __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
> - {
> - _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> - for (; __first != __last; ++__first)
> - std::_Construct(std::__addressof(*__first));
> - __guard.release();
> - }
> - };
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++17-extensions"
>
> - template<>
> - struct __uninitialized_default_1<true>
> + // If we can value-initialize *__first using memset then return
> + // std::to_address(__first), otherwise return nullptr.
> + template<typename _ForwardIterator>
> + _GLIBCXX20_CONSTEXPR
> + inline void*
> + __ptr_for_trivial_zero_init(_ForwardIterator __first)
> {
> - template<typename _ForwardIterator>
> - static void
> - __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
> - {
> - if (__first == __last)
> - return;
> +#ifdef __cpp_lib_is_constant_evaluated
> + if (std::is_constant_evaluated())
> + return nullptr; // Cannot memset during constant evaluation.
> +#endif
>
> - typename iterator_traits<_ForwardIterator>::value_type* __val
> - = std::__addressof(*__first);
> - std::_Construct(__val);
> - if (++__first != __last)
> - std::fill(__first, __last, *__val);
> - }
> - };
> -
> - template<bool _TrivialValueType>
> - struct __uninitialized_default_n_1
> - {
> - template<typename _ForwardIterator, typename _Size>
> - _GLIBCXX20_CONSTEXPR
> - static _ForwardIterator
> - __uninit_default_n(_ForwardIterator __first, _Size __n)
> - {
> - _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> - for (; __n > 0; --__n, (void) ++__first)
> - std::_Construct(std::__addressof(*__first));
> - __guard.release();
> - return __first;
> - }
> - };
> -
> - template<>
> - struct __uninitialized_default_n_1<true>
> - {
> - template<typename _ForwardIterator, typename _Size>
> - _GLIBCXX20_CONSTEXPR
> - static _ForwardIterator
> - __uninit_default_n(_ForwardIterator __first, _Size __n)
> - {
> - if (__n > 0)
> +#if __cpp_lib_concepts
> + if constexpr (!contiguous_iterator<_ForwardIterator>)
> + return nullptr; // Need a raw pointer for memset.
> +#else
> + if constexpr (!is_pointer<_ForwardIterator>::value)
> + return nullptr;
> +#endif
> + else
> + {
> + using _ValueType
> + = typename iterator_traits<_ForwardIterator>::value_type;
> + // Need value-init to be equivalent to zero-init.
> + using __value_init_is_zero_init
> + = __and_<is_trivial<_ValueType>,
> + is_trivially_constructible<_ValueType>>;
> + if constexpr (__value_init_is_zero_init::value)
> {
> - typename iterator_traits<_ForwardIterator>::value_type* __val
> - = std::__addressof(*__first);
> - std::_Construct(__val);
> - ++__first;
> - __first = std::fill_n(__first, __n - 1, *__val);
> + using _Ptr = decltype(std::__to_address(__first));
> + // Cannot use memset if _Ptr is cv-qualified.
> + if constexpr (is_convertible<_Ptr, void*>::value)
> + return std::__to_address(__first);
> }
> - return __first;
> }
> - };
> + return nullptr;
> + }
>
> // __uninitialized_default
> // Fills [first, last) with value-initialized value_types.
> template<typename _ForwardIterator>
> inline void
> - __uninitialized_default(_ForwardIterator __first,
> - _ForwardIterator __last)
> + __uninitialized_default(_ForwardIterator __first, _ForwardIterator __last)
> {
> - typedef typename iterator_traits<_ForwardIterator>::value_type
> - _ValueType;
> - // trivial types can have deleted assignment
> - const bool __assignable = is_copy_assignable<_ValueType>::value;
> + if constexpr (__is_random_access_iter<_ForwardIterator>::__value)
> + if (void* __ptr = std::__ptr_for_trivial_zero_init(__first))
> + {
> + using _ValueType
> + = typename iterator_traits<_ForwardIterator>::value_type;
> + if (auto __dist = __last - __first)
> + {
> + __glibcxx_assert(__dist > 0);
> + const size_t __n = __dist;
> + __glibcxx_assert(__n < __SIZE_MAX__ / sizeof(_ValueType));
> + __builtin_memset(__ptr, 0, __n * sizeof(_ValueType));
> + }
> + return;
> + }
>
> - std::__uninitialized_default_1<__is_trivial(_ValueType)
> - && __assignable>::
> - __uninit_default(__first, __last);
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __first != __last; ++__first)
> + std::_Construct(std::__addressof(*__first));
> + __guard.release();
> }
>
> // __uninitialized_default_n
> @@ -682,23 +666,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> inline _ForwardIterator
> __uninitialized_default_n(_ForwardIterator __first, _Size __n)
> {
> -#ifdef __cpp_lib_is_constant_evaluated
> - if (std::is_constant_evaluated())
> - return __uninitialized_default_n_1<false>::
> - __uninit_default_n(__first, __n);
> -#endif
> + if constexpr (is_integral<_Size>::value)
> + if constexpr (__is_random_access_iter<_ForwardIterator>::__value)
> + if (void* __ptr = std::__ptr_for_trivial_zero_init(__first))
> + {
> + using _ValueType
> + = typename iterator_traits<_ForwardIterator>::value_type;
> + if (__n <= 0)
> + return __first;
> + else if (size_t(__n) < __SIZE_MAX__ / sizeof(_ValueType))
> + {
> + __builtin_memset(__ptr, 0, __n * sizeof(_ValueType));
> + return __first + __n;
> + }
> + }
>
> - typedef typename iterator_traits<_ForwardIterator>::value_type
> - _ValueType;
> - // See uninitialized_fill_n for the conditions for using std::fill_n.
> - constexpr bool __can_fill
> - = __and_<is_integral<_Size>, is_copy_assignable<_ValueType>>::value;
> -
> - return __uninitialized_default_n_1<__is_trivial(_ValueType)
> - && __can_fill>::
> - __uninit_default_n(__first, __n);
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __n > 0; --__n, (void) ++__first)
> + std::_Construct(std::__addressof(*__first));
> + __guard.release();
> + return __first;
> }
> -
> +#pragma GCC diagnostic pop
>
> // __uninitialized_default_a
> // Fills [first, last) with value_types constructed by the allocator
> diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc
> index 7705c6813e3..9c4198c1a98 100644
> --- a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc
> +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct_n/sizes.cc
> @@ -52,9 +52,22 @@ test02()
> VERIFY( i[4] == 5 );
> }
>
> +void
> +test03()
> +{
> + int i[3] = { 1, 2, 3 };
> + // The standard defines it in terms of a loop which only runs for positive n.
> + auto j = std::uninitialized_value_construct_n(i+1, -5);
> + VERIFY( j == i+1 );
> + VERIFY( i[0] == 1 );
> + VERIFY( i[1] == 2 );
> + VERIFY( i[2] == 3 );
> +}
> +
> int
> main()
> {
> test01();
> test02();
> + test03();
> }
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] libstdc++: Use std::__uninitialized_default for ranges::uninitialized_value_construct
2024-06-27 10:39 [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
2024-06-27 10:39 ` [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset Jonathan Wakely
@ 2024-06-27 10:39 ` Jonathan Wakely
2024-07-03 15:32 ` [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
2 siblings, 0 replies; 6+ messages in thread
From: Jonathan Wakely @ 2024-06-27 10:39 UTC (permalink / raw)
To: libstdc++, gcc-patches
By generalizing std::__uninitialized_default to work with non-common
ranges (i.e. iterator/sentinel pair) we can reuse it for the
ranges::uninitialized_value_construct function. Doing that ensures that
whatever optimizations we have for std::uninitialized_value_construct
are automatically used for the ranges version too.
Tested x86_64-linux.
-- >8 --
This reuses the memset optimization in __uninitialized_default for the
ranges equivalent, and similarly for uninitialized_value_construct_n.
libstdc++-v3/ChangeLog:
* include/bits/ranges_uninitialized.h (uninitialized_value_construct):
Use __uninitialized_default.
(uninitialized_value_construct_n): Use __uninitialized_default_n.
* include/bits/stl_uninitialized.h (__uninitialized_default):
Allow first and last to be different types, to support arbitrary
sentinels. Return the end of the initialized range.
(uninitialized_value_construct): Discard return value from
__uninitialized_default.
---
.../include/bits/ranges_uninitialized.h | 27 +++----------------
libstdc++-v3/include/bits/stl_uninitialized.h | 13 +++++----
2 files changed, 11 insertions(+), 29 deletions(-)
diff --git a/libstdc++-v3/include/bits/ranges_uninitialized.h b/libstdc++-v3/include/bits/ranges_uninitialized.h
index f16f2ef39f5..d84c8502eb9 100644
--- a/libstdc++-v3/include/bits/ranges_uninitialized.h
+++ b/libstdc++-v3/include/bits/ranges_uninitialized.h
@@ -201,18 +201,8 @@ namespace ranges
_Iter
operator()(_Iter __first, _Sent __last) const
{
- using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
- if constexpr (is_trivial_v<_ValueType>
- && is_copy_assignable_v<_ValueType>)
- return ranges::fill(__first, __last, _ValueType());
- else
- {
- auto __guard = __detail::_DestroyGuard(__first);
- for (; __first != __last; ++__first)
- ::new (__detail::__voidify(*__first)) _ValueType();
- __guard.release();
- return __first;
- }
+ return std::__uninitialized_default(std::move(__first),
+ std::move(__last));
}
template<__detail::__nothrow_forward_range _Range>
@@ -234,18 +224,7 @@ namespace ranges
_Iter
operator()(_Iter __first, iter_difference_t<_Iter> __n) const
{
- using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
- if constexpr (is_trivial_v<_ValueType>
- && is_copy_assignable_v<_ValueType>)
- return ranges::fill_n(__first, __n, _ValueType());
- else
- {
- auto __guard = __detail::_DestroyGuard(__first);
- for (; __n > 0; ++__first, (void) --__n)
- ::new (__detail::__voidify(*__first)) _ValueType();
- __guard.release();
- return __first;
- }
+ return std::__uninitialized_default_n(std::move(__first), __n);
}
};
diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
index 1216b319f66..b13562992de 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -634,9 +634,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// __uninitialized_default
// Fills [first, last) with value-initialized value_types.
- template<typename _ForwardIterator>
- inline void
- __uninitialized_default(_ForwardIterator __first, _ForwardIterator __last)
+ template<typename _ForwardIterator, typename _Sentinel>
+ _GLIBCXX20_CONSTEXPR
+ inline _ForwardIterator
+ __uninitialized_default(_ForwardIterator __first, _Sentinel __last)
{
if constexpr (__is_random_access_iter<_ForwardIterator>::__value)
if (void* __ptr = std::__ptr_for_trivial_zero_init(__first))
@@ -649,14 +650,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
const size_t __n = __dist;
__glibcxx_assert(__n < __SIZE_MAX__ / sizeof(_ValueType));
__builtin_memset(__ptr, 0, __n * sizeof(_ValueType));
+ return __first + __dist;
}
- return;
+ return __first;
}
_UninitDestroyGuard<_ForwardIterator> __guard(__first);
for (; __first != __last; ++__first)
std::_Construct(std::__addressof(*__first));
__guard.release();
+ return __first;
}
// __uninitialized_default_n
@@ -939,7 +942,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
uninitialized_value_construct(_ForwardIterator __first,
_ForwardIterator __last)
{
- return std::__uninitialized_default(__first, __last);
+ std::__uninitialized_default(__first, __last);
}
/**
--
2.45.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h>
2024-06-27 10:39 [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
2024-06-27 10:39 ` [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset Jonathan Wakely
2024-06-27 10:39 ` [PATCH 3/3] libstdc++: Use std::__uninitialized_default for ranges::uninitialized_value_construct Jonathan Wakely
@ 2024-07-03 15:32 ` Jonathan Wakely
2024-07-03 15:36 ` Ville Voutilainen
2 siblings, 1 reply; 6+ messages in thread
From: Jonathan Wakely @ 2024-07-03 15:32 UTC (permalink / raw)
To: libstdc++, gcc-patches
On Thu, 27 Jun 2024 at 11:52, Jonathan Wakely wrote:
>
> This refactoring to use RAII doesn't seem to make any difference in
> benchmarks, although the generated code for some std::vector operations
> seems to be slightly larger. Maybe it will be faster (or slower) in some
> cases I didn't test?
>
> I think I like the change anyway - any other opinions on whether it's an
> improvement?
Any thoughts before I push this? Better? Worse? Needs more cowbell?
> Tested x86_64-linux.
>
> -- >8 --
>
> This adds an _UninitDestroyGuard class template, similar to
> ranges::_DestroyGuard used in <bits/ranges_uninitialized.h>. This allows
> us to remove all the try-catch blocks and rethrows, because any required
> cleanup gets done in the guard destructor.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/stl_uninitialized.h (_UninitDestroyGuard): New
> class template and partial specialization.
> (__do_uninit_copy, __do_uninit_fill, __do_uninit_fill_n)
> (__uninitialized_copy_a, __uninitialized_fill_a)
> (__uninitialized_fill_n_a, __uninitialized_copy_move)
> (__uninitialized_move_copy, __uninitialized_fill_move)
> (__uninitialized_move_fill, __uninitialized_default_1)
> (__uninitialized_default_n_a, __uninitialized_default_novalue_1)
> (__uninitialized_default_novalue_n_1, __uninitialized_copy_n)
> (__uninitialized_copy_n_pair): Use it.
> ---
> libstdc++-v3/include/bits/stl_uninitialized.h | 365 ++++++++----------
> 1 file changed, 156 insertions(+), 209 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
> index 3c405d8fbe8..a9965f26269 100644
> --- a/libstdc++-v3/include/bits/stl_uninitialized.h
> +++ b/libstdc++-v3/include/bits/stl_uninitialized.h
> @@ -107,24 +107,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __is_trivial(T) && __is_assignable(T&, U)
> #endif
>
> + template<typename _ForwardIterator, typename _Alloc = void>
> + struct _UninitDestroyGuard
> + {
> + _GLIBCXX20_CONSTEXPR
> + explicit
> + _UninitDestroyGuard(_ForwardIterator& __first, _Alloc& __a)
> + : _M_first(__first), _M_cur(__builtin_addressof(__first)), _M_alloc(__a)
> + { }
> +
> + _GLIBCXX20_CONSTEXPR
> + ~_UninitDestroyGuard()
> + {
> + if (__builtin_expect(_M_cur != 0, 0))
> + std::_Destroy(_M_first, *_M_cur, _M_alloc);
> + }
> +
> + _GLIBCXX20_CONSTEXPR
> + void release() { _M_cur = 0; }
> +
> + private:
> + _ForwardIterator const _M_first;
> + _ForwardIterator* _M_cur;
> + _Alloc& _M_alloc;
> +
> + _UninitDestroyGuard(const _UninitDestroyGuard&);
> + };
> +
> + template<typename _ForwardIterator>
> + struct _UninitDestroyGuard<_ForwardIterator, void>
> + {
> + _GLIBCXX20_CONSTEXPR
> + explicit
> + _UninitDestroyGuard(_ForwardIterator& __first)
> + : _M_first(__first), _M_cur(__builtin_addressof(__first))
> + { }
> +
> + _GLIBCXX20_CONSTEXPR
> + ~_UninitDestroyGuard()
> + {
> + if (__builtin_expect(_M_cur != 0, 0))
> + std::_Destroy(_M_first, *_M_cur);
> + }
> +
> + _GLIBCXX20_CONSTEXPR
> + void release() { _M_cur = 0; }
> +
> + _ForwardIterator const _M_first;
> + _ForwardIterator* _M_cur;
> +
> + private:
> + _UninitDestroyGuard(const _UninitDestroyGuard&);
> + };
> +
> template<typename _InputIterator, typename _ForwardIterator>
> _GLIBCXX20_CONSTEXPR
> _ForwardIterator
> __do_uninit_copy(_InputIterator __first, _InputIterator __last,
> _ForwardIterator __result)
> {
> - _ForwardIterator __cur = __result;
> - __try
> - {
> - for (; __first != __last; ++__first, (void)++__cur)
> - std::_Construct(std::__addressof(*__cur), *__first);
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__result);
> + for (; __first != __last; ++__first, (void)++__result)
> + std::_Construct(std::__addressof(*__result), *__first);
> + __guard.release();
> + return __result;
> }
>
> template<bool _TrivialValueTypes>
> @@ -192,17 +238,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __do_uninit_fill(_ForwardIterator __first, _ForwardIterator __last,
> const _Tp& __x)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - for (; __cur != __last; ++__cur)
> - std::_Construct(std::__addressof(*__cur), __x);
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __first != __last; ++__first)
> + std::_Construct(std::__addressof(*__first), __x);
> + __guard.release();
> }
>
> template<bool _TrivialValueType>
> @@ -260,18 +299,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _ForwardIterator
> __do_uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - for (; __n > 0; --__n, (void) ++__cur)
> - std::_Construct(std::__addressof(*__cur), __x);
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __n > 0; --__n, (void) ++__first)
> + std::_Construct(std::__addressof(*__first), __x);
> + __guard.release();
> + return __first;
> }
>
> template<bool _TrivialValueType>
> @@ -344,19 +376,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __uninitialized_copy_a(_InputIterator __first, _InputIterator __last,
> _ForwardIterator __result, _Allocator& __alloc)
> {
> - _ForwardIterator __cur = __result;
> - __try
> - {
> - typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> - for (; __first != __last; ++__first, (void)++__cur)
> - __traits::construct(__alloc, std::__addressof(*__cur), *__first);
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __cur, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator>
> + __guard(__result, __alloc);
> +
> + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> + for (; __first != __last; ++__first, (void)++__result)
> + __traits::construct(__alloc, std::__addressof(*__result), *__first);
> + __guard.release();
> + return __result;
> }
>
> #if _GLIBCXX_HOSTED
> @@ -406,18 +433,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __uninitialized_fill_a(_ForwardIterator __first, _ForwardIterator __last,
> const _Tp& __x, _Allocator& __alloc)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> - for (; __cur != __last; ++__cur)
> - __traits::construct(__alloc, std::__addressof(*__cur), __x);
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator>
> + __guard(__first, __alloc);
> +
> + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> + for (; __first != __last; ++__first)
> + __traits::construct(__alloc, std::__addressof(*__first), __x);
> +
> + __guard.release();
> }
>
> #if _GLIBCXX_HOSTED
> @@ -442,19 +465,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n,
> const _Tp& __x, _Allocator& __alloc)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> - for (; __n > 0; --__n, (void) ++__cur)
> - __traits::construct(__alloc, std::__addressof(*__cur), __x);
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator>
> + __guard(__first, __alloc);
> + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> + for (; __n > 0; --__n, (void) ++__first)
> + __traits::construct(__alloc, std::__addressof(*__first), __x);
> + __guard.release();
> + return __first;
> }
>
> #if _GLIBCXX_HOSTED
> @@ -493,17 +510,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _Allocator& __alloc)
> {
> _ForwardIterator __mid = std::__uninitialized_copy_a(__first1, __last1,
> - __result,
> - __alloc);
> - __try
> - {
> - return std::__uninitialized_move_a(__first2, __last2, __mid, __alloc);
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __mid, __alloc);
> - __throw_exception_again;
> - }
> + __result, __alloc);
> + _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__result,
> + __alloc);
> + __result = __mid; // Everything up to __mid is now guarded.
> + __result = std::__uninitialized_move_a(__first2, __last2, __mid, __alloc);
> + __guard.release();
> + return __result;
> }
>
> // __uninitialized_move_copy
> @@ -521,17 +534,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _Allocator& __alloc)
> {
> _ForwardIterator __mid = std::__uninitialized_move_a(__first1, __last1,
> - __result,
> - __alloc);
> - __try
> - {
> - return std::__uninitialized_copy_a(__first2, __last2, __mid, __alloc);
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __mid, __alloc);
> - __throw_exception_again;
> - }
> + __result, __alloc);
> + _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__result,
> + __alloc);
> + __result = __mid; // Everything up to __mid is now guarded.
> + __result = std::__uninitialized_copy_a(__first2, __last2, __mid, __alloc);
> + __guard.release();
> }
>
> // __uninitialized_fill_move
> @@ -545,15 +553,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _InputIterator __last, _Allocator& __alloc)
> {
> std::__uninitialized_fill_a(__result, __mid, __x, __alloc);
> - __try
> - {
> - return std::__uninitialized_move_a(__first, __last, __mid, __alloc);
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __mid, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__result,
> + __alloc);
> + __result = __mid; // Everything up to __mid is now guarded.
> + __result = std::__uninitialized_move_a(__first, __last, __mid, __alloc);
> + __guard.release();
> + return __result;
> }
>
> // __uninitialized_move_fill
> @@ -570,15 +575,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _ForwardIterator __mid2 = std::__uninitialized_move_a(__first1, __last1,
> __first2,
> __alloc);
> - __try
> - {
> - std::__uninitialized_fill_a(__mid2, __last2, __x, __alloc);
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first2, __mid2, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__first2,
> + __alloc);
> + __first2 = __mid2; // Everything up to __mid2 is now guarded.
> + std::__uninitialized_fill_a(__mid2, __last2, __x, __alloc);
> + __guard.release();
> }
>
> /// @endcond
> @@ -596,17 +597,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static void
> __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - for (; __cur != __last; ++__cur)
> - std::_Construct(std::__addressof(*__cur));
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __first != __last; ++__first)
> + std::_Construct(std::__addressof(*__first));
> + __guard.release();
> }
> };
>
> @@ -636,18 +630,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static _ForwardIterator
> __uninit_default_n(_ForwardIterator __first, _Size __n)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - for (; __n > 0; --__n, (void) ++__cur)
> - std::_Construct(std::__addressof(*__cur));
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __n > 0; --__n, (void) ++__first)
> + std::_Construct(std::__addressof(*__first));
> + __guard.release();
> + return __first;
> }
> };
>
> @@ -722,18 +709,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _ForwardIterator __last,
> _Allocator& __alloc)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> - for (; __cur != __last; ++__cur)
> - __traits::construct(__alloc, std::__addressof(*__cur));
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__first,
> + __alloc);
> + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> + for (; __first != __last; ++__first)
> + __traits::construct(__alloc, std::__addressof(*__first));
> + __guard.release();
> }
>
> #if _GLIBCXX_HOSTED
> @@ -753,19 +734,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __uninitialized_default_n_a(_ForwardIterator __first, _Size __n,
> _Allocator& __alloc)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> - for (; __n > 0; --__n, (void) ++__cur)
> - __traits::construct(__alloc, std::__addressof(*__cur));
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur, __alloc);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator, _Allocator> __guard(__first,
> + __alloc);
> + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
> + for (; __n > 0; --__n, (void) ++__first)
> + __traits::construct(__alloc, std::__addressof(*__first));
> + __guard.release();
> + return __first;
> }
>
> #if _GLIBCXX_HOSTED
> @@ -787,17 +762,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __uninit_default_novalue(_ForwardIterator __first,
> _ForwardIterator __last)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - for (; __cur != __last; ++__cur)
> - std::_Construct_novalue(std::__addressof(*__cur));
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __first != __last; ++__first)
> + std::_Construct_novalue(std::__addressof(*__first));
> + __guard.release();
> }
> };
>
> @@ -818,18 +786,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static _ForwardIterator
> __uninit_default_novalue_n(_ForwardIterator __first, _Size __n)
> {
> - _ForwardIterator __cur = __first;
> - __try
> - {
> - for (; __n > 0; --__n, (void) ++__cur)
> - std::_Construct_novalue(std::__addressof(*__cur));
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__first, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> + for (; __n > 0; --__n, (void) ++__first)
> + std::_Construct_novalue(std::__addressof(*__first));
> + __guard.release();
> + return __first;
> }
> };
>
> @@ -877,18 +838,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __uninitialized_copy_n(_InputIterator __first, _Size __n,
> _ForwardIterator __result, input_iterator_tag)
> {
> - _ForwardIterator __cur = __result;
> - __try
> - {
> - for (; __n > 0; --__n, (void) ++__first, ++__cur)
> - std::_Construct(std::__addressof(*__cur), *__first);
> - return __cur;
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__result);
> + for (; __n > 0; --__n, (void) ++__first, ++__result)
> + std::_Construct(std::__addressof(*__result), *__first);
> + __guard.release();
> + return __result;
> }
>
> template<typename _RandomAccessIterator, typename _Size,
> @@ -903,20 +857,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> typename _ForwardIterator>
> pair<_InputIterator, _ForwardIterator>
> __uninitialized_copy_n_pair(_InputIterator __first, _Size __n,
> - _ForwardIterator __result, input_iterator_tag)
> + _ForwardIterator __result, input_iterator_tag)
> {
> - _ForwardIterator __cur = __result;
> - __try
> - {
> - for (; __n > 0; --__n, (void) ++__first, ++__cur)
> - std::_Construct(std::__addressof(*__cur), *__first);
> - return {__first, __cur};
> - }
> - __catch(...)
> - {
> - std::_Destroy(__result, __cur);
> - __throw_exception_again;
> - }
> + _UninitDestroyGuard<_ForwardIterator> __guard(__result);
> + for (; __n > 0; --__n, (void) ++__first, ++__result)
> + std::_Construct(std::__addressof(*__result), *__first);
> + __guard.release();
> + return {__first, __result};
> }
>
> template<typename _RandomAccessIterator, typename _Size,
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h>
2024-07-03 15:32 ` [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
@ 2024-07-03 15:36 ` Ville Voutilainen
0 siblings, 0 replies; 6+ messages in thread
From: Ville Voutilainen @ 2024-07-03 15:36 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: libstdc++, gcc-patches
On Wed, 3 Jul 2024 at 18:33, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Thu, 27 Jun 2024 at 11:52, Jonathan Wakely wrote:
> >
> > This refactoring to use RAII doesn't seem to make any difference in
> > benchmarks, although the generated code for some std::vector operations
> > seems to be slightly larger. Maybe it will be faster (or slower) in some
> > cases I didn't test?
> >
> > I think I like the change anyway - any other opinions on whether it's an
> > improvement?
>
> Any thoughts before I push this? Better? Worse? Needs more cowbell?
I think the patch is an improvement. Push it.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-07-03 15:37 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-27 10:39 [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
2024-06-27 10:39 ` [PATCH 2/3] libstdc++: Optimize __uninitialized_default using memset Jonathan Wakely
2024-06-27 11:46 ` Jonathan Wakely
2024-06-27 10:39 ` [PATCH 3/3] libstdc++: Use std::__uninitialized_default for ranges::uninitialized_value_construct Jonathan Wakely
2024-07-03 15:32 ` [PATCH 1/3] libstdc++: Use RAII in <bits/stl_uninitialized.h> Jonathan Wakely
2024-07-03 15:36 ` Ville Voutilainen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).