RecyclableList.SetNewLength renamed to RecyclableList.Resize & performance improved.

This commit is contained in:
mlemanczyk committed 2023-03-30 19:33:55 +02:00
1 parent 8c0d3c5fbc
commit d1e0482693
1 file changed
+64 -72
+64 -72
View File
@@ -230,99 +230,93 @@ namespace Recyclable.Collections
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, long oldCapacity, long newCapacity, ArrayPool<T[]> memoryBlocksPool, ArrayPool<T> blockArrayPool) protected static long Resize(ref T[][]? source, int minBlockSize, long oldCapacity, long newCapacity, ArrayPool<T[]> memoryBlocksPool, ArrayPool<T> blockArrayPool)
{ {
const int sourceBlockCount = 0, requiredBlockCount = 1, uninitializedBlocksCount = 2, i = 3; int minBlockSizePowerToShift = 31 - BitOperations.LeadingZeroCount((uint)minBlockSize);
minBlockSize = (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize); int sourceBlockCount = (int)(oldCapacity >> minBlockSizePowerToShift);
int minBlockSizePowerToShift = MathUtils.GetPow2Shift(minBlockSize); int requiredBlockCount = (int)(newCapacity >> minBlockSizePowerToShift) + ((newCapacity & (minBlockSize - 1)) > 0 ? 1 : 0);
Span<int> localInts = stackalloc int[4] int blockIndex;
{ T[][]? newMemoryBlocks;
(int)(oldCapacity >> minBlockSizePowerToShift), // sourceBlockCount Span<T[]> memoryBlocksSpan;
(int)(newCapacity >> minBlockSizePowerToShift) + ((newCapacity & (minBlockSize - 1)) > 0 ? 1 : 0), // requiredBlockCount
0, // uninitializedBlocksCount
0, // i
};
T[][] newMemoryBlocks = localInts[requiredBlockCount] < RecyclableDefaults.MinPooledArrayLength // Release excesive blocks if we're downsizing
? new T[localInts[requiredBlockCount]][] if (requiredBlockCount < sourceBlockCount && (minBlockSize >= RecyclableDefaults.MinPooledArrayLength))
: memoryBlocksPool.Rent(localInts[requiredBlockCount]);
Span<T[]> newMemoryBlocksSpan = new(newMemoryBlocks);
if (localInts[sourceBlockCount] > 0)
{ {
Span<T[]> sourceSpan = new(source); memoryBlocksSpan = new(source, requiredBlockCount, sourceBlockCount - requiredBlockCount);
sourceSpan.CopyTo(newMemoryBlocksSpan); blockIndex = 0;
if (localInts[sourceBlockCount] >= RecyclableDefaults.MinPooledArrayLength) while (blockIndex < memoryBlocksSpan.Length)
{ {
memoryBlocksPool.Return(source!, NeedsClearing); blockArrayPool.Return(memoryBlocksSpan[blockIndex++]!, NeedsClearing);
} }
newMemoryBlocksSpan = newMemoryBlocksSpan[localInts[sourceBlockCount]..];
} }
localInts[uninitializedBlocksCount] = newMemoryBlocksSpan.Length; // Allocate new memory block for all arrays
if (localInts[uninitializedBlocksCount] >= localInts[requiredBlockCount]) newMemoryBlocks = requiredBlockCount < RecyclableDefaults.MinPooledArrayLength
? (new T[requiredBlockCount][])
: memoryBlocksPool.Rent(requiredBlockCount);
// Copy arrays from the old memory block for all arrays
if (sourceBlockCount > 0)
{ {
Array.Copy(source!, newMemoryBlocks, sourceBlockCount);
// We can now return the old memory block for all arrays itself
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
{
memoryBlocksPool.Return(source!, true);
}
}
source = newMemoryBlocks;
// Allocate arrays for any new blocks
if (requiredBlockCount > sourceBlockCount)
{
memoryBlocksSpan = new(newMemoryBlocks, sourceBlockCount, newMemoryBlocks.Length - sourceBlockCount);
if (minBlockSize >= RecyclableDefaults.MinPooledArrayLength) if (minBlockSize >= RecyclableDefaults.MinPooledArrayLength)
{ {
newMemoryBlocksSpan[0] = blockArrayPool.Rent(minBlockSize); if (sourceBlockCount == 0)
minBlockSize = newMemoryBlocksSpan[0].Length; {
memoryBlocksSpan[0] = blockArrayPool.Rent(minBlockSize);
minBlockSize = memoryBlocksSpan[0].Length;
blockIndex = 1;
}
else
{
blockIndex = 0;
}
while (blockIndex < memoryBlocksSpan.Length)
{
memoryBlocksSpan[blockIndex++] = blockArrayPool.Rent(minBlockSize);
}
} }
else else
{ {
newMemoryBlocksSpan[0] = new T[minBlockSize]; blockIndex = 0;
} while (blockIndex < memoryBlocksSpan.Length)
{
newMemoryBlocksSpan = newMemoryBlocksSpan[1..]; memoryBlocksSpan[blockIndex++] = new T[minBlockSize];
localInts[uninitializedBlocksCount]--; }
}
if (minBlockSize >= RecyclableDefaults.MinPooledArrayLength)
{
for (localInts[i] = 0; localInts[i] < localInts[uninitializedBlocksCount]; localInts[i]++)
{
newMemoryBlocksSpan[localInts[i]] = blockArrayPool.Rent(minBlockSize);
}
}
else
{
for (localInts[i] = 0; localInts[i] < localInts[uninitializedBlocksCount]; localInts[i]++)
{
newMemoryBlocksSpan[localInts[i]] = new T[minBlockSize];
} }
} }
return newMemoryBlocks; return newMemoryBlocks.Length << minBlockSizePowerToShift;
} }
protected long EnsureCapacity(long requestedCapacity) protected long EnsureCapacity(long requestedCapacity)
{ {
T[][] memory = _memoryBlocks; long newCapacity = _capacity > 0
long newCapacity; ? checked((long)BitOperations.RoundUpToPowerOf2((ulong)requestedCapacity))
: requestedCapacity;
if (_capacity > 0) newCapacity = Resize(ref _memoryBlocks!, _blockSize, _capacity, newCapacity, _memoryBlocksPool, _blockArrayPool);
{
newCapacity = _capacity;
while (newCapacity < requestedCapacity)
{
newCapacity *= 2;
}
}
else
{
newCapacity = requestedCapacity;
}
memory = SetNewLength(memory, _blockSize, _capacity, newCapacity, _memoryBlocksPool, _blockArrayPool);
if (newCapacity > 0 && _capacity == 0) if (newCapacity > 0 && _capacity == 0)
{ {
_blockSize = memory[0].Length; _blockSize = _memoryBlocks[0].Length;
_blockSizePow2Shift = MathUtils.GetPow2Shift(_blockSize); _blockSizePow2Shift = MathUtils.GetPow2Shift(_blockSize);
} }
newCapacity = memory.Length * _blockSize;
_capacity = newCapacity; _capacity = newCapacity;
_memoryBlocks = memory;
return newCapacity; return newCapacity;
} }
@@ -335,7 +329,7 @@ namespace Recyclable.Collections
if (expectedItemsCount > 0) if (expectedItemsCount > 0)
{ {
_memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); _capacity = Resize(ref _memoryBlocks!, (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize), 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0) if (_memoryBlocks.Length > 0)
{ {
minBlockSize = _memoryBlocks[0].Length; minBlockSize = _memoryBlocks[0].Length;
@@ -343,11 +337,10 @@ namespace Recyclable.Collections
_blockSize = minBlockSize; _blockSize = minBlockSize;
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize); _blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
_capacity = _memoryBlocks.Length * minBlockSize;
} }
else else
{ {
_blockSize = minBlockSize; _blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize);
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize); _blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
_memoryBlocks = _emptyMemoryBlocksArray; _memoryBlocks = _emptyMemoryBlocksArray;
} }
@@ -362,7 +355,7 @@ namespace Recyclable.Collections
if (expectedItemsCount > 0) if (expectedItemsCount > 0)
{ {
_memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool); _capacity = Resize(ref _memoryBlocks!, (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize), 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0) if (_memoryBlocks.Length > 0)
{ {
minBlockSize = _memoryBlocks[0].Length; minBlockSize = _memoryBlocks[0].Length;
@@ -370,11 +363,10 @@ namespace Recyclable.Collections
_blockSize = minBlockSize; _blockSize = minBlockSize;
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize); _blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
_capacity = _memoryBlocks.Length * minBlockSize;
} }
else else
{ {
_blockSize = minBlockSize; _blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize);
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize); _blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
_memoryBlocks = _emptyMemoryBlocksArray; _memoryBlocks = _emptyMemoryBlocksArray;
} }