RecyclableList.AddRange(IEnumerable<T> source) performance improvements. Small source code cleanup.

This commit is contained in:
Marek Lemańczyk committed 2023-03-12 03:44:07 +01:00
1 parent efb393e13f
commit c211fb6ee4
2 files changed
+40 -23

No files matched your search

+36 -21
View File
@@ -548,7 +548,7 @@ namespace Recyclable.Collections
} }
long capacity = _capacity; long capacity = _capacity;
long oldLongCount = _longCount; long copied = _longCount;
int blockSize = _blockSize; int blockSize = _blockSize;
int targetItemIdx = _nextItemIndex; int targetItemIdx = _nextItemIndex;
int targetBlockIdx = _lastBlockIndex; int targetBlockIdx = _lastBlockIndex;
@@ -557,7 +557,7 @@ namespace Recyclable.Collections
Span<T> blockArraySpan; Span<T> blockArraySpan;
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
{ {
long requiredCapacity = oldLongCount + requiredAdditionalCapacity; long requiredCapacity = copied + requiredAdditionalCapacity;
if (capacity < requiredCapacity) if (capacity < requiredCapacity)
{ {
_ = EnsureCapacity(requiredCapacity); _ = EnsureCapacity(requiredCapacity);
@@ -594,51 +594,66 @@ namespace Recyclable.Collections
if (enumerator.MoveNext()) if (enumerator.MoveNext())
{ {
long available = capacity - targetItemIdx; long available = capacity - copied,
memoryBlocksSpan = new(_memoryBlocks); initialCount = copied;
memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length);
var memoryBlocksCount = memoryBlocksSpan.Length; var memoryBlocksCount = memoryBlocksSpan.Length;
while (true) while (true)
{ {
if (targetItemIdx + growByCount > capacity) if (copied + growByCount > capacity)
{ {
capacity = EnsureCapacity(capacity + growByCount); capacity = EnsureCapacity(capacity + growByCount);
memoryBlocksSpan = new(_memoryBlocks); memoryBlocksCount = _memoryBlocks.Length;
memoryBlocksCount = memoryBlocksSpan.Length; if (blockSize != _blockSize)
available = capacity - targetItemIdx; {
blockSize = _blockSize;
}
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
available = capacity - copied;
} }
blockArraySpan = memoryBlocksSpan[targetBlockIdx]; blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
for (i = 0; i < available; i++) for (i = 1; i <= available; i++)
{ {
blockArraySpan[targetItemIdx++] = enumerator.Current; blockArraySpan[targetItemIdx++] = enumerator.Current;
if (!enumerator.MoveNext()) if (!enumerator.MoveNext())
{ {
break; copied += i;
if (targetItemIdx < blockSize)
{
_lastBlockIndex = targetBlockIdx;
_nextItemIndex = targetItemIdx;
}
else
{
_lastBlockIndex = targetBlockIdx + 1;
_nextItemIndex = 0;
}
_longCount += copied - _longCount;
return;
} }
if (targetItemIdx == blockSize) if (targetItemIdx == blockSize)
{ {
targetBlockIdx++; targetBlockIdx++;
targetItemIdx = 0;
if (targetBlockIdx == memoryBlocksCount) if (targetBlockIdx == memoryBlocksCount)
{ {
break; break;
} }
targetItemIdx = 0; blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx]);
} }
} }
if (i < available) copied += available;
{
break;
}
} }
} }
_longCount = targetItemIdx;
_lastBlockIndex = targetBlockIdx;
_nextItemIndex = targetItemIdx;
} }
public void Clear() public void Clear()
@@ -320,7 +320,8 @@ namespace Recyclable.CollectionsTests
new object[] { "RecyclableArrayList<int>", _testData.ToRecyclableArrayList() }, new object[] { "RecyclableArrayList<int>", _testData.ToRecyclableArrayList() },
new object[] { "RecyclableList<int>", _testData.ToRecyclableList() }, new object[] { "RecyclableList<int>", _testData.ToRecyclableList() },
new object[] { "IList<int>", new CustomIList<int>(_testData) }, new object[] { "IList<int>", new CustomIList<int>(_testData) },
new object[] { "IEnumerable<int>", _testData }, new object[] { "IEnumerable<int> with non-enumerated count", _testData },
new object[] { "IEnumerable<int> without non-enumerated count", new EnumerableWithoutCount<int>(_testData) }
}; };
public static IEnumerable<object[]> AddRangeEmptyCollectionTypes => new[] public static IEnumerable<object[]> AddRangeEmptyCollectionTypes => new[]
@@ -433,7 +434,8 @@ namespace Recyclable.CollectionsTests
// Validate // Validate
var expectedData = testData.Concat(testData).ToArray(); var expectedData = testData.Concat(testData).ToArray();
_ = list.Capacity.Should().Be(229376L, "when capacity == 0, then we allocate as much memory as needed, only"); _ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedData.Length)
.And.BeLessThan((int)(expectedData.Length * 1.5));
_ = list.Should().HaveCount(expectedData.Length) _ = list.Should().HaveCount(expectedData.Length)
.And.ContainInConsecutiveOrder(expectedData) .And.ContainInConsecutiveOrder(expectedData)
.And.BeEquivalentTo(expectedData); .And.BeEquivalentTo(expectedData);