RecyclableList.Resize performance improvements.
This commit is contained in:
1 parent
ed11e42b48
commit
60494c3219
1 file changed
+25
-24
@@ -14,13 +14,13 @@ namespace Recyclable.Collections
|
|||||||
private static readonly T[][] _emptyMemoryBlocksArray = new T[0][];
|
private static readonly T[][] _emptyMemoryBlocksArray = new T[0][];
|
||||||
private static readonly T[] _emptyBlockArray = new T[0];
|
private static readonly T[] _emptyBlockArray = new T[0];
|
||||||
|
|
||||||
//private MemoryBlockInfo<T> _memoryBlockInfo;
|
|
||||||
private int _blockSize;
|
private int _blockSize;
|
||||||
private byte _blockSizePow2Shift;
|
private byte _blockSizePow2Shift;
|
||||||
private int _blockSizeMinus1;
|
private int _blockSizeMinus1;
|
||||||
private bool _blockPoolingRequired;
|
private bool _blockPoolingRequired;
|
||||||
private int _nextItemBlockIndex;
|
private int _nextItemBlockIndex;
|
||||||
private int _nextItemIndex;
|
private int _nextItemIndex;
|
||||||
|
private int _reservedBlockCount;
|
||||||
|
|
||||||
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ namespace Recyclable.Collections
|
|||||||
set => _longCount = value;
|
set => _longCount = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReservedBlockCount => checked((int)(_capacity >> _blockSizePow2Shift) + ((_capacity & _blockSizeMinus1) > 0 ? 1 : 0));
|
public int ReservedBlockCount => _reservedBlockCount;
|
||||||
public int BlockSize => _blockSize;
|
public int BlockSize => _blockSize;
|
||||||
public int NextItemBlockIndex => _nextItemBlockIndex;
|
public int NextItemBlockIndex => _nextItemBlockIndex;
|
||||||
public int NextItemIndex => _nextItemIndex;
|
public int NextItemIndex => _nextItemIndex;
|
||||||
@@ -122,7 +122,7 @@ namespace Recyclable.Collections
|
|||||||
if (copied + growByCount > capacity)
|
if (copied + growByCount > capacity)
|
||||||
{
|
{
|
||||||
capacity = EnsureCapacity(this, capacity + growByCount);
|
capacity = EnsureCapacity(this, capacity + growByCount);
|
||||||
memoryBlocksCount = _memoryBlocks.Length;
|
memoryBlocksCount = _reservedBlockCount;
|
||||||
if (blockSize != _blockSize)
|
if (blockSize != _blockSize)
|
||||||
{
|
{
|
||||||
blockSize = _blockSize;
|
blockSize = _blockSize;
|
||||||
@@ -190,7 +190,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
var memoryBlocksCount = ReservedBlockCount;
|
var memoryBlocksCount = ReservedBlockCount;
|
||||||
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
|
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
|
||||||
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
|
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
|
||||||
foreach (var item in source)
|
foreach (var item in source)
|
||||||
{
|
{
|
||||||
blockArraySpan[targetItemIdx++] = item;
|
blockArraySpan[targetItemIdx++] = item;
|
||||||
@@ -256,10 +256,9 @@ namespace Recyclable.Collections
|
|||||||
/// <returns>The maximum no. of items <paramref name="list"/> can store.</returns>
|
/// <returns>The maximum no. of items <paramref name="list"/> can store.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
protected static long Resize(RecyclableList<T> list, int minBlockSize, byte minBlockSizePow2Shift, long newCapacity)
|
protected static long Resize(RecyclableList<T> list, int minBlockSize, byte minBlockSizePow2Shift, long newCapacity)
|
||||||
//protected static long Resize(RecyclableList<T> list, in MemoryBlockInfo<T> blockInfo, long newCapacity)
|
|
||||||
{
|
{
|
||||||
ArrayPool<T> blockArrayPool = list._blockArrayPool;
|
ArrayPool<T> blockArrayPool = list._blockArrayPool;
|
||||||
int sourceBlockCount = checked((int)(list._capacity >> minBlockSizePow2Shift));
|
int sourceBlockCount = list._reservedBlockCount;
|
||||||
int requiredBlockCount = checked((int)(newCapacity >> minBlockSizePow2Shift) + ((newCapacity & (minBlockSize - 1)) > 0 ? 1 : 0));
|
int requiredBlockCount = checked((int)(newCapacity >> minBlockSizePow2Shift) + ((newCapacity & (minBlockSize - 1)) > 0 ? 1 : 0));
|
||||||
int blockIndex;
|
int blockIndex;
|
||||||
Span<T[]> memoryBlocksSpan;
|
Span<T[]> memoryBlocksSpan;
|
||||||
@@ -267,29 +266,28 @@ namespace Recyclable.Collections
|
|||||||
// Release excessive blocks if we're downsizing
|
// Release excessive blocks if we're downsizing
|
||||||
if (requiredBlockCount < sourceBlockCount && list._blockPoolingRequired)
|
if (requiredBlockCount < sourceBlockCount && list._blockPoolingRequired)
|
||||||
{
|
{
|
||||||
memoryBlocksSpan = new(list._memoryBlocks, requiredBlockCount, sourceBlockCount - requiredBlockCount);
|
memoryBlocksSpan = new(list._memoryBlocks, requiredBlockCount, requiredBlockCount - sourceBlockCount);
|
||||||
blockIndex = 0;
|
blockIndex = 0;
|
||||||
while (blockIndex < memoryBlocksSpan.Length)
|
while (blockIndex < memoryBlocksSpan.Length)
|
||||||
{
|
{
|
||||||
blockArrayPool.Return(memoryBlocksSpan[blockIndex++]!, NeedsClearing);
|
blockArrayPool.Return(memoryBlocksSpan[blockIndex++], NeedsClearing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<T[]> memoryBlocksPool = list._memoryBlocksPool;
|
ArrayPool<T[]> memoryBlocksPool = list._memoryBlocksPool;
|
||||||
|
|
||||||
// Allocate new memory block for all arrays
|
// Allocate new memory block for all arrays
|
||||||
T[][]? newMemoryBlocks = requiredBlockCount < RecyclableDefaults.MinPooledArrayLength
|
bool memoryBlockPoolingRequired = requiredBlockCount >= RecyclableDefaults.MinPooledArrayLength;
|
||||||
? new T[requiredBlockCount][]
|
T[][] newMemoryBlocks = memoryBlockPoolingRequired ? memoryBlocksPool.Rent(requiredBlockCount) : new T[requiredBlockCount][];
|
||||||
: memoryBlocksPool.Rent(requiredBlockCount);
|
|
||||||
|
|
||||||
// Copy arrays from the old memory block for all arrays
|
// Copy arrays from the old memory block for all arrays
|
||||||
if (sourceBlockCount > 0)
|
if (sourceBlockCount > 0)
|
||||||
{
|
{
|
||||||
Array.Copy(list._memoryBlocks!, newMemoryBlocks, sourceBlockCount);
|
Array.Copy(list._memoryBlocks, newMemoryBlocks, sourceBlockCount);
|
||||||
// We can now return the old memory block for all arrays itself
|
// We can now return the old memory block for all arrays itself
|
||||||
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
|
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
memoryBlocksPool.Return(list._memoryBlocks!, true);
|
memoryBlocksPool.Return(list._memoryBlocks, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +296,7 @@ namespace Recyclable.Collections
|
|||||||
// Allocate arrays for any new blocks
|
// Allocate arrays for any new blocks
|
||||||
if (requiredBlockCount > sourceBlockCount)
|
if (requiredBlockCount > sourceBlockCount)
|
||||||
{
|
{
|
||||||
memoryBlocksSpan = new(newMemoryBlocks, sourceBlockCount, newMemoryBlocks.Length - sourceBlockCount);
|
memoryBlocksSpan = new(newMemoryBlocks, sourceBlockCount, requiredBlockCount - sourceBlockCount);
|
||||||
if (list._blockPoolingRequired)
|
if (list._blockPoolingRequired)
|
||||||
{
|
{
|
||||||
if (sourceBlockCount == 0)
|
if (sourceBlockCount == 0)
|
||||||
@@ -327,7 +325,8 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (long)newMemoryBlocks.Length << minBlockSizePow2Shift;
|
list._reservedBlockCount = requiredBlockCount;
|
||||||
|
return (long)requiredBlockCount << minBlockSizePow2Shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
@@ -378,7 +377,7 @@ namespace Recyclable.Collections
|
|||||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||||
SetupBlockArrayPooling(this, minBlockSize, blockArrayPool);
|
SetupBlockArrayPooling(this, minBlockSize, blockArrayPool);
|
||||||
_capacity = Resize(this, minBlockSize, _blockSizePow2Shift, expectedItemsCount.Value);
|
_capacity = Resize(this, minBlockSize, _blockSizePow2Shift, expectedItemsCount.Value);
|
||||||
if (_memoryBlocks!.Length > 0 && _blockSize != _memoryBlocks[0].Length)
|
if (_blockSize != _memoryBlocks[0].Length && _capacity > 0)
|
||||||
{
|
{
|
||||||
SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool);
|
SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool);
|
||||||
_blockSizeMinus1 = _blockSize - 1;
|
_blockSizeMinus1 = _blockSize - 1;
|
||||||
@@ -406,7 +405,7 @@ namespace Recyclable.Collections
|
|||||||
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
minBlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize));
|
||||||
SetupBlockArrayPooling(this, minBlockSize, blockArrayPool);
|
SetupBlockArrayPooling(this, minBlockSize, blockArrayPool);
|
||||||
_capacity = Resize(this, minBlockSize, _blockSizePow2Shift, expectedItemsCount.Value);
|
_capacity = Resize(this, minBlockSize, _blockSizePow2Shift, expectedItemsCount.Value);
|
||||||
if (_memoryBlocks!.Length > 0 && minBlockSize != _memoryBlocks[0].Length)
|
if (minBlockSize != _memoryBlocks![0].Length && _capacity > 0)
|
||||||
{
|
{
|
||||||
SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool);
|
SetupBlockArrayPooling(this, _memoryBlocks[0].Length, blockArrayPool);
|
||||||
_blockSizeMinus1 = _blockSize - 1;
|
_blockSizeMinus1 = _blockSize - 1;
|
||||||
@@ -473,7 +472,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
int blockSize = _blockSize,
|
int blockSize = _blockSize,
|
||||||
targetBlockIndex = _nextItemBlockIndex,
|
targetBlockIndex = _nextItemBlockIndex,
|
||||||
memoryBlockCount = _memoryBlocks.Length;
|
memoryBlockCount = _reservedBlockCount;
|
||||||
|
|
||||||
Span<T> itemsSpan = new(items);
|
Span<T> itemsSpan = new(items);
|
||||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
|
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
|
||||||
@@ -516,7 +515,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
int blockSize = _blockSize,
|
int blockSize = _blockSize,
|
||||||
targetBlockIndex = _nextItemBlockIndex,
|
targetBlockIndex = _nextItemBlockIndex,
|
||||||
memoryBlockCount = _memoryBlocks.Length;
|
memoryBlockCount = _reservedBlockCount;
|
||||||
|
|
||||||
Span<T> itemsSpan = new(items.AsArray(), 0, items.Count);
|
Span<T> itemsSpan = new(items.AsArray(), 0, items.Count);
|
||||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
|
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
|
||||||
@@ -560,9 +559,9 @@ namespace Recyclable.Collections
|
|||||||
targetBlockIndex = _nextItemBlockIndex,
|
targetBlockIndex = _nextItemBlockIndex,
|
||||||
toCopy;
|
toCopy;
|
||||||
|
|
||||||
Span<T[]> sourceMemoryBlocksSpan = new(items._memoryBlocks, 0, items._memoryBlocks.Length);
|
Span<T[]> sourceMemoryBlocksSpan = new(items._memoryBlocks, 0, items._reservedBlockCount);
|
||||||
Span<T> itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex], 0, items._blockSize);
|
Span<T> itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex], 0, items._blockSize);
|
||||||
Span<T[]> targetMemoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length);
|
Span<T[]> targetMemoryBlocksSpan = new(_memoryBlocks, 0, _reservedBlockCount);
|
||||||
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
|
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
|
||||||
long copiedCount = 0;
|
long copiedCount = 0;
|
||||||
while (copiedCount < itemsCount)
|
while (copiedCount < itemsCount)
|
||||||
@@ -630,7 +629,7 @@ namespace Recyclable.Collections
|
|||||||
itemsCount = items.Count,
|
itemsCount = items.Count,
|
||||||
copied = Math.Min(blockSize - _nextItemIndex, itemsCount);
|
copied = Math.Min(blockSize - _nextItemIndex, itemsCount);
|
||||||
|
|
||||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length);
|
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _reservedBlockCount);
|
||||||
items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], _nextItemIndex, copied);
|
items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], _nextItemIndex, copied);
|
||||||
if (_nextItemIndex + copied == blockSize)
|
if (_nextItemIndex + copied == blockSize)
|
||||||
{
|
{
|
||||||
@@ -673,7 +672,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
int blockSize = _blockSize,
|
int blockSize = _blockSize,
|
||||||
targetBlockIndex = _nextItemBlockIndex,
|
targetBlockIndex = _nextItemBlockIndex,
|
||||||
memoryBlockCount = _memoryBlocks.Length;
|
memoryBlockCount = _reservedBlockCount;
|
||||||
|
|
||||||
// We're better off to temporarily copy it to a fixed array,
|
// We're better off to temporarily copy it to a fixed array,
|
||||||
// than copying them item by item. We may run into OOMs, though.
|
// than copying them item by item. We may run into OOMs, though.
|
||||||
@@ -757,16 +756,18 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (_blockPoolingRequired)
|
if (_blockPoolingRequired)
|
||||||
{
|
{
|
||||||
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, ReservedBlockCount);
|
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _reservedBlockCount);
|
||||||
int memoryBlocksCount = memoryBlocksSpan.Length;
|
int memoryBlocksCount = memoryBlocksSpan.Length;
|
||||||
ArrayPool<T> blockArrayPool = _blockArrayPool;
|
ArrayPool<T> blockArrayPool = _blockArrayPool;
|
||||||
for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++)
|
for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++)
|
||||||
{
|
{
|
||||||
blockArrayPool.Return(memoryBlocksSpan[toRemoveIdx], NeedsClearing);
|
blockArrayPool.Return(memoryBlocksSpan[toRemoveIdx], NeedsClearing);
|
||||||
|
memoryBlocksSpan[toRemoveIdx] = _emptyBlockArray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_capacity = 0;
|
_capacity = 0;
|
||||||
|
_reservedBlockCount = 0;
|
||||||
_nextItemBlockIndex = 0;
|
_nextItemBlockIndex = 0;
|
||||||
_nextItemIndex = 0;
|
_nextItemIndex = 0;
|
||||||
_longCount = 0;
|
_longCount = 0;
|
||||||
|
|||||||
Reference in new issue
Block a user