feat: Adds a single threaded array pool (#967)
## Added Feature Adds a single threaded array pool that works exactly the same as `ArrayPool<T>.Shared`. The `STArrayPool<T>.Shared` can only be used on a single thread, the main game thread of the server. Note: Unlike the built-in array pool, there is no hook into the _GC Gen 2_. This means to relieve potentially high memory pressure, `ArrayPool<T>.Shared.Trim()` must be called. The pool will only release arrays _after two successive calls within 10 seconds or longer_. If the server is at less than 70% total memory usage, or the server is not going to use this pool for something egregious, then don't bother ever calling Trim(). ## Changes - [X] Fixes ArrayPool calls that should be cleared due to references. - [X] Benchmarks against ArrayPool with 4+ rented arrays deep of the same length. - [x] Unit tests
This commit is contained in:
parent
0925a2d435
commit
76fddcbccd
9 changed files with 604 additions and 12 deletions
|
|
@ -6,6 +6,7 @@ using System.Buffers;
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Server.Buffers;
|
||||
|
||||
namespace Server.Collections
|
||||
{
|
||||
|
|
@ -19,20 +20,25 @@ namespace Server.Collections
|
|||
private int _head; // The index from which to dequeue if the queue isn't empty.
|
||||
private int _tail; // The index at which to enqueue if the queue isn't full.
|
||||
private int _size; // Number of elements.
|
||||
private bool _mt;
|
||||
private int _version;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static PooledRefQueue<T> Create(int capacity = 32) => new(capacity);
|
||||
public static PooledRefQueue<T> Create(int capacity = 32, bool mt = false) => new(capacity, mt);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static PooledRefQueue<T> CreateMT(int capacity = 32) => new(capacity, true);
|
||||
|
||||
// Creates a queue with room for capacity objects. The default grow factor
|
||||
// is used.
|
||||
public PooledRefQueue(int capacity)
|
||||
public PooledRefQueue(int capacity, bool mt = false)
|
||||
{
|
||||
_mt = mt;
|
||||
_array = capacity switch
|
||||
{
|
||||
< 0 => throw new ArgumentOutOfRangeException(nameof(capacity), capacity, CollectionThrowStrings.ArgumentOutOfRange_NeedNonNegNum),
|
||||
0 => Array.Empty<T>(),
|
||||
_ => ArrayPool<T>.Shared.Rent(capacity)
|
||||
_ => (mt ? ArrayPool<T>.Shared : STArrayPool<T>.Shared).Rent(capacity)
|
||||
};
|
||||
|
||||
_head = 0;
|
||||
|
|
@ -254,14 +260,14 @@ namespace Server.Collections
|
|||
return arr;
|
||||
}
|
||||
|
||||
public T[] ToPooledArray()
|
||||
public T[] ToPooledArray(bool mt = false)
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
T[] arr = ArrayPool<T>.Shared.Rent(_size);
|
||||
T[] arr = (mt ? ArrayPool<T>.Shared : STArrayPool<T>.Shared).Rent(_size);
|
||||
|
||||
if (_head < _tail)
|
||||
{
|
||||
|
|
@ -280,7 +286,7 @@ namespace Server.Collections
|
|||
// must be >= _size.
|
||||
private void SetCapacity(int capacity)
|
||||
{
|
||||
T[] newarray = ArrayPool<T>.Shared.Rent(capacity);
|
||||
T[] newarray = (_mt ? ArrayPool<T>.Shared : STArrayPool<T>.Shared).Rent(capacity);
|
||||
if (_size > 0)
|
||||
{
|
||||
if (_head < _tail)
|
||||
|
|
@ -296,7 +302,8 @@ namespace Server.Collections
|
|||
|
||||
if (_array.Length > 0)
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(_array, true);
|
||||
Clear();
|
||||
(_mt ? ArrayPool<T>.Shared : STArrayPool<T>.Shared).Return(_array);
|
||||
}
|
||||
|
||||
_array = newarray;
|
||||
|
|
@ -377,7 +384,8 @@ namespace Server.Collections
|
|||
var array = _array;
|
||||
if (array.Length > 0)
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array, true);
|
||||
Clear();
|
||||
(_mt ? ArrayPool<T>.Shared : STArrayPool<T>.Shared).Return(array);
|
||||
}
|
||||
|
||||
this = default;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue