diff mbox series

Improve relocation

Message ID alpine.DEB.2.21.1810261316090.23342@stedding.saclay.inria.fr
State New
Headers show
Series Improve relocation | expand

Commit Message

Marc Glisse Oct. 26, 2018, 12:02 p.m. UTC
Hello,

here are some tweaks so that I can usefully mark deque as trivially 
relocatable. It includes more noexcept(auto) madness. For __relocate_a_1, 
I should also test if copying, ++ and != are noexcept, but I wanted to ask 
first because there might be restrictions on what iterators are allowed to 
do, even if I didn't see them. Also, the current code already ignores 
those, so it may as well be fixed in another patch.

Allocators are complicated. I specialized only for the default allocator, 
because that's by far the one that is used the most, and I have much less 
risk of getting it wrong. Some allocator expert is welcome to make a 
better test. I do not know in details how deque is implemented. A quick 
look seemed to show that trivial relocation should be fine, but I would 
appreciate a confirmation.

The extra parameter for __is_trivially_relocatable is not used, but I 
expect it will be as soon as the specializations of 
__is_trivially_relocatable become more advanced.

If I use or specialize __is_trivially_relocatable in many places, this 
forces to #include bits/stl_uninitialized.h in many places. I wonder if I 
should move some of that stuff. Since I may use it in std::swap, 
bits/move.h looks like a sensible place for the core pieces 
(__is_trivially_relocatable, and __relocate_object if I ever create that). 
That or type_traits.

Regtested on gcc112. I manually checked that there was a speed-up for 
operations on vector<deque<int>>, although doing any kind of benchmarking 
on gcc112 is hard, I'll test locally next time.

2018-10-26  Marc Glisse  <marc.glisse@inria.fr>

 	PR libstdc++/87106
 	* include/bits/stl_algobase.h: Include <type_traits>.
 	(__niter_base): Add noexcept specification.
 	* include/bits/stl_deque.h: Include <bits/stl_uninitialized.h>.
 	(__is_trivially_relocatable): Specialize for deque.
 	* include/bits/stl_iterator.h: Include <type_traits>.
 	(__niter_base): Add noexcept specification.
 	* include/bits/stl_uninitialized.h (__is_trivially_relocatable):
 	Add parameter for meta-programming.
 	(__relocate_a_1, __relocate_a): Add noexcept specification.
 	* include/bits/stl_vector.h (__use_relocate): Test __relocate_a.

Comments

Marc Glisse Nov. 21, 2018, 7:43 p.m. UTC | #1
ping?

On Fri, 26 Oct 2018, Marc Glisse wrote:

> Hello,
>
> here are some tweaks so that I can usefully mark deque as trivially 
> relocatable. It includes more noexcept(auto) madness. For __relocate_a_1, I 
> should also test if copying, ++ and != are noexcept, but I wanted to ask 
> first because there might be restrictions on what iterators are allowed to 
> do, even if I didn't see them. Also, the current code already ignores those, 
> so it may as well be fixed in another patch.
>
> Allocators are complicated. I specialized only for the default allocator, 
> because that's by far the one that is used the most, and I have much less 
> risk of getting it wrong. Some allocator expert is welcome to make a better 
> test. I do not know in details how deque is implemented. A quick look seemed 
> to show that trivial relocation should be fine, but I would appreciate a 
> confirmation.
>
> The extra parameter for __is_trivially_relocatable is not used, but I expect 
> it will be as soon as the specializations of __is_trivially_relocatable 
> become more advanced.
>
> If I use or specialize __is_trivially_relocatable in many places, this forces 
> to #include bits/stl_uninitialized.h in many places. I wonder if I should 
> move some of that stuff. Since I may use it in std::swap, bits/move.h looks 
> like a sensible place for the core pieces (__is_trivially_relocatable, and 
> __relocate_object if I ever create that). That or type_traits.
>
> Regtested on gcc112. I manually checked that there was a speed-up for 
> operations on vector<deque<int>>, although doing any kind of benchmarking on 
> gcc112 is hard, I'll test locally next time.
>
> 2018-10-26  Marc Glisse  <marc.glisse@inria.fr>
>
> 	PR libstdc++/87106
> 	* include/bits/stl_algobase.h: Include <type_traits>.
> 	(__niter_base): Add noexcept specification.
> 	* include/bits/stl_deque.h: Include <bits/stl_uninitialized.h>.
> 	(__is_trivially_relocatable): Specialize for deque.
> 	* include/bits/stl_iterator.h: Include <type_traits>.
> 	(__niter_base): Add noexcept specification.
> 	* include/bits/stl_uninitialized.h (__is_trivially_relocatable):
> 	Add parameter for meta-programming.
> 	(__relocate_a_1, __relocate_a): Add noexcept specification.
> 	* include/bits/stl_vector.h (__use_relocate): Test __relocate_a.
Jonathan Wakely Nov. 22, 2018, 9:26 a.m. UTC | #2
On 26/10/18 14:02 +0200, Marc Glisse wrote:
>Index: libstdc++-v3/include/bits/stl_iterator.h
>===================================================================
>--- libstdc++-v3/include/bits/stl_iterator.h	(revision 265522)
>+++ libstdc++-v3/include/bits/stl_iterator.h	(working copy)
>@@ -59,20 +59,24 @@
> 
> #ifndef _STL_ITERATOR_H
> #define _STL_ITERATOR_H 1
> 
> #include <bits/cpp_type_traits.h>
> #include <ext/type_traits.h>
> #include <bits/move.h>
> #include <bits/ptr_traits.h>
> 
> #if __cplusplus > 201402L

I think this should be >= 201103L, no?

OK for trunk (with that #if changed, if appropriate).

Thanks.
Marc Glisse Nov. 22, 2018, 6:10 p.m. UTC | #3
On Thu, 22 Nov 2018, Jonathan Wakely wrote:

> On 26/10/18 14:02 +0200, Marc Glisse wrote:
>> Index: libstdc++-v3/include/bits/stl_iterator.h
>> ===================================================================
>> --- libstdc++-v3/include/bits/stl_iterator.h	(revision 265522)
>> +++ libstdc++-v3/include/bits/stl_iterator.h	(working copy)
>> @@ -59,20 +59,24 @@
>> 
>> #ifndef _STL_ITERATOR_H
>> #define _STL_ITERATOR_H 1
>> 
>> #include <bits/cpp_type_traits.h>
>> #include <ext/type_traits.h>
>> #include <bits/move.h>
>> #include <bits/ptr_traits.h>
>> 
>> #if __cplusplus > 201402L
>
> I think this should be >= 201103L, no?

Ah, yes, I guess I started from a copy-paste and got interrupted before 
updating it :-(

> OK for trunk (with that #if changed, if appropriate).

Thanks. Re-tested and committed. Do you have an opinion on the following 
item, which may be a bug in the current code?

"For __relocate_a_1, I should also test if copying, ++ and != are 
noexcept, but I wanted to ask first because there might be restrictions on 
what iterators are allowed to do, even if I didn't see them."
Jonathan Wakely Nov. 22, 2018, 6:30 p.m. UTC | #4
On 22/11/18 19:10 +0100, Marc Glisse wrote:
>On Thu, 22 Nov 2018, Jonathan Wakely wrote:
>
>>On 26/10/18 14:02 +0200, Marc Glisse wrote:
>>>Index: libstdc++-v3/include/bits/stl_iterator.h
>>>===================================================================
>>>--- libstdc++-v3/include/bits/stl_iterator.h	(revision 265522)
>>>+++ libstdc++-v3/include/bits/stl_iterator.h	(working copy)
>>>@@ -59,20 +59,24 @@
>>>
>>>#ifndef _STL_ITERATOR_H
>>>#define _STL_ITERATOR_H 1
>>>
>>>#include <bits/cpp_type_traits.h>
>>>#include <ext/type_traits.h>
>>>#include <bits/move.h>
>>>#include <bits/ptr_traits.h>
>>>
>>>#if __cplusplus > 201402L
>>
>>I think this should be >= 201103L, no?
>
>Ah, yes, I guess I started from a copy-paste and got interrupted 
>before updating it :-(
>
>>OK for trunk (with that #if changed, if appropriate).
>
>Thanks. Re-tested and committed. Do you have an opinion on the 
>following item, which may be a bug in the current code?
>
>"For __relocate_a_1, I should also test if copying, ++ and != are 
>noexcept, but I wanted to ask first because there might be 
>restrictions on what iterators are allowed to do, even if I didn't see 
>them."

I decided to postpone thinking about that :-)

In general iterators can throw on those operations. But for container
iterators copy construction of "a returned iterator" never throws. I
think that means that copying a singular iterator is allowed to throw,
but no container member functions ever return singular iterators.

Increment and decrement could throw, but for our implementation they
don't (and so far __relocate_a_1 is only used with pointers, or vector
or deque iterators, right?) Since we know we're using valid iterator
ranges (because we choose the begin and end iterators of the ranges
being relocated) it should also be safe to assume we never increment a
past-the-end iterator or anything else that might give an iterator a
reasom to throw.

Is that good enough, or do you want to make __relocate_a_1 general
enough to work with arbitrary iterators?
Marc Glisse Nov. 22, 2018, 6:44 p.m. UTC | #5
On Thu, 22 Nov 2018, Jonathan Wakely wrote:

>> "For __relocate_a_1, I should also test if copying, ++ and != are noexcept, 
>> but I wanted to ask first because there might be restrictions on what 
>> iterators are allowed to do, even if I didn't see them."
>
> I decided to postpone thinking about that :-)
>
> In general iterators can throw on those operations. But for container
> iterators copy construction of "a returned iterator" never throws. I
> think that means that copying a singular iterator is allowed to throw,
> but no container member functions ever return singular iterators.
>
> Increment and decrement could throw, but for our implementation they
> don't (and so far __relocate_a_1 is only used with pointers, or vector
> or deque iterators, right?) Since we know we're using valid iterator
> ranges (because we choose the begin and end iterators of the ranges
> being relocated) it should also be safe to assume we never increment a
> past-the-end iterator or anything else that might give an iterator a
> reasom to throw.

Indeed, I think currently we only use relocate with vector iterators, so 
it is not urgent to fix this.

> Is that good enough, or do you want to make __relocate_a_1 general
> enough to work with arbitrary iterators?

Eventually yes, but now that I think about it, as long as relocation is a 
private implementation detail, all uses are likely to be with standard 
iterators that we control, so it may not matter.

So now I agree with the "postpone" decision, thanks.
diff mbox series

Patch

Index: libstdc++-v3/include/bits/stl_algobase.h
===================================================================
--- libstdc++-v3/include/bits/stl_algobase.h	(revision 265522)
+++ libstdc++-v3/include/bits/stl_algobase.h	(working copy)
@@ -62,20 +62,23 @@ 
 #include <ext/type_traits.h>
 #include <ext/numeric_traits.h>
 #include <bits/stl_pair.h>
 #include <bits/stl_iterator_base_types.h>
 #include <bits/stl_iterator_base_funcs.h>
 #include <bits/stl_iterator.h>
 #include <bits/concept_check.h>
 #include <debug/debug.h>
 #include <bits/move.h> // For std::swap
 #include <bits/predefined_ops.h>
+#if __cplusplus >= 201103L
+# include <type_traits>
+#endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __cplusplus < 201103L
   // See http://gcc.gnu.org/ml/libstdc++/2004-08/msg00167.html: in a
   // nutshell, we are partially implementing the resolution of DR 187,
   // when it's safe, i.e., the value_types are equal.
   template<bool _BoolType>
@@ -268,20 +271,21 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       if (__comp(__a, __b))
 	return __b;
       return __a;
     }
 
   // Fallback implementation of the function in bits/stl_iterator.h used to
   // remove the __normal_iterator wrapper. See copy, fill, ...
   template<typename _Iterator>
     inline _Iterator
     __niter_base(_Iterator __it)
+    _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
     { return __it; }
 
   // Reverse the __niter_base transformation to get a
   // __normal_iterator back again (this assumes that __normal_iterator
   // is only used to wrap random access iterators, like pointers).
   template<typename _From, typename _To>
     inline _From
     __niter_wrap(_From __from, _To __res)
     { return __from + (__res - std::__niter_base(__from)); }
 
Index: libstdc++-v3/include/bits/stl_deque.h
===================================================================
--- libstdc++-v3/include/bits/stl_deque.h	(revision 265522)
+++ libstdc++-v3/include/bits/stl_deque.h	(working copy)
@@ -54,20 +54,21 @@ 
  */
 
 #ifndef _STL_DEQUE_H
 #define _STL_DEQUE_H 1
 
 #include <bits/concept_check.h>
 #include <bits/stl_iterator_base_types.h>
 #include <bits/stl_iterator_base_funcs.h>
 #if __cplusplus >= 201103L
 #include <initializer_list>
+#include <bits/stl_uninitialized.h> // for __is_trivially_relocatable
 #endif
 
 #include <debug/assertions.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
   /**
@@ -2359,14 +2360,23 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   /// See std::deque::swap().
   template<typename _Tp, typename _Alloc>
     inline void
     swap(deque<_Tp,_Alloc>& __x, deque<_Tp,_Alloc>& __y)
     _GLIBCXX_NOEXCEPT_IF(noexcept(__x.swap(__y)))
     { __x.swap(__y); }
 
 #undef _GLIBCXX_DEQUE_BUF_SIZE
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus >= 201103L
+  // std::allocator is safe, but it is not the only allocator
+  // for which this is valid.
+  template<class _Tp>
+    struct __is_trivially_relocatable<_GLIBCXX_STD_C::deque<_Tp>>
+    : true_type { };
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
 #endif /* _STL_DEQUE_H */
Index: libstdc++-v3/include/bits/stl_iterator.h
===================================================================
--- libstdc++-v3/include/bits/stl_iterator.h	(revision 265522)
+++ libstdc++-v3/include/bits/stl_iterator.h	(working copy)
@@ -59,20 +59,24 @@ 
 
 #ifndef _STL_ITERATOR_H
 #define _STL_ITERATOR_H 1
 
 #include <bits/cpp_type_traits.h>
 #include <ext/type_traits.h>
 #include <bits/move.h>
 #include <bits/ptr_traits.h>
 
 #if __cplusplus > 201402L
+# include <type_traits>
+#endif
+
+#if __cplusplus > 201402L
 # define __cpp_lib_array_constexpr 201603
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /**
    * @addtogroup iterators
    * @{
@@ -997,20 +1001,21 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Iterator, typename _Container>
     _Iterator
     __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
+    _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
     { return __it.base(); }
 
 #if __cplusplus >= 201103L
 
   /**
    * @addtogroup iterators
    * @{
    */
 
   // 24.4.3  Move iterators
Index: libstdc++-v3/include/bits/stl_uninitialized.h
===================================================================
--- libstdc++-v3/include/bits/stl_uninitialized.h	(revision 265522)
+++ libstdc++-v3/include/bits/stl_uninitialized.h	(working copy)
@@ -887,60 +887,63 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 			 __dest, std::move(*__orig)))
 	     && noexcept(std::allocator_traits<_Allocator>::destroy(
 			    __alloc, std::__addressof(*__orig))))
     {
       typedef std::allocator_traits<_Allocator> __traits;
       __traits::construct(__alloc, __dest, std::move(*__orig));
       __traits::destroy(__alloc, std::__addressof(*__orig));
     }
 
   // This class may be specialized for specific types.
-  template<typename _Tp>
+  template<typename _Tp, typename = void>
     struct __is_trivially_relocatable
     : is_trivial<_Tp> { };
 
   template <typename _Tp, typename _Up>
     inline __enable_if_t<std::__is_trivially_relocatable<_Tp>::value, _Tp*>
     __relocate_a_1(_Tp* __first, _Tp* __last,
-		   _Tp* __result, allocator<_Up>& __alloc)
+		   _Tp* __result, allocator<_Up>& __alloc) noexcept
     {
       ptrdiff_t __count = __last - __first;
       __builtin_memmove(__result, __first, __count * sizeof(_Tp));
       return __result + __count;
     }
 
   template <typename _InputIterator, typename _ForwardIterator,
 	    typename _Allocator>
     inline _ForwardIterator
     __relocate_a_1(_InputIterator __first, _InputIterator __last,
 		   _ForwardIterator __result, _Allocator& __alloc)
+    noexcept(noexcept(std::__relocate_object_a(std::addressof(*__result),
+					       std::addressof(*__first),
+					       __alloc)))
     {
       typedef typename iterator_traits<_InputIterator>::value_type
 	_ValueType;
       typedef typename iterator_traits<_ForwardIterator>::value_type
 	_ValueType2;
       static_assert(std::is_same<_ValueType, _ValueType2>::value);
-      static_assert(noexcept(std::__relocate_object_a(std::addressof(*__result),
-						      std::addressof(*__first),
-						      __alloc)));
       _ForwardIterator __cur = __result;
       for (; __first != __last; ++__first, (void)++__cur)
 	std::__relocate_object_a(std::__addressof(*__cur),
 				 std::__addressof(*__first), __alloc);
       return __cur;
     }
 
   template <typename _InputIterator, typename _ForwardIterator,
 	    typename _Allocator>
     inline _ForwardIterator
     __relocate_a(_InputIterator __first, _InputIterator __last,
 		 _ForwardIterator __result, _Allocator& __alloc)
+    noexcept(noexcept(__relocate_a_1(std::__niter_base(__first),
+				     std::__niter_base(__last),
+				     std::__niter_base(__result), __alloc)))
     {
       return __relocate_a_1(std::__niter_base(__first),
 			    std::__niter_base(__last),
 			    std::__niter_base(__result), __alloc);
     }
 #endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
Index: libstdc++-v3/include/bits/stl_vector.h
===================================================================
--- libstdc++-v3/include/bits/stl_vector.h	(revision 265522)
+++ libstdc++-v3/include/bits/stl_vector.h	(working copy)
@@ -417,24 +417,24 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       const_iterator;
       typedef std::reverse_iterator<const_iterator>	const_reverse_iterator;
       typedef std::reverse_iterator<iterator>		reverse_iterator;
       typedef size_t					size_type;
       typedef ptrdiff_t					difference_type;
       typedef _Alloc					allocator_type;
 
     private:
 #if __cplusplus >= 201103L
       static constexpr bool __use_relocate =
-	noexcept(std::__relocate_object_a(
-			std::addressof(*std::declval<pointer>()),
-			std::addressof(*std::declval<pointer>()),
-			std::declval<_Tp_alloc_type&>()));
+	noexcept(std::__relocate_a(std::declval<pointer>(),
+				   std::declval<pointer>(),
+				   std::declval<pointer>(),
+				   std::declval<_Tp_alloc_type&>()));
 #endif
 
     protected:
       using _Base::_M_allocate;
       using _Base::_M_deallocate;
       using _Base::_M_impl;
       using _Base::_M_get_Tp_allocator;
 
     public:
       // [23.2.4.1] construct/copy/destroy