RecyclableList.AddRange(IList<T> items) performance improvements

This commit is contained in:
Marek Lemańczyk committed 2023-03-11 19:51:51 +01:00
1 parent 44c8286f85
commit fb3290f853
1 file changed
+30 -20
+30 -20
View File
@@ -443,41 +443,51 @@ namespace Recyclable.Collections
_longCount = targetCapacity; _longCount = targetCapacity;
} }
public void AddRange(in IList<T> items) public void AddRange(IList<T> items)
{ {
long sourceItemsCount = items.Count; long targetCapacity = _longCount + items.Count;
long oldLongCount = _longCount;
long targetCapacity = oldLongCount + sourceItemsCount;
if (_capacity < targetCapacity) if (_capacity < targetCapacity)
{ {
_ = EnsureCapacity(targetCapacity); _ = EnsureCapacity(targetCapacity);
} }
int blockSize = _blockSize; int blockSize = _blockSize,
int targetItemIdx = (int)(oldLongCount % blockSize); targetBlockIndex = _lastBlockIndex,
int targetBlockIdx = (int)(oldLongCount / blockSize) + (targetItemIdx > 0 ? 1 : 0); memoryBlockCount = _memoryBlocks.Length;
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
var memoryBlocksCount = memoryBlocksSpan.Length; T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? _blockArrayPool.Rent(items.Count) : new T[items.Count];
Span<T> targetBlockSpan = new(memoryBlocksSpan[targetBlockIdx]); try
for (int i = 0; i < sourceItemsCount; i++)
{ {
targetBlockSpan[targetItemIdx++] = items[i]; items.CopyTo(itemsBuffer, 0);
if (targetItemIdx == blockSize)
Span<T> itemsSpan = new(itemsBuffer, 0, items.Count);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
{ {
targetBlockIdx++; itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
if (targetBlockIdx == memoryBlocksCount) itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (targetBlockIndex == memoryBlockCount)
{ {
break; break;
} }
targetBlockSpan = new Span<T>(memoryBlocksSpan[targetBlockIdx]); targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
targetItemIdx = 0;
}
} }
itemsSpan.CopyTo(targetBlockArraySpan);
_longCount = targetCapacity; _longCount = targetCapacity;
_lastBlockIndex = targetBlockIdx; _lastBlockIndex = targetBlockIndex;
_nextItemIndex = targetItemIdx; _nextItemIndex = itemsSpan.Length;
}
finally
{
if (items.Count >= RecyclableDefaults.MinPooledArrayLength)
{
_blockArrayPool.Return(itemsBuffer);
}
}
} }
public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength) public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)