Add chunk pooling and optimize stack growth
This commit is contained in:
1 parent
1b4b9f534b
commit
ac163014d8
1 file changed
+37
-22
@@ -1,5 +1,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Recyclable.Collections.Pools;
|
using Recyclable.Collections.Pools;
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
@@ -7,23 +9,42 @@ namespace Recyclable.Collections
|
|||||||
internal sealed class RecyclableStack<T> : IEnumerable<T>, IDisposable
|
internal sealed class RecyclableStack<T> : IEnumerable<T>, IDisposable
|
||||||
{
|
{
|
||||||
private static readonly bool _needsClearing = !typeof(T).IsValueType;
|
private static readonly bool _needsClearing = !typeof(T).IsValueType;
|
||||||
|
private static readonly ObjectPool<Chunk> _chunkPool = new DefaultObjectPool<Chunk>(new DefaultPooledObjectPolicy<Chunk>());
|
||||||
|
|
||||||
private sealed class Chunk
|
private sealed class Chunk
|
||||||
{
|
{
|
||||||
internal T[] Buffer;
|
internal T[] Buffer = Array.Empty<T>();
|
||||||
internal int Index;
|
internal int Index;
|
||||||
internal Chunk? Previous;
|
internal Chunk? Previous;
|
||||||
internal Chunk? Next;
|
internal Chunk? Next;
|
||||||
|
}
|
||||||
|
|
||||||
internal Chunk(int size, Chunk? previous)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static Chunk RentChunk(int size, Chunk? previous)
|
||||||
|
{
|
||||||
|
var chunk = _chunkPool.Get();
|
||||||
|
chunk.Buffer = size >= RecyclableDefaults.MinPooledArrayLength
|
||||||
|
? RecyclableArrayPool<T>.RentShared(size)
|
||||||
|
: new T[size];
|
||||||
|
chunk.Index = 0;
|
||||||
|
chunk.Previous = previous;
|
||||||
|
chunk.Next = null;
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void ReturnChunk(Chunk chunk)
|
||||||
|
{
|
||||||
|
var buffer = chunk.Buffer;
|
||||||
|
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
Buffer = size >= RecyclableDefaults.MinPooledArrayLength
|
RecyclableArrayPool<T>.ReturnShared(buffer, _needsClearing);
|
||||||
? RecyclableArrayPool<T>.RentShared(size)
|
|
||||||
: new T[size];
|
|
||||||
Previous = previous;
|
|
||||||
Next = null;
|
|
||||||
Index = 0;
|
|
||||||
}
|
}
|
||||||
|
chunk.Buffer = Array.Empty<T>();
|
||||||
|
chunk.Index = 0;
|
||||||
|
chunk.Previous = null;
|
||||||
|
chunk.Next = null;
|
||||||
|
_chunkPool.Return(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Chunk _current;
|
private Chunk _current;
|
||||||
@@ -44,7 +65,7 @@ namespace Recyclable.Collections
|
|||||||
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
_current = new Chunk(initialCapacity, null);
|
_current = RentChunk(initialCapacity, null);
|
||||||
_bottom = _current;
|
_bottom = _current;
|
||||||
_capacity = initialCapacity;
|
_capacity = initialCapacity;
|
||||||
_count = 0;
|
_count = 0;
|
||||||
@@ -128,11 +149,11 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var doubled = _capacity * 2;
|
var doubled = _capacity << 1;
|
||||||
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newChunk = new Chunk(newSize, _current);
|
var newChunk = RentChunk(newSize, _current);
|
||||||
_current.Next = newChunk;
|
_current.Next = newChunk;
|
||||||
_current = newChunk;
|
_current = newChunk;
|
||||||
_capacity += newSize;
|
_capacity += newSize;
|
||||||
@@ -140,18 +161,15 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
private void ReleaseCurrentChunk()
|
private void ReleaseCurrentChunk()
|
||||||
{
|
{
|
||||||
var toReturn = _current.Buffer;
|
var toReturn = _current;
|
||||||
_current = _current.Previous!;
|
_current = toReturn.Previous!;
|
||||||
_current.Next = null;
|
_current.Next = null;
|
||||||
_capacity -= toReturn.Length;
|
_capacity -= toReturn.Buffer.Length;
|
||||||
if (_current.Previous == null)
|
if (_current.Previous == null)
|
||||||
{
|
{
|
||||||
_bottom = _current;
|
_bottom = _current;
|
||||||
}
|
}
|
||||||
if (toReturn.Length >= RecyclableDefaults.MinPooledArrayLength)
|
ReturnChunk(toReturn);
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(toReturn, _needsClearing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -162,10 +180,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
if (_current.Buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
ReturnChunk(_current);
|
||||||
{
|
|
||||||
RecyclableArrayPool<T>.ReturnShared(_current.Buffer, _needsClearing);
|
|
||||||
}
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user