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:
Kamron Batman 2022-03-22 09:58:24 -07:00 • committed by GitHub
parent 0925a2d435
commit 76fddcbccd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 604 additions and 12 deletions

View file

@ -0,0 +1,95 @@
using System.Buffers;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Server.Buffers;
using Server.Collections;
namespace Benchmarks
{
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkPooledRefQueue
{
[GlobalSetup]
public void Setup()
{
// Allocate
var arrays = new long[16][];
for (var i = 0; i < 16; i++)
{
arrays[i] = ArrayPool<long>.Shared.Rent(64);
}
var stArrays = new long[16][];
for (var i = 0; i < 16; i++)
{
stArrays[i] = STArrayPool<long>.Shared.Rent(64);
}
for (var i = 0; i < 16; i++)
{
ArrayPool<long>.Shared.Return(arrays[i]);
}
for (var i = 0; i < 16; i++)
{
STArrayPool<long>.Shared.Return(stArrays[i]);
}
}
[Benchmark]
public void UseQueue()
{
for (var i = 0; i < 8; i++)
{
var queue = new Queue<long>();
for (var j = 0; j < 32; j++)
{
queue.Enqueue(j);
}
for (var j = 0; j < 32; j++)
{
var num = queue.Dequeue();
}
}
}
[Benchmark]
public void UsePooledRefQueue()
{
for (var i = 0; i < 8; i++)
{
using var queue = PooledRefQueue<long>.Create();
for (var j = 0; j < 32; j++)
{
queue.Enqueue(j);
}
for (var j = 0; j < 32; j++)
{
var num = queue.Dequeue();
}
}
}
[Benchmark]
public void UsePooledRefQueueMT()
{
for (var i = 0; i < 8; i++)
{
using var queue = PooledRefQueue<long>.CreateMT();
for (var j = 0; j < 32; j++)
{
queue.Enqueue(j);
}
for (var j = 0; j < 32; j++)
{
var num = queue.Dequeue();
}
}
}
}
}

View file

@ -0,0 +1,92 @@
using System.Buffers;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Server.Buffers;
namespace Benchmarks
{
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkSTArray
{
private static long[][] arrays = new long[16][];
private static long[][] stArrays = new long[16][];
private static long[][] newArrays = new long[16][];
private static Queue<long>[] newQueue = new Queue<long>[16];
[GlobalSetup]
public void Setup()
{
// Allocate
arrays = new long[16][];
for (var i = 0; i < 16; i++)
{
arrays[i] = ArrayPool<long>.Shared.Rent(64);
}
stArrays = new long[16][];
for (var i = 0; i < 16; i++)
{
stArrays[i] = STArrayPool<long>.Shared.Rent(64);
}
for (var i = 0; i < 16; i++)
{
ArrayPool<long>.Shared.Return(arrays[i]);
}
for (var i = 0; i < 16; i++)
{
STArrayPool<long>.Shared.Return(stArrays[i]);
}
}
[Benchmark]
public void ArrayPool()
{
for (var i = 0; i < 8; i++)
{
arrays[i] = ArrayPool<long>.Shared.Rent(64);
}
for (var i = 0; i < 8; i++)
{
ArrayPool<long>.Shared.Return(arrays[i], true);
}
}
[Benchmark]
public void STArrayPool()
{
for (var i = 0; i < 8; i++)
{
arrays[i] = STArrayPool<long>.Shared.Rent(64);
}
for (var i = 0; i < 8; i++)
{
STArrayPool<long>.Shared.Return(arrays[i], true);
}
}
[Benchmark]
public void NewArray()
{
for (var i = 0; i < 8; i++)
{
newArrays[i] = new long[64];
}
}
[Benchmark]
public void NewQueue()
{
for (var i = 0; i < 8; i++)
{
newQueue[i] = new Queue<long>();
newQueue[i].EnsureCapacity(64);
}
}
}
}