RecyclableList.Resize performance improvements.
This commit is contained in:
1 parent
60494c3219
commit
4600c4521e
1 file changed
+54
-41
@@ -253,6 +253,10 @@ namespace Recyclable.Collections
|
|||||||
///
|
///
|
||||||
/// This method checks for integral overflow.
|
/// This method checks for integral overflow.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method doesn't support downsizing the memory block. As such it doesn't release excessive blocks. This is to for additional performance
|
||||||
|
/// gain - 1+ operation less for each call. If you need this functionality, you need to implement it on a higher level.
|
||||||
|
/// </remarks>
|
||||||
/// <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)
|
||||||
@@ -263,65 +267,65 @@ namespace Recyclable.Collections
|
|||||||
int blockIndex;
|
int blockIndex;
|
||||||
Span<T[]> memoryBlocksSpan;
|
Span<T[]> memoryBlocksSpan;
|
||||||
|
|
||||||
// Release excessive blocks if we're downsizing
|
if (requiredBlockCount > (list._memoryBlocks?.Length ?? 0))
|
||||||
if (requiredBlockCount < sourceBlockCount && list._blockPoolingRequired)
|
|
||||||
{
|
{
|
||||||
memoryBlocksSpan = new(list._memoryBlocks, requiredBlockCount, requiredBlockCount - sourceBlockCount);
|
// Allocate new memory block for all arrays
|
||||||
blockIndex = 0;
|
T[][] newMemoryBlocks = requiredBlockCount >= RecyclableDefaults.MinPooledArrayLength ? list._memoryBlocksPool.Rent(requiredBlockCount) : new T[requiredBlockCount][];
|
||||||
while (blockIndex < memoryBlocksSpan.Length)
|
|
||||||
|
// Copy arrays from the old memory block for all arrays
|
||||||
|
if (sourceBlockCount > 0)
|
||||||
{
|
{
|
||||||
blockArrayPool.Return(memoryBlocksSpan[blockIndex++], NeedsClearing);
|
Array.Copy(list._memoryBlocks!, newMemoryBlocks, sourceBlockCount);
|
||||||
|
// We can now return the old memory block for all arrays itself
|
||||||
|
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
list._memoryBlocksPool.Return(list._memoryBlocks!, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list._memoryBlocks = newMemoryBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<T[]> memoryBlocksPool = list._memoryBlocksPool;
|
|
||||||
|
|
||||||
// Allocate new memory block for all arrays
|
|
||||||
bool memoryBlockPoolingRequired = requiredBlockCount >= RecyclableDefaults.MinPooledArrayLength;
|
|
||||||
T[][] newMemoryBlocks = memoryBlockPoolingRequired ? memoryBlocksPool.Rent(requiredBlockCount) : new T[requiredBlockCount][];
|
|
||||||
|
|
||||||
// Copy arrays from the old memory block for all arrays
|
|
||||||
if (sourceBlockCount > 0)
|
|
||||||
{
|
|
||||||
Array.Copy(list._memoryBlocks, newMemoryBlocks, sourceBlockCount);
|
|
||||||
// We can now return the old memory block for all arrays itself
|
|
||||||
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
{
|
|
||||||
memoryBlocksPool.Return(list._memoryBlocks, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list._memoryBlocks = newMemoryBlocks;
|
|
||||||
|
|
||||||
// Allocate arrays for any new blocks
|
// Allocate arrays for any new blocks
|
||||||
if (requiredBlockCount > sourceBlockCount)
|
if (requiredBlockCount > sourceBlockCount)
|
||||||
{
|
{
|
||||||
memoryBlocksSpan = new(newMemoryBlocks, sourceBlockCount, requiredBlockCount - sourceBlockCount);
|
memoryBlocksSpan = new(list._memoryBlocks!, sourceBlockCount, requiredBlockCount - sourceBlockCount);
|
||||||
if (list._blockPoolingRequired)
|
if (list._blockPoolingRequired)
|
||||||
{
|
{
|
||||||
if (sourceBlockCount == 0)
|
if (sourceBlockCount == 0)
|
||||||
{
|
{
|
||||||
memoryBlocksSpan[0] = blockArrayPool.Rent(minBlockSize);
|
minBlockSize = GetEstimatedBlockSize(minBlockSize, blockArrayPool);
|
||||||
minBlockSize = memoryBlocksSpan[0].Length;
|
|
||||||
blockIndex = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
blockIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (blockIndex < memoryBlocksSpan.Length)
|
blockIndex = 0;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
memoryBlocksSpan[blockIndex++] = blockArrayPool.Rent(minBlockSize);
|
memoryBlocksSpan[blockIndex] = blockArrayPool.Rent(minBlockSize);
|
||||||
}
|
if (blockIndex + 1 < memoryBlocksSpan.Length)
|
||||||
|
{
|
||||||
|
blockIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blockIndex = 0;
|
blockIndex = 0;
|
||||||
while (blockIndex < memoryBlocksSpan.Length)
|
do
|
||||||
{
|
{
|
||||||
memoryBlocksSpan[blockIndex++] = new T[minBlockSize];
|
memoryBlocksSpan[blockIndex] = new T[minBlockSize];
|
||||||
}
|
if (blockIndex + 1 < memoryBlocksSpan.Length)
|
||||||
|
{
|
||||||
|
blockIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +333,15 @@ namespace Recyclable.Collections
|
|||||||
return (long)requiredBlockCount << minBlockSizePow2Shift;
|
return (long)requiredBlockCount << minBlockSizePow2Shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int GetEstimatedBlockSize(int minBlockSize, ArrayPool<T> blockArrayPool)
|
||||||
|
{
|
||||||
|
var array = blockArrayPool.Rent(minBlockSize);
|
||||||
|
minBlockSize = array.Length;
|
||||||
|
blockArrayPool.Return(array);
|
||||||
|
return minBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
protected static void SetupBlockArrayPooling(RecyclableList<T> list, int blockSize, ArrayPool<T>? blockArrayPool = null)
|
protected static void SetupBlockArrayPooling(RecyclableList<T> list, int blockSize, ArrayPool<T>? blockArrayPool = null)
|
||||||
{
|
{
|
||||||
@@ -347,7 +360,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static long EnsureCapacity(RecyclableList<T> list, long requestedCapacity)
|
protected static long EnsureCapacity(RecyclableList<T> list, long requestedCapacity)
|
||||||
{
|
{
|
||||||
long newCapacity = list._capacity > 0
|
long newCapacity = list._capacity > 0
|
||||||
? checked((long)BitOperations.RoundUpToPowerOf2((ulong)requestedCapacity))
|
? checked((long)BitOperations.RoundUpToPowerOf2((ulong)requestedCapacity))
|
||||||
@@ -377,7 +390,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 (_blockSize != _memoryBlocks[0].Length && _capacity > 0)
|
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;
|
||||||
|
|||||||
Reference in new issue
Block a user