This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Deque... (Re: [Patch] libstdc++/23425)


On Nov 25, 2005, at 2:00 PM, Paolo Carlini wrote:

So, finally... Here is what I have, taking shape... Exactly as per
Howard's suggestions, the basic ideas are:
1- Consistently use _M_erase_at_begin/_M_erase_at_end and avoid calling
move/copy unnecessarily.
2- Consistently use the "segmented iterator" optimization, when nothing
better is available, that is, when _Destroy(iterator, iterator) doesn't
boil down for sure to nothing (see _M_destroy_data_aux).


I have verified from dumps that the loops over pointers produced by 2-
are optimized to empty loops as expected, even to nothing if _Destroy is
overloaded for random access iterators (but probably the additional
overloads can wait because the loop optimizer will be improved and we
have a PR about that opened by Chris, 23361). And of course that
otherwise the dumps are as good or better than current mainline ;)


Testcases ran through valgrind.

Anyone can spot something wrong? Otherwise I will probably go ahead for v7.

I've done a brief visual and can find nothing wrong. It sure looks like you cleaned up erase(iter,iter)! :-)


The optimization for scalars is slick. Nice job! Can't wait till that works for user-defined types too.

A very minor nit: We're passing around the allocator by value for the purpose of function dispatching. Can we change that to by ref? I'd like to get ready for stateful allocators. I believe this would be a fairly minor mod to _M_get_Tp_allocator() and its clients. Perhaps starting with:

      _Tp_alloc_type&
      _M_get_Tp_allocator()
      { return *static_cast<_Tp_alloc_type*>(&this->_M_impl); }

      _Tp_alloc_type const&
      _M_get_Tp_allocator() const
      { return *static_cast<const _Tp_alloc_type*>(&this->_M_impl); }

I'm guessing this would be a minor optimization if the allocator has state.

Not for this patch, but as I was looking about I noticed a couple of places that could still be optimized (this is future work, not a suggestion for this patch!)...

* I think on erase we should keep a spare empty node hanging around so that it can be recycled on the next insert, even if it needs to be moved from front to back or vice versa. I'm especially concerned about the steady-state queue use case:

while (a long time)
{
     dq.push_front(t);
     ...
     dq.pop_back();
}

I believe that after reaching a steady state the above loop should not continually allocate/deallocate nodes. Rather it should keep the just emptied back node around and then move it to the front when push_front requests it. I'd keep no more than one empty node on each end, and maybe that's too much. Maybe at most just one empty node on either end (since it can be moved where needed).

* We call std::copy a lot internally (with deque::iterators as the output range, or both input and output). We need to specialize (overload) std::copy to work nicely with deque::iterator (the segmented iterator optimization). Same goes for the other std::algorithms we call.

-Howard


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]