You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a tracking issue for the .next_chunk() and .next_chunk_back() method on iterators which allows you to advance the iterator N elements and return an array [T; N]
Public API
use core::array;traitIterator{typeItem;fnnext_chunk<constN:usize>(&mutself) -> Result<[Self::Item;N], array::IntoIter<Self::Item,N>>whereSelf:Sized;}traitDoubleEndedIterator{fnnext_chunk_back<constN:usize>(&mutself) -> Result<[Self::Item;N], array::IntoIter<Self::Item,N>>whereSelf:Sized;}
Should this method support N=0? itertools does not, and it sounds plausible; however, if we want to do the same, we will have the same concern as <[T]>::array_chunks() that cannot be stabilized until generic_const_exprs or some subset will be stabilized.
I'm wondering about the intended uses of this API.
If it is for performance (autovectorization) then it seems fairly brittle. I just tried doing this benchmark:
#[bench]fnbench_next_chunk(b:&mutBencher){let v = vec![13u8;2048];
b.iter(|| {constCHUNK:usize = 8;letmut sum = [0u32;CHUNK];letmut iter = black_box(v.clone()).into_iter();whileletOk(chunk) = iter.next_chunk::<CHUNK>(){for i in0..CHUNK{
sum[i] += chunk[i]asu32;}}
sum
})}
and it took 17,272 ns/iter when compiled with target-cpu=znver2. With a handrolled implementation I got 211 ns/iter. I encountered some 10x slowdowns in a few other combinations. Sometimes codegen-units=1 produced even worse assembly because vectorizer did silly things such as loading one byte at a time into a simd register with vpinsrb.
And that's with direct, linear memory access on a Vec::IntoIter which should be relatively straight-forward to turn into masked simd loads. Many iterators yield references. A [&T; N] would need a gather-load which is expensive and only available on newer CPUs, this is unlike slice::array_chunks which yields &[T; N]. We could try specializing slice.iter().next_chunk() but then you might as well use array_chunks. And of course it'll get more complex with additional adapters.
Another limitation is that it can't be used in a for-in loop or chained further with other iterator adapters, an issue #92393 didn't have.
Reacted by Kornel, Proc, Suchir Kavi, Istvan and Totodo
This is probably an example of a case where you should first use copied(), and then chunk it, and then you turn that array into a simd type for the work, before possibly turning it back at the end of your loop.
And the change into and out of simd form will have a cost, so you'll want to ensure you're doing enough work inside each loop pass or it might exceed the cost of the scalar only code.
yeah that was just an example to illustrate that the default implementation is suboptimal and we'll need optimized implementations on most linear-memory sources and length-preserving adapters.
Myself I would imagine using this most on iterators that aren't linear memory sources, and "the point" would be to have a clean and unified way to turn some crazy data source into simple SIMD chunks.
So I'm finding myself caring less and less about the actual naming. I've wanted this feature at least 3 times in the last year and had to write my own version to get around. Can we just pick something and get the feature stabilized?
Reacted by LambdAurora, Matt Furden, Zebreus, Alexander T. Haselton, Aflutter, z17, Harry Maclean, Evgeniy Dushistov, Caio, meppu and 16 more
I'm not sure what that issue has to do with next_chunk specifically. It seems to be mostly about the codegen for it.collect::<Vec<_>>(), which I think is easily disturbed by anything mutating the iterator.
It should be allowed and return a zero-length array without advancing the iterator. No good reason to forbid it.
For next_chunk it might not be a problem but for the chunks iterators on slices and for iterator.array_chunks() it is. So we might as well do something about it here
I disagree. For those, it's necessary by definition. There's no reason to apply the same logic here besides, in my opinion, a misguided desire for consistency. The behavior of next_chunk is totally reasonable for array length 0, unlike the others you mentioned.
"Doing something about it" will pretty much require const expressions just like all the other ones, needlessly delaying the stabilization.
The other alternative here is to piggy-back on some future language feature for in-place initialization, which also has to address fallibility somehow. The downside of transparently making the current API lower to "out-ptr for Some/Ok payload" ABI is that it also fails transparently: whenever move elimination doesn't fire for any reason, or a pointer to an entire Result<[T; N], _> is required, codegen will be just as inefficient as today, without any clue at the source level.
In contrast, most in-place-init proposals have some explicit opt-in at the call site that guarantees no extra moves or temporaries. That's necessary for soundness in some use cases, but it's also useful for types (here: array lengths) large enough to overflow the stack. The downside is that there's some extra annotation burden, which may be a bad trade-off for an API that's partly about convenience. But depending on which (if any) approach is adopted, maybe the common case is succinct enough to be attractive for this API, too.
Are we able to add DoubleEndedIterator::next_chunk_back underneath this tracking issue? The person who made the ACP for this method and the original PR had it marked under #![feature(iter_next_chunk)] feature gated. I picked up the PR here to get it over the finish line due to inactivity.
Activity
ChayimFriedman2 commentedon Jun 21, 2022
Should this method support
N=0?itertoolsdoes not, and it sounds plausible; however, if we want to do the same, we will have the same concern as<[T]>::array_chunks()that cannot be stabilized untilgeneric_const_exprsor some subset will be stabilized.the8472 commentedon Jun 25, 2022
I'm wondering about the intended uses of this API.
If it is for performance (autovectorization) then it seems fairly brittle. I just tried doing this benchmark:
and it took
17,272 ns/iterwhen compiled withtarget-cpu=znver2. With a handrolled implementation I got211 ns/iter. I encountered some 10x slowdowns in a few other combinations. Sometimescodegen-units=1produced even worse assembly because vectorizer did silly things such as loading one byte at a time into a simd register withvpinsrb.And that's with direct, linear memory access on a
Vec::IntoIterwhich should be relatively straight-forward to turn into masked simd loads. Many iterators yield references. A[&T; N]would need a gather-load which is expensive and only available on newer CPUs, this is unlikeslice::array_chunkswhich yields&[T; N]. We could try specializingslice.iter().next_chunk()but then you might as well usearray_chunks. And of course it'll get more complex with additional adapters.Another limitation is that it can't be used in a for-in loop or chained further with other iterator adapters, an issue #92393 didn't have.
Lokathor commentedon Sep 7, 2022
This is probably an example of a case where you should first use
copied(), and then chunk it, and then you turn that array into a simd type for the work, before possibly turning it back at the end of your loop.And the change into and out of simd form will have a cost, so you'll want to ensure you're doing enough work inside each loop pass or it might exceed the cost of the scalar only code.
the8472 commentedon Sep 7, 2022
yeah that was just an example to illustrate that the default implementation is suboptimal and we'll need optimized implementations on most linear-memory sources and length-preserving adapters.
Lokathor commentedon Sep 7, 2022
Mm, yeah.
Myself I would imagine using this most on iterators that aren't linear memory sources, and "the point" would be to have a clean and unified way to turn some crazy data source into simple SIMD chunks.
rossmacarthur commentedon Nov 1, 2022
Might be good to align the naming of this function with
Iterator::array_chunks, i.e. eitherIterator::chunksIterator::next_chunkOr
Iterator::array_chunksIterator::next_array_chunkmark-i-m commentedon Jan 24, 2023
So I'm finding myself caring less and less about the actual naming. I've wanted this feature at least 3 times in the last year and had to write my own version to get around. Can we just pick something and get the feature stabilized?
ZhennanWu commentedon Apr 23, 2023
I believe the current implementation also suffers from this compiler optimization issue: #110734
Godbolt: https://godbolt.org/z/Te5Wo9eh7
the8472 commentedon Apr 23, 2023
I'm not sure what that issue has to do with
next_chunkspecifically. It seems to be mostly about the codegen forit.collect::<Vec<_>>(), which I think is easily disturbed by anything mutating the iterator.pitaj commentedon May 25, 2023
Let's get this stabilized. Here are my answers to the Unresolved Questions:
next_chunkseems like the best option to me.Probably, but that should be under a separate feature and not block this.
It should be allowed and return a zero-length array without advancing the iterator. No good reason to forbid it.
the8472 commentedon May 25, 2023
For
next_chunkit might not be a problem but for the chunks iterators on slices and for iterator.array_chunks() it is. So we might as well do something about it herepitaj commentedon May 25, 2023
I disagree. For those, it's necessary by definition. There's no reason to apply the same logic here besides, in my opinion, a misguided desire for consistency. The behavior of
next_chunkis totally reasonable for array length 0, unlike the others you mentioned."Doing something about it" will pretty much require const expressions just like all the other ones, needlessly delaying the stabilization.
the8472 commentedon May 25, 2023
Well, it would prevent implementations of
next_chunkusing those things internally22 remaining items
hanna-kruppe commentedon Dec 24, 2025
The other alternative here is to piggy-back on some future language feature for in-place initialization, which also has to address fallibility somehow. The downside of transparently making the current API lower to "out-ptr for Some/Ok payload" ABI is that it also fails transparently: whenever move elimination doesn't fire for any reason, or a pointer to an entire
Result<[T; N], _>is required, codegen will be just as inefficient as today, without any clue at the source level.In contrast, most in-place-init proposals have some explicit opt-in at the call site that guarantees no extra moves or temporaries. That's necessary for soundness in some use cases, but it's also useful for types (here: array lengths) large enough to overflow the stack. The downside is that there's some extra annotation burden, which may be a bad trade-off for an API that's partly about convenience. But depending on which (if any) approach is adopted, maybe the common case is succinct enough to be attractive for this API, too.
Rollup merge of rust-lang#149131 - bend-n:optimize_slice_iter_next_ch…
Rollup merge of rust-lang#150230 - bend-n:trusted_len_spec_for_iter_n…
Rollup merge of rust-lang#150230 - bend-n:trusted_len_spec_for_iter_n…
Unrolled build for #150230
asder8215 commentedon May 19, 2026
Are we able to add
DoubleEndedIterator::next_chunk_backunderneath this tracking issue? The person who made the ACP for this method and the original PR had it marked under#![feature(iter_next_chunk)]feature gated. I picked up the PR here to get it over the finish line due to inactivity.Rollup merge of rust-lang#156737 - asder8215:double_ended_iterator_ne…
Auto merge of #156737 - asder8215:double_ended_iterator_next_chunk_ba…
Rollup merge of rust-lang#156737 - asder8215:double_ended_iterator_ne…
Unrolled build for #156737
aapoalas commentedon Jul 3, 2026
Added
.next_chunk_back()to the tracking issue.asder8215 commentedon Jul 3, 2026
@aapoalas to note,
.next_chunk_back()should be underneath theDoubleEndedIteratortrait notIteratortrait.giang299 commentedon Jul 11, 2026
IT MUST BE next_chunk()
next_chunk is amz naming. we already have a Vec::chunks(),..etc before, it's universal.