-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Iterator trait TrustedLen to enable better FromIterator / Extend #37306
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
Benchmark and result at https://gist.github.com/bluss/baa98105d141cff3949dda1c1f2d8cce Using SetLenOnDrop was required to avoid the aliasing troubles in the case of The .chain() cases unfortunately are not optimized well (remains as one loop that goes through the chain's state logic, instead of splitting it into two loops for the front and back part). |
I believe at least this adapter for Result also should have TrustedLen implemented on it: https://github.com/rust-lang/rust/blob/master/src/libcore/result.rs#L997. I know it currently doesn't have a size_hint, but @eddyb had me add it in #37270, since we believe it should have it. |
@Mark-Simulacrum Thanks. Yes, there's a ton of iterators that will need the marker |
Nice! Does this mean that we can remove some of the specialization around extending vectors from slices as well? |
@alexcrichton I think it does. We can probably remove .extend_from_slice()'s implementation entirely. |
Nevermind, extend_from_slice is Clone and extend(&[T]) requires Copy. |
The new Vec::extend covers the duties of .extend_from_slice() and some previous specializations.
Ah, that's how to do it. Now extend_from_slice's own implementation is gone, and the specialization that redirected to it. (Sorry #37094 !). I verified performance using a simple benchmark (only). |
Is the specialization of |
The more I read this as well the more I feel like we need more eyes on this. This kind of usage of specialization seems like it could make for some really nasty bugs if we get this wrong. Could we perhaps start more conservatively with implementations of The addition of |
Ah and cc @rust-lang/libs |
Right, I think it should compile to the same thing. I had the idea that it could reuse I would like to keep the Vec::append specialization, since it's less code for the optimizer to chew on, it doesn't rely on release mode optimizations, and because it's easy to keep what's there. The Range iterators should be unproblematic, given TrustedLen's contract. |
We in general have a large problem of lots of code to chew on in LLVM, and while specialization can indeed solve a lot of that I'd personally prefer to err on the side of less source code as there's just fewer unsafe blocks and fewer hoops you need to jump through to see how Perhaps some debug assertions could be added to the |
A reason that there is no I guess we can add a debug assertion to where this is used. |
|
@rfcbot fcp merge I personally feel that we should get rid of the other specialization of
Right it gives LLVM more to chew on, but we've practically never optimized the standard library for minimizing the amount of code we send to LLVM, so I don't think now's really the time to start. Removing the extra specialization keeps it a little more understandable as there's only one specialization to consider, not multiple. |
Team member @alexcrichton has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
@alexcrichton Oh, I can be on board with that. As long as Vec::append's own implementation stays as it is. |
Oh yeah I wouldn't imagine any change to the implementation of |
This now produces as good code (with optimizations) using the TrustedLen codepath.
The special case for |
Benchmark file for this PR https://gist.github.com/bluss/3828cadbd92f5a94d020a474b9879f6c One highlight is the
pub fn map_fast(l: &[(u32, u32)]) -> Vec<u32> {
let mut result = Vec::with_capacity(l.len());
for i in 0..l.len() {
unsafe {
*result.get_unchecked_mut(i) = l[i].0;
result.set_len(i);
}
}
result
} and the other one is just |
Something that's not in this PR is to replace the for loop in Remember that most of these benchmarks are best cases, where the iterator element is a small value and where loop optimizations like unrolling or converting to memcpy have a big impact. |
Waiting for @Kimundi to chime in. |
Opened a tracking issue and linked it in. |
🔔 This is now entering its final comment period, as per the review above. 🔔 psst @alexcrichton, I wasn't able to add the |
Looks like travis error is legit? |
Yes, sorry, syntax error in the stability attr. Fixed by amending that commit. |
@bors: r+ |
📌 Commit f0e6b90 has been approved by |
Add Iterator trait TrustedLen to enable better FromIterator / Extend This trait attempts to improve FromIterator / Extend code by enabling it to trust the iterator to produce an exact number of elements, which means that reallocation needs to happen only once and is moved out of the loop. `TrustedLen` differs from `ExactSizeIterator` in that it attempts to include _more_ iterators by allowing for the case that the iterator's len does not fit in `usize`. Consumers must check for this case (for example they could panic, since they can't allocate a collection of that size). For example, chain can be TrustedLen and all numerical ranges can be TrustedLen. All they need to do is to report an exact size if it fits in `usize`, and `None` as the upper bound otherwise. The trait describes its contract like this: ``` An iterator that reports an accurate length using size_hint. The iterator reports a size hint where it is either exact (lower bound is equal to upper bound), or the upper bound is `None`. The upper bound must only be `None` if the actual iterator length is larger than `usize::MAX`. The iterator must produce exactly the number of elements it reported. This trait must only be implemented when the contract is upheld. Consumers of this trait must inspect `.size_hint()`’s upper bound. ``` Fixes rust-lang#37232
Add Iterator trait TrustedLen to enable better FromIterator / Extend This trait attempts to improve FromIterator / Extend code by enabling it to trust the iterator to produce an exact number of elements, which means that reallocation needs to happen only once and is moved out of the loop. `TrustedLen` differs from `ExactSizeIterator` in that it attempts to include _more_ iterators by allowing for the case that the iterator's len does not fit in `usize`. Consumers must check for this case (for example they could panic, since they can't allocate a collection of that size). For example, chain can be TrustedLen and all numerical ranges can be TrustedLen. All they need to do is to report an exact size if it fits in `usize`, and `None` as the upper bound otherwise. The trait describes its contract like this: ``` An iterator that reports an accurate length using size_hint. The iterator reports a size hint where it is either exact (lower bound is equal to upper bound), or the upper bound is `None`. The upper bound must only be `None` if the actual iterator length is larger than `usize::MAX`. The iterator must produce exactly the number of elements it reported. This trait must only be implemented when the contract is upheld. Consumers of this trait must inspect `.size_hint()`’s upper bound. ``` Fixes #37232
Was there a particular reason behind not taking advantage of |
No reason, if it makes sense, just explore it |
This trait attempts to improve FromIterator / Extend code by enabling it to trust the iterator to produce an exact number of elements, which means that reallocation needs to happen only once and is moved out of the loop.
TrustedLen
differs fromExactSizeIterator
in that it attempts to include more iterators by allowing for the case that the iterator's len does not fit inusize
. Consumers must check for this case (for example they could panic, since they can't allocate a collection of that size).For example, chain can be TrustedLen and all numerical ranges can be TrustedLen. All they need to do is to report an exact size if it fits in
usize
, andNone
as the upper bound otherwise.The trait describes its contract like this:
Fixes #37232