Draft RecyclableArrayList performance improvements ported from RecyclableList.
This commit is contained in:
1 parent
3db10d7602
commit
98cac9a563
1 file changed
+57
-33
@@ -18,21 +18,23 @@ namespace Recyclable.Collections
|
|||||||
throw new ArgumentOutOfRangeException(message, (Exception?)null);
|
throw new ArgumentOutOfRangeException(message, (Exception?)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
protected static T[] SetNewLength(in T[]? source, in int newSize)
|
protected static T[] Resize(in T[]? source, int newSize)
|
||||||
{
|
{
|
||||||
ArrayPool<T> arrayPool = _arrayPool;
|
ArrayPool<T> arrayPool = _arrayPool;
|
||||||
var sourceLength = source?.Length ?? 0;
|
|
||||||
T[] newMemoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
|
T[] newMemoryBlock = newSize >= RecyclableDefaults.MinPooledArrayLength
|
||||||
? arrayPool.Rent(newSize)
|
? arrayPool.Rent(newSize)
|
||||||
: new T[newSize];
|
: new T[newSize];
|
||||||
|
|
||||||
if (sourceLength > 0)
|
if (source?.Length > 0)
|
||||||
{
|
{
|
||||||
var sourceSpan = new Memory<T>(source);
|
Array.Copy(source!, 0, newMemoryBlock, 0, source!.Length);
|
||||||
var newArraySpan = new Memory<T>(newMemoryBlock);
|
//var sourceSpan = new Memory<T>(source);
|
||||||
sourceSpan.CopyTo(newArraySpan);
|
//var newArraySpan = new Memory<T>(newMemoryBlock);
|
||||||
if (sourceLength >= RecyclableDefaults.MinPooledArrayLength)
|
|
||||||
|
//sourceSpan.CopyTo(newArraySpan);
|
||||||
|
|
||||||
|
if (source!.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
arrayPool.Return(source!, NeedsClearing);
|
arrayPool.Return(source!, NeedsClearing);
|
||||||
}
|
}
|
||||||
@@ -41,19 +43,17 @@ namespace Recyclable.Collections
|
|||||||
return newMemoryBlock;
|
return newMemoryBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int EnsureCapacity(in int requestedCapacity)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
protected static int EnsureCapacity(RecyclableArrayList<T> list, int requestedCapacity)
|
||||||
{
|
{
|
||||||
int oldCapacity = _capacity;
|
|
||||||
ref T[] memory = ref _memoryBlock;
|
|
||||||
|
|
||||||
int newCapacity;
|
int newCapacity;
|
||||||
switch (oldCapacity > 0)
|
switch (list._capacity > 0)
|
||||||
{
|
{
|
||||||
case true:
|
case true:
|
||||||
newCapacity = oldCapacity;
|
newCapacity = list._capacity;
|
||||||
while (newCapacity < requestedCapacity)
|
while (newCapacity < requestedCapacity)
|
||||||
{
|
{
|
||||||
newCapacity *= 2;
|
newCapacity <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -63,9 +63,9 @@ namespace Recyclable.Collections
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory = SetNewLength(memory, newCapacity);
|
list._memoryBlock = Resize(list._memoryBlock, newCapacity);
|
||||||
newCapacity = memory.Length;
|
newCapacity = list._memoryBlock.Length;
|
||||||
_capacity = newCapacity;
|
list._capacity = newCapacity;
|
||||||
return newCapacity;
|
return newCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (initialCapacity > 0)
|
if (initialCapacity > 0)
|
||||||
{
|
{
|
||||||
_memoryBlock = SetNewLength(_memoryBlock, initialCapacity);
|
_memoryBlock = Resize(_memoryBlock, initialCapacity);
|
||||||
_capacity = _memoryBlock.Length;
|
_capacity = _memoryBlock.Length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,15 +116,18 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public RecyclableArrayList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.Capacity)
|
public RecyclableArrayList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.Capacity)
|
||||||
{
|
{
|
||||||
_memoryBlock = SetNewLength(_memoryBlock, initialCapacity);
|
_memoryBlock = Resize(_memoryBlock, initialCapacity);
|
||||||
_capacity = _memoryBlock.Length;
|
_capacity = _memoryBlock.Length;
|
||||||
AddRange(source);
|
AddRange(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T this[int index]
|
public T this[int index]
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => _memoryBlock[index];
|
get => _memoryBlock[index];
|
||||||
set => new Span<T>(_memoryBlock)[index] = value;
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
set => _memoryBlock[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int _capacity;
|
protected int _capacity;
|
||||||
@@ -135,7 +138,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (_capacity != value)
|
if (_capacity != value)
|
||||||
{
|
{
|
||||||
_memoryBlock = SetNewLength(_memoryBlock, value);
|
_memoryBlock = Resize(_memoryBlock, value);
|
||||||
_capacity = _memoryBlock.Length;
|
_capacity = _memoryBlock.Length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +147,10 @@ namespace Recyclable.Collections
|
|||||||
protected int _count;
|
protected int _count;
|
||||||
public int Count
|
public int Count
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => _count;
|
get => _count;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
set => _count = value;
|
set => _count = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +162,13 @@ namespace Recyclable.Collections
|
|||||||
int requiredCapacity = _count + 1;
|
int requiredCapacity = _count + 1;
|
||||||
if (_capacity < requiredCapacity)
|
if (_capacity < requiredCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(requiredCapacity);
|
_ = EnsureCapacity(this, requiredCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
_memoryBlock[_count++] = item;
|
_memoryBlock[_count++] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void AddRange(in T[] items)
|
public void AddRange(in T[] items)
|
||||||
{
|
{
|
||||||
if (items.LongLength == 0)
|
if (items.LongLength == 0)
|
||||||
@@ -173,7 +180,7 @@ namespace Recyclable.Collections
|
|||||||
int targetCapacity = _count + sourceItemsCount;
|
int targetCapacity = _count + sourceItemsCount;
|
||||||
if (_capacity < targetCapacity)
|
if (_capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(targetCapacity);
|
_ = EnsureCapacity(this, targetCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
||||||
@@ -182,6 +189,7 @@ namespace Recyclable.Collections
|
|||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void AddRange(List<T> items)
|
public void AddRange(List<T> items)
|
||||||
{
|
{
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
@@ -193,13 +201,14 @@ namespace Recyclable.Collections
|
|||||||
var targetCapacity = _count + sourceItemsCount;
|
var targetCapacity = _count + sourceItemsCount;
|
||||||
if (_capacity < targetCapacity)
|
if (_capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(targetCapacity);
|
_ = EnsureCapacity(this, targetCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.CopyTo(_memoryBlock, _count);
|
items.CopyTo(_memoryBlock, _count);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void AddRange(IList<T> items)
|
public void AddRange(IList<T> items)
|
||||||
{
|
{
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
@@ -211,13 +220,14 @@ namespace Recyclable.Collections
|
|||||||
var targetCapacity = _count + sourceItemsCount;
|
var targetCapacity = _count + sourceItemsCount;
|
||||||
if (_capacity < targetCapacity)
|
if (_capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(targetCapacity);
|
_ = EnsureCapacity(this, targetCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.CopyTo(_memoryBlock, _count);
|
items.CopyTo(_memoryBlock, _count);
|
||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void AddRange(RecyclableArrayList<T> items)
|
public void AddRange(RecyclableArrayList<T> items)
|
||||||
{
|
{
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
@@ -229,7 +239,7 @@ namespace Recyclable.Collections
|
|||||||
var targetCapacity = _count + sourceItemsCount;
|
var targetCapacity = _count + sourceItemsCount;
|
||||||
if (_capacity < targetCapacity)
|
if (_capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(targetCapacity);
|
_ = EnsureCapacity(this, targetCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
var targetSpan = new Span<T>(_memoryBlock, _count, sourceItemsCount);
|
||||||
@@ -238,6 +248,7 @@ namespace Recyclable.Collections
|
|||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void AddRange(RecyclableList<T> items)
|
public void AddRange(RecyclableList<T> items)
|
||||||
{
|
{
|
||||||
if (items.LongCount == 0)
|
if (items.LongCount == 0)
|
||||||
@@ -259,7 +270,7 @@ namespace Recyclable.Collections
|
|||||||
int targetCapacity = _count + (int)sourceItemsCount;
|
int targetCapacity = _count + (int)sourceItemsCount;
|
||||||
if (_capacity < targetCapacity)
|
if (_capacity < targetCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(targetCapacity);
|
_ = EnsureCapacity(this, targetCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<T> targetSpan = new Span<T>(_memoryBlock)[_count..],
|
Span<T> targetSpan = new Span<T>(_memoryBlock)[_count..],
|
||||||
@@ -281,7 +292,7 @@ namespace Recyclable.Collections
|
|||||||
itemsSpan = new(items.AsArray[blockIndex], 0, (int)sourceItemsCount);
|
itemsSpan = new(items.AsArray[blockIndex], 0, (int)sourceItemsCount);
|
||||||
itemsSpan.CopyTo(targetSpan);
|
itemsSpan.CopyTo(targetSpan);
|
||||||
}
|
}
|
||||||
else if (blockIndex <= items.LastTakenBlockIndex)
|
else if (blockIndex <= items.LastBlockWithData)
|
||||||
{
|
{
|
||||||
itemsSpan = new(items.AsArray[blockIndex], 0, items.NextItemIndex);
|
itemsSpan = new(items.AsArray[blockIndex], 0, items.NextItemIndex);
|
||||||
itemsSpan.CopyTo(targetSpan);
|
itemsSpan.CopyTo(targetSpan);
|
||||||
@@ -290,6 +301,7 @@ namespace Recyclable.Collections
|
|||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
if (source is T[] sourceArray)
|
if (source is T[] sourceArray)
|
||||||
@@ -320,7 +332,7 @@ namespace Recyclable.Collections
|
|||||||
Span<T> memorySpan;
|
Span<T> memorySpan;
|
||||||
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(targetItemIdx + requiredAdditionalCapacity);
|
_ = EnsureCapacity(this, targetItemIdx + requiredAdditionalCapacity);
|
||||||
memorySpan = new(_memoryBlock);
|
memorySpan = new(_memoryBlock);
|
||||||
foreach (var item in source)
|
foreach (var item in source)
|
||||||
{
|
{
|
||||||
@@ -343,7 +355,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (targetItemIdx + growByCount > capacity)
|
if (targetItemIdx + growByCount > capacity)
|
||||||
{
|
{
|
||||||
capacity = EnsureCapacity(capacity + growByCount);
|
capacity = EnsureCapacity(this, capacity + growByCount);
|
||||||
memorySpan = new(_memoryBlock);
|
memorySpan = new(_memoryBlock);
|
||||||
available = capacity - targetItemIdx;
|
available = capacity - targetItemIdx;
|
||||||
}
|
}
|
||||||
@@ -365,13 +377,21 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public T[] AsArray => _memoryBlock;
|
public T[] AsArray => _memoryBlock;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
if (NeedsClearing)
|
||||||
|
{
|
||||||
|
Array.Clear(_memoryBlock, 0, _count);
|
||||||
|
}
|
||||||
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Contains(T item) => _count > 0 && Array.IndexOf(_memoryBlock, item, 0, _count) >= 0;
|
public bool Contains(T item) => _count > 0 && Array.IndexOf(_memoryBlock, item, 0, _count) >= 0;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public void CopyTo(T[] array, int arrayIndex) => Array.Copy(_memoryBlock, 0, array, arrayIndex, _count);
|
public void CopyTo(T[] array, int arrayIndex) => Array.Copy(_memoryBlock, 0, array, arrayIndex, _count);
|
||||||
|
|
||||||
protected static IEnumerable<T> Enumerate(RecyclableArrayList<T> list)
|
protected static IEnumerable<T> Enumerate(RecyclableArrayList<T> list)
|
||||||
@@ -384,15 +404,17 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int IndexOf(T itemToFind) => _count > 0 ? Array.IndexOf(_memoryBlock, itemToFind, 0, _count) : ItemNotFound;
|
public int IndexOf(T itemToFind) => _count > 0 ? Array.IndexOf(_memoryBlock, itemToFind, 0, _count) : ItemNotFound;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Insert(int index, T item)
|
public void Insert(int index, T item)
|
||||||
{
|
{
|
||||||
int oldCount = _count;
|
int oldCount = _count;
|
||||||
int requestedCapacity = oldCount + 1;
|
int requestedCapacity = oldCount + 1;
|
||||||
if (_capacity < requestedCapacity)
|
if (_capacity < requestedCapacity)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(requestedCapacity);
|
_ = EnsureCapacity(this, requestedCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldCount > 0)
|
if (oldCount > 0)
|
||||||
@@ -434,7 +456,7 @@ namespace Recyclable.Collections
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void RemoveAt(int index)
|
public void RemoveAt(int index)
|
||||||
{
|
{
|
||||||
if (index > _count - 1)
|
if (index >= _count)
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range");
|
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range");
|
||||||
}
|
}
|
||||||
@@ -453,8 +475,10 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public IEnumerator<T> GetEnumerator() => Enumerate(this).GetEnumerator();
|
public IEnumerator<T> GetEnumerator() => Enumerate(this).GetEnumerator();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
IEnumerator IEnumerable.GetEnumerator() => Enumerate(this).GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => Enumerate(this).GetEnumerator();
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
Reference in new issue
Block a user