This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Implement the deque functions of P1206R7
AbandonedPublic

Authored by philnik on Jun 23 2022, 2:41 AM.

Details

Reviewers
ldionne
Mordante
var-const
Group Reviewers
Restricted Project

Diff Detail

Event Timeline

philnik created this revision.Jun 23 2022, 2:41 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 23 2022, 2:41 AM
Herald added a subscriber: mgorny. · View Herald Transcript
philnik requested review of this revision.Jun 23 2022, 2:41 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 23 2022, 2:41 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript

Thanks for working on this. I did a shallow review and hope to find time for a more in depth review at another time.

libcxx/include/CMakeLists.txt
195

Can you update the C++2b status page with the status |Partial| and add a note which section is implemented.
(I guess a full status page is a bit overkill.)

libcxx/include/__concepts/container_compatible_range.h
24

s/_Type/_Tp

libcxx/include/__ranges/from_range_t.h
22

Please update the appropriate synopsis.

24

I don't see any tests for this trivial public type.

libcxx/include/deque
14

Please update the synopsis.

1391

When the range is a temporary are we allowed to move the elements from the range instead?

libcxx/test/std/containers/sequences/deque/deque.cons/from_range.pass.cpp
15

Can we add some test validating that ranges not passing the concept __container_compatible_range are rejected? For example an output range?

47

http://eel.is/c++draft/sequence.reqmts#12 requires Cpp17MoveInsertable, please validates this works. (Based on earlier observations I expect it to fail).

48

Can you also test the complexity requirements.

ldionne requested changes to this revision.Aug 11 2022, 9:44 AM

This is an awesome patch! I think there's a bunch of stuff that needs to be investigated per our live review just now, but we will all end up with a better understanding of how deque works so that's great.

libcxx/include/__algorithm/iterator_operations.h
47

I think you'll find that you don't need this anymore once you rebase.

libcxx/include/__ranges/from_range_t.h
18

This will soon not be useful anymore, but I think we should do it for consistency.

libcxx/include/__split_buffer
121–126

I think we should make those _LIBCPP_HIDE_FROM_ABI. Especially since we're modifying them in a way that might not be backwards compatible if this had leaked into a user's ABI.

libcxx/include/deque
1339

&& _LIBCPP_HAS_NO_INCOMPLETE_RANGES, here and elsewhere. We'll bulk-remove those in the future.

1385–1394

We could factor something out here, it seems like a potentially useful algorithm to me (in fact I thought the stdlib already had this algorithm):

template <class _Iter1, class _Sent1, class _Iter2, class _Sent2>
std::pair<_Iter1, _Iter2> __copy_shortest(_Iter1 __it1, _Sent1 __sent1, _Iter2 __it2, _Sent2 __sent2) {
    while (__first1 != __sent1 && __first2 != __sent2) {
      *__first2 = *__first1;
      ++__first1;
      ++__first2;
    }
    return {__first1, __first2};
}

And in fact, we can optimize the above for sized_sentinels, which would get rid of the need for the overload of assign_range we have below. And that would not add as many new dependencies to <deque> (views::take and friends).


Alternatively, per our live discussion, if you want to keep only the overload below (with the removed sized_range constraint), I'd be OK with that, BUT:

  1. We need to show that there is no significant compile-time impact to existing users of <deque>. In particular, if we end up pulling in a significant part of <ranges> just to implement this function, that doesn't work.
  1. We need to show that the code generated is comparable to what I suggested above (and if we added an optimization for sized sentinels that basically lowered to std::copy aka memmove). If that's not the case, then we should use the lower-level version, and also file a bug against Clang so we can find ways to improve the codegen of ranges. Note that this exercise it useful on its own.

Yeah, sorry, I know I'm a pain but I think the bar for using higher-level and more complicated constructs from lower-level ones should be high, and that's what I'm enforcing here.

1410

Any reason for not using this? IMO it's strictly clearer and it skips one level of indirection through ranges::distance if we already have a sized_range. And if we don't have a sized_range, we definitely don't want to be calling ranges::distance over it!

1511
1516–1517

When you have a sized_range, we should be able to use std::uninitialized_allocator_copy here with __deque_iterator, and update the size at the end only. When we don't have a sized_range, the for loop with emplace_back seems like the best we can do.

Also, can you file a bug report to track creating a high-level libc++-internal abstraction for chunked iterators? Ideally, we should be able to generalize the overloads of std::copy & friends we have at the top of this file, and also use std::uninitialized_allocator_copy here, and have it do just the right thing. Then, once we use that abstraction in uninitialized_allocator_copy, we'd get improved perf for free.

1524
1529–1538

I think you should be checking for a sized_range before you check for a bidirectional_range.

1539

You are missing a <vector> include here, and I must admit that makes me frown.

This also leads me to think that you should instead use __split_buffer here, that seems like the class used in both vector and deque to do these things. Furthermore, it should be possible in theory to instead do this:

  1. Allocate a buffer and append the elements to it
  2. Link that buffer to the beginning of the deque directly, to avoid having to reallocate and copy elements twice
  3. Copy just the n elements at the end of that buffer to the original beginning of the deque.

As you go through this exercise, it might be useful to take some notes about how the data structure works and we can promote them to documentation in the future.

1582
1585

I have the same kind of hunch as above here -- why can't we reuse the buffer we just allocated, at least in part?

1742
This revision now requires changes to proceed.Aug 11 2022, 9:44 AM
philnik abandoned this revision.May 22 2023, 1:44 PM
philnik marked 16 inline comments as done.