Optimize long-list AddRange for stack

This commit is contained in:
Marek Lemańczyk committed 2025-07-17 00:21:15 +02:00
1 parent 4274f4dcbc
commit 1aedfb2cbc
2 files changed
+77 -4

No files matched your search

@@ -281,27 +281,62 @@ namespace Recyclable.Collections
return;
}
EnsureCapacity(stack, stack._count + longCount);
int blockSize = items._blockSize;
int fullBlocks = (int)(longCount >> items._blockSizePow2BitShift);
int lastBlockLength = (int)(longCount & items._blockSizeMinus1);
RecyclableArrayPoolChunk<T> chunk = stack._current;
int index = chunk.Index;
int chunkLength = chunk.Value.Length;
for (int blockIndex = 0; blockIndex < fullBlocks; blockIndex++)
{
T[] block = items._memoryBlocks[blockIndex];
for (int i = 0; i < blockSize; i++)
int sourceIndex = 0;
while (sourceIndex < blockSize)
{
stack.Push(block[i]);
if (index == chunkLength)
{
chunk.Index = index;
Grow(stack);
chunk = stack._current;
index = chunk.Index;
chunkLength = chunk.Value.Length;
}
int toCopy = Math.Min(chunkLength - index, blockSize - sourceIndex);
Array.Copy(block, sourceIndex, chunk.Value, index, toCopy);
sourceIndex += toCopy;
index += toCopy;
}
}
if (lastBlockLength > 0)
{
T[] block = items._memoryBlocks[fullBlocks];
for (int i = 0; i < lastBlockLength; i++)
int sourceIndex = 0;
while (sourceIndex < lastBlockLength)
{
stack.Push(block[i]);
if (index == chunkLength)
{
chunk.Index = index;
Grow(stack);
chunk = stack._current;
index = chunk.Index;
chunkLength = chunk.Value.Length;
}
int toCopy = Math.Min(chunkLength - index, lastBlockLength - sourceIndex);
Array.Copy(block, sourceIndex, chunk.Value, index, toCopy);
sourceIndex += toCopy;
index += toCopy;
}
}
chunk.Index = index;
stack._count += longCount;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -404,5 +404,43 @@ namespace Recyclable.CollectionsTests
_ = stack.Should().Equal(expected);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
public void AddRangeShouldAddLongListItemsInCorrectOrder(int itemsCount)
{
long[] data = RecyclableLongListTestData.CreateTestData(itemsCount).ToArray();
using var source = new RecyclableLongList<long>(data);
using var stack = new RecyclableStack<long>();
stack.AddRange(source);
List<long> expected = new(itemsCount);
expected.AddRange(data);
expected.Reverse();
_ = stack.Count.Should().Be(itemsCount);
_ = stack.Should().Equal(expected);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))]
public void AddRangeShouldNotOverrideItemsFromLongList(int itemsCount)
{
long[] data = RecyclableLongListTestData.CreateTestData(itemsCount).ToArray();
using var source = new RecyclableLongList<long>(data);
using var stack = new RecyclableStack<long>();
stack.AddRange(source);
stack.AddRange(source);
List<long> expected = new(itemsCount * 2);
expected.AddRange(data);
expected.AddRange(data);
expected.Reverse();
_ = stack.Count.Should().Be(itemsCount * 2);
_ = stack.Should().Equal(expected);
}
}
}