From addef85027aaaf5a6ab0efcac37cbbca46668a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sun, 12 Mar 2023 15:41:29 +0100 Subject: [PATCH] RecyclableList.AddRange(IEnumerable source) performance improvements. --- Recyclable.Collections/RecyclableList.cs | 218 ++++++++++++----------- 1 file changed, 116 insertions(+), 102 deletions(-) diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index cff289f..dca2c75 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -78,6 +78,120 @@ namespace Recyclable.Collections throw new ArgumentOutOfRangeException("index"); } + protected void AddRangeEnumerated(IEnumerable source, int growByCount) + { + long i, copied = _longCount; + + int blockSize = _blockSize, targetItemIdx = _nextItemIndex, targetBlockIdx = _lastBlockIndex; + + IEnumerator enumerator = source.GetEnumerator(); + if (!enumerator.MoveNext()) + { + return; + } + + long capacity = _capacity; + long available = capacity - copied; + + Span memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length); + Span blockArraySpan; + var memoryBlocksCount = memoryBlocksSpan.Length; + while (true) + { + if (copied + growByCount > capacity) + { + capacity = EnsureCapacity(capacity + growByCount); + memoryBlocksCount = _memoryBlocks.Length; + if (blockSize != _blockSize) + { + blockSize = _blockSize; + } + + memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); + available = capacity - copied; + } + + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize); + for (i = 1; i <= available; i++) + { + blockArraySpan[targetItemIdx++] = enumerator.Current; + + if (!enumerator.MoveNext()) + { + if (targetItemIdx < blockSize) + { + _lastBlockIndex = targetBlockIdx; + _nextItemIndex = targetItemIdx; + } + else + { + _lastBlockIndex = targetBlockIdx + 1; + _nextItemIndex = 0; + } + + _longCount += copied + i - _longCount; + return; + } + + if (targetItemIdx == blockSize) + { + targetBlockIdx++; + targetItemIdx = 0; + + if (targetBlockIdx == memoryBlocksCount) + { + break; + } + + blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize); + } + } + + copied += available; + } + } + + protected void AddRangeWithKnownCount(IEnumerable source, int requiredAdditionalCapacity) + { + long copied = _longCount; + int blockSize = _blockSize; + int targetItemIdx = _nextItemIndex; + int targetBlockIdx = _lastBlockIndex; + + Span memoryBlocksSpan; + Span blockArraySpan; + long requiredCapacity = copied + requiredAdditionalCapacity; + if (_capacity < requiredCapacity) + { + _ = EnsureCapacity(requiredCapacity); + blockSize = _blockSize; + } + + var memoryBlocksCount = _memoryBlocks.Length; + memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); + blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); + foreach (var item in source) + { + blockArraySpan[targetItemIdx++] = item; + if (targetItemIdx == blockSize) + { + targetBlockIdx++; + targetItemIdx = 0; + if (targetBlockIdx == memoryBlocksCount) + { + break; + } + + blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); + } + } + + _longCount = requiredCapacity; + _lastBlockIndex = targetBlockIdx; + _nextItemIndex = targetItemIdx; + return; + } + protected static long DoIndexOf(T item, in T[][] memoryBlocks, int lastBlockIndex, int blockSize, int nextItemIndex) { Span memoryBlocksSpan = new(memoryBlocks); @@ -547,113 +661,13 @@ namespace Recyclable.Collections return; } - long capacity = _capacity; - long copied = _longCount; - int blockSize = _blockSize; - int targetItemIdx = _nextItemIndex; - int targetBlockIdx = _lastBlockIndex; - - Span memoryBlocksSpan; - Span blockArraySpan; if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) { - long requiredCapacity = copied + requiredAdditionalCapacity; - if (capacity < requiredCapacity) - { - _ = EnsureCapacity(requiredCapacity); - blockSize = _blockSize; - } - - var memoryBlocksCount = _memoryBlocks.Length; - memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); - blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); - foreach (var item in source) - { - blockArraySpan[targetItemIdx++] = item; - if (targetItemIdx == blockSize) - { - targetBlockIdx++; - targetItemIdx = 0; - if (targetBlockIdx == memoryBlocksCount) - { - break; - } - - blockArraySpan = new Span(memoryBlocksSpan[targetBlockIdx], 0, blockSize); - } - } - - _longCount = requiredCapacity; - _lastBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; + AddRangeWithKnownCount(source, requiredAdditionalCapacity); return; } - long i; - using var enumerator = source.GetEnumerator(); - - if (enumerator.MoveNext()) - { - long available = capacity - copied, - initialCount = copied; - - memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length); - var memoryBlocksCount = memoryBlocksSpan.Length; - while (true) - { - if (copied + growByCount > capacity) - { - capacity = EnsureCapacity(capacity + growByCount); - memoryBlocksCount = _memoryBlocks.Length; - if (blockSize != _blockSize) - { - blockSize = _blockSize; - } - - memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount); - available = capacity - copied; - } - - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize); - for (i = 1; i <= available; i++) - { - blockArraySpan[targetItemIdx++] = enumerator.Current; - - if (!enumerator.MoveNext()) - { - copied += i; - if (targetItemIdx < blockSize) - { - _lastBlockIndex = targetBlockIdx; - _nextItemIndex = targetItemIdx; - } - else - { - _lastBlockIndex = targetBlockIdx + 1; - _nextItemIndex = 0; - } - - _longCount += copied - _longCount; - return; - } - - if (targetItemIdx == blockSize) - { - targetBlockIdx++; - targetItemIdx = 0; - - if (targetBlockIdx == memoryBlocksCount) - { - break; - } - - blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize); - } - } - - copied += available; - } - } + AddRangeEnumerated(source, growByCount); } public void Clear()