Finished RecyclableList implementation of array pool overriding.

This commit is contained in:
mlemanczyk committed 2023-01-29 18:05:27 +01:00
1 parent 76b03345a4
commit 71544f8a47
1 file changed
+27 -18
+27 -18
View File
@@ -6,8 +6,8 @@ namespace Recyclable.Collections
{ {
public class RecyclableList<T> : IDisposable, IList<T> public class RecyclableList<T> : IDisposable, IList<T>
{ {
private static readonly ArrayPool<T[]> _arrayPool = ArrayPool<T[]>.Create(); private static readonly ArrayPool<T[]> _defaultMemoryBlocksPool = ArrayPool<T[]>.Create();
private static readonly ArrayPool<T> _blockArrayPool = ArrayPool<T>.Create(); private static readonly ArrayPool<T> _defaultBlockArrayPool = ArrayPool<T>.Create();
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 static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default; private static readonly IEqualityComparer<T> _equalityComparer = EqualityComparer<T>.Default;
@@ -15,6 +15,8 @@ namespace Recyclable.Collections
private int _blockSize; private int _blockSize;
private int _lastBlockIndex; private int _lastBlockIndex;
private int _lastItemIndex; private int _lastItemIndex;
protected ArrayPool<T[]> _memoryBlocksPool;
protected ArrayPool<T> _blockArrayPool;
protected T[][] _memoryBlocks; protected T[][] _memoryBlocks;
private long _capacity; private long _capacity;
@@ -36,7 +38,7 @@ namespace Recyclable.Collections
public bool IsReadOnly { get; } = false; public bool IsReadOnly { get; } = false;
public int BlockCount => _memoryBlocks.Length; public int BlockCount => _memoryBlocks.Length;
private static void RemoveAt(RecyclableList<T> list, long index) private static void RemoveAt(RecyclableList<T> list, long index, ArrayPool<T> blockArrayPool)
{ {
long oldCountMinus1 = list._longCount - 1; long oldCountMinus1 = list._longCount - 1;
if (index != oldCountMinus1) if (index != oldCountMinus1)
@@ -61,7 +63,7 @@ namespace Recyclable.Collections
T[][] memoryBlocks = list._memoryBlocks; T[][] memoryBlocks = list._memoryBlocks;
if (blockSize >= RecyclableDefaults.MinPooledArrayLength) if (blockSize >= RecyclableDefaults.MinPooledArrayLength)
{ {
_blockArrayPool.Return(memoryBlocks[list.BlockCount - 1]); blockArrayPool.Return(memoryBlocks[list.BlockCount - 1]);
} }
list._capacity -= blockSize; list._capacity -= blockSize;
@@ -74,15 +76,14 @@ namespace Recyclable.Collections
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, in long newCapacity) protected static T[][] SetNewLength(in T[][]? source, int minBlockSize, in long newCapacity, ArrayPool<T[]> memoryBlocksPool, ArrayPool<T> blockArrayPool)
{ {
ArrayPool<T[]> arrayPool = _arrayPool;
var sourceBlockCount = source?.Length ?? 0; var sourceBlockCount = source?.Length ?? 0;
int requiredBlockCount = (int)(newCapacity / minBlockSize) + (newCapacity % minBlockSize > 0 ? 1 : 0); int requiredBlockCount = (int)(newCapacity / minBlockSize) + (newCapacity % minBlockSize > 0 ? 1 : 0);
T[][] newMemoryBlocks = requiredBlockCount < RecyclableDefaults.MinPooledArrayLength T[][] newMemoryBlocks = requiredBlockCount < RecyclableDefaults.MinPooledArrayLength
? new T[requiredBlockCount][] ? new T[requiredBlockCount][]
: arrayPool.Rent(requiredBlockCount); : memoryBlocksPool.Rent(requiredBlockCount);
var newMemoryBlocksSpan = new Span<T[]>(newMemoryBlocks); var newMemoryBlocksSpan = new Span<T[]>(newMemoryBlocks);
if (sourceBlockCount > 0) if (sourceBlockCount > 0)
@@ -91,13 +92,12 @@ namespace Recyclable.Collections
sourceSpan.CopyTo(newMemoryBlocksSpan); sourceSpan.CopyTo(newMemoryBlocksSpan);
if (sourceSpan.Length >= RecyclableDefaults.MinPooledArrayLength) if (sourceSpan.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
arrayPool.Return(source!); memoryBlocksPool.Return(source!);
} }
newMemoryBlocksSpan = newMemoryBlocksSpan[sourceBlockCount..]; newMemoryBlocksSpan = newMemoryBlocksSpan[sourceBlockCount..];
} }
ArrayPool<T> blockArrayPool = _blockArrayPool;
int uninitializedBlocksCount = newMemoryBlocksSpan.Length; int uninitializedBlocksCount = newMemoryBlocksSpan.Length;
if (uninitializedBlocksCount == requiredBlockCount) if (uninitializedBlocksCount == requiredBlockCount)
{ {
@@ -151,7 +151,7 @@ namespace Recyclable.Collections
newCapacity = requestedCapacity; newCapacity = requestedCapacity;
} }
memory = SetNewLength(memory, _blockSize, newCapacity); memory = SetNewLength(memory, _blockSize, newCapacity, _memoryBlocksPool, _blockArrayPool);
if (newCapacity > 0 && _capacity == 0) if (newCapacity > 0 && _capacity == 0)
{ {
_blockSize = memory[0].Length; _blockSize = memory[0].Length;
@@ -163,11 +163,16 @@ namespace Recyclable.Collections
return newCapacity; return newCapacity;
} }
public RecyclableList(int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default) public RecyclableList(int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool<T[]>? memoryBlocksPool = default, ArrayPool<T>? blockArrayPool = default)
{ {
memoryBlocksPool ??= _defaultMemoryBlocksPool;
blockArrayPool ??= _defaultBlockArrayPool;
_memoryBlocksPool = memoryBlocksPool;
_blockArrayPool = blockArrayPool;
if (expectedItemsCount > 0) if (expectedItemsCount > 0)
{ {
_memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value); _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0) if (_memoryBlocks.Length > 0)
{ {
minBlockSize = _memoryBlocks[0].Length; minBlockSize = _memoryBlocks[0].Length;
@@ -183,11 +188,15 @@ namespace Recyclable.Collections
} }
} }
public RecyclableList(IEnumerable<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default) public RecyclableList(IEnumerable<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool<T[]>? memoryBlocksPool = default, ArrayPool<T>? blockArrayPool = default)
{ {
memoryBlocksPool ??= _defaultMemoryBlocksPool;
blockArrayPool ??= _defaultBlockArrayPool;
_memoryBlocksPool = memoryBlocksPool;
_blockArrayPool = blockArrayPool;
if (expectedItemsCount > 0) if (expectedItemsCount > 0)
{ {
_memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value); _memoryBlocks = SetNewLength(_memoryBlocks, minBlockSize, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0) if (_memoryBlocks.Length > 0)
{ {
minBlockSize = _memoryBlocks[0].Length; minBlockSize = _memoryBlocks[0].Length;
@@ -379,7 +388,7 @@ namespace Recyclable.Collections
long oldLongCount = _longCount; long oldLongCount = _longCount;
int blockSize = _blockSize; int blockSize = _blockSize;
int targetItemIdx = (int)(oldLongCount % blockSize); int targetItemIdx = (int)(oldLongCount % blockSize);
int targetBlockIdx = targetItemIdx / blockSize + (targetItemIdx > 0 ? 1 : 0); int targetBlockIdx = (targetItemIdx / blockSize) + (targetItemIdx > 0 ? 1 : 0);
Span<T[]> memoryBlocksSpan; Span<T[]> memoryBlocksSpan;
Span<T> blockArraySpan; Span<T> blockArraySpan;
@@ -520,8 +529,8 @@ namespace Recyclable.Collections
} }
} }
public void RemoveAt(int index) => RemoveAt(this, index); public void RemoveAt(int index) => RemoveAt(this, index, _blockArrayPool);
public void RemoveAt(long index) => RemoveAt(this, index); public void RemoveAt(long index) => RemoveAt(this, index, _blockArrayPool);
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
@@ -533,7 +542,7 @@ namespace Recyclable.Collections
_capacity = 0; _capacity = 0;
if (_memoryBlocks.Length >= RecyclableDefaults.MinPooledArrayLength) if (_memoryBlocks.Length >= RecyclableDefaults.MinPooledArrayLength)
{ {
_arrayPool.Return(_memoryBlocks); _memoryBlocksPool.Return(_memoryBlocks);
} }
GC.SuppressFinalize(this); GC.SuppressFinalize(this);