Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions src/libraries/System.Linq/src/System/Linq/Chunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Linq
{
Expand Down Expand Up @@ -50,26 +51,25 @@ public static IEnumerable<TSource[]> Chunk<TSource>(this IEnumerable<TSource> so
private static IEnumerable<TSource[]> ChunkIterator<TSource>(IEnumerable<TSource> source, int size)
{
using IEnumerator<TSource> e = source.GetEnumerator();
while (e.MoveNext())
{
TSource[] chunk = new TSource[size];
chunk[0] = e.Current;

int i = 1;
for (; i < chunk.Length && e.MoveNext(); i++)
if (e.MoveNext())
{
List<TSource> chunkBuilder = new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend presizing the List up to a "reasonable" chunk size (e.g. new(Math.Min(chunkSize, 128))) to prevent unneeded resizes when the first chunk is filled. You could use benchmarks to determine a good cutoff number.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes there's going to be enough to fill a chunk. Do we know that's a good assumption? Benchmarks here can be used to show that any size is optimal, so how are you going to choose the benchmarks to use? To me, the reasonable sizes to use are either the chunk size (which the presence of this pr is dismissing) or the small, default value list uses internally so this method is out of the guessing game.

while (true)
{
chunk[i] = e.Current;
}
do
{
chunkBuilder.Add(e.Current);
}
while (chunkBuilder.Count < size && e.MoveNext());

if (i == chunk.Length)
{
yield return chunk;
}
else
{
Array.Resize(ref chunk, i);
yield return chunk;
yield break;
yield return chunkBuilder.ToArray();

if (chunkBuilder.Count < size || !e.MoveNext())
{
yield break;
}
chunkBuilder.Clear();
}
}
}
Expand Down
11 changes: 10 additions & 1 deletion src/libraries/System.Linq/tests/ChunkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,14 @@ public void AddingToSourceBeforeIterating()

Assert.Equal(new[] {new[] {9999, 0, 888}, new[] {-1, 66, -777}, new[] {1, 2, -12345}, new[] {10}}, chunks);
}

// reproduces https://github.com/dotnet/runtime/issues/67132
[Fact]
public void DoesNotPrematurelyAllocateHugeArray()
{
int[][] chunks = Enumerable.Range(0, 10).Chunk(int.MaxValue).ToArray();

Assert.Equal(new[] { Enumerable.Range(0, 10).ToArray() }, chunks);
}
}
}
}