Keep std::deque algos specializations in Debug mode
Jonathan Wakely
jwakely@redhat.com
Tue Sep 4 13:00:00 GMT 2018
On 25/08/18 22:44 +0200, François Dumont wrote:
>The last optimizations that get disabled when Debug mode is enable are
>the algo specializations for std::deque iterators.
>
>This patch move those algos in std namespace as they should even when
>Debug mode is enable so that they get considered even when calls are
>made with the namespace qualification. And it adds all the algos Debug
>implementations which forward to normal implementations to benefit
>from optimizations.
>
>Note that I try to use typename deque<>::iterator or typename
>deque<>::const_iterator to define Debug algos but it didn't work, gcc
>was just not considering those overloads. I wonder why ?
>
>I added test and manually checked that behavior was correct. Do you
>see a way to automate this validation ?
>
>François
>
>diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
>index 125bcffb0c3..2a3f23a8588 100644
>--- a/libstdc++-v3/include/bits/deque.tcc
>+++ b/libstdc++-v3/include/bits/deque.tcc
>@@ -980,22 +980,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> this->_M_impl._M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
> }
>
>+_GLIBCXX_END_NAMESPACE_CONTAINER
>+
> // Overload for deque::iterators, exploiting the "segmented-iterator
> // optimization".
> template<typename _Tp>
> void
>- fill(const _Deque_iterator<_Tp, _Tp&, _Tp*>& __first,
>- const _Deque_iterator<_Tp, _Tp&, _Tp*>& __last, const _Tp& __value)
>+ fill(const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>& __first,
>+ const _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>& __last,
>+ const _Tp& __value)
> {
>- typedef typename _Deque_iterator<_Tp, _Tp&, _Tp*>::_Self _Self;
>-
>- for (typename _Self::_Map_pointer __node = __first._M_node + 1;
>- __node < __last._M_node; ++__node)
>- std::fill(*__node, *__node + _Self::_S_buffer_size(), __value);
>+ typedef typename _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>::_Self
>+ _Self;
>
> if (__first._M_node != __last._M_node)
> {
> std::fill(__first._M_cur, __first._M_last, __value);
>+
>+ for (typename _Self::_Map_pointer __node = __first._M_node + 1;
>+ __node != __last._M_node; ++__node)
Is there any particular reason to change this from using < to != for
the comparison?
(This change is part of the reason I asked for the ChangeLog, but you
didn't mention it in the ChangeLog).
Moving it inside the condition makes sense (not only does it avoid a
branch in the single-page case, but means we fill the elements in
order).
>+ std::fill(*__node, *__node + _Self::_S_buffer_size(), __value);
>+
> std::fill(__last._M_first, __last._M_cur, __value);
> }
> else
The rest of the code changes look fine, I just wondered about that
bit.
I do have some comments on the new tests though ...
>diff --git a/libstdc++-v3/testsuite/23_containers/deque/copy.cc b/libstdc++-v3/testsuite/23_containers/deque/copy.cc
>new file mode 100644
>index 00000000000..9171200bc20
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/deque/copy.cc
>@@ -0,0 +1,56 @@
>+// Copyright (C) 2018 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include <limits>
>+#include <algorithm>
>+#include <deque>
>+
>+#include <testsuite_hooks.h>
>+
>+void test01()
>+{
>+ std::deque<char> d;
>+ for (char c = 0; c != std::numeric_limits<char>::max(); ++c)
>+ d.push_back(c);
>+
>+ std::deque<char> dest(std::numeric_limits<char>::max(), '\0');
These deques only have 127 or 255 elements (depending on
is_signed<char>) which will fit on a single page of a deque (the
default is 512 bytes per page).
That means the tests don't exercise the logic for handling
non-contiguous blocks of memory.
Ideally we'd want to test multiple cases:
- a single page, with/without empty capacity at front/back
- multiple pages, with/without empty capacity at front/back
That would be 8 cases. I think we want to test at least a single
page and multiple pages.
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/deque/fill.cc
>@@ -0,0 +1,35 @@
>+// Copyright (C) 2018 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include <limits>
>+#include <algorithm>
>+#include <deque>
>+
>+#include <testsuite_hooks.h>
>+
>+int main()
>+{
>+ std::deque<char> d;
>+ for (char c = 1; c != std::numeric_limits<char>::max(); ++c)
>+ d.push_back(c);
>+
>+ std::fill(d.begin(), d.end(), '\0');
>+
>+ VERIFY( d.front() == '\0' );
>+ VERIFY( d.back() == '\0' );
That doesn't check that the middle of the deque was filled, which
would matter if it had multiple pages. How about checking there are no
non-zero elements?
VERIFY( std::find_if(d.begin(), d.end(),
std::bind1st(std::equal_to<bool>(), true)) == d.end() )
(That would be easier in C++11, but we want these tests to be able to
run for C++98 too.)
More information about the Libstdc++
mailing list