ModernUO/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs
Kamron Batman 76fddcbccd
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
2022-03-22 09:58:24 -07:00

71 lines
2.1 KiB
C#

using System;
using Server.Buffers;
using Xunit;
namespace Server.Tests.Tests.Buffers;
public class STArrayPoolTests
{
[Theory]
[InlineData(0, 0)]
[InlineData(2, 16)]
[InlineData(56, 64)]
[InlineData(120, 128)]
[InlineData(65535, 65536)]
[InlineData(1024 * 1024 * 15, 1024 * 1024 * 16)]
public void ValidMinimumLengths(int requestedLength, int expectedLength)
{
var arr = STArrayPool<byte>.Shared.Rent(requestedLength);
Assert.Equal(expectedLength, arr.Length);
}
[Fact]
public void NegativeLengthThrows()
{
Assert.Throws<ArgumentOutOfRangeException>(
() =>
{
var arr = STArrayPool<byte>.Shared.Rent(-1);
}
);
}
[Fact]
public void CachesOnlyUpToCPUCountPerBucket()
{
STArrayPool<byte>.Shared.ResetForTesting();
var cores = Environment.ProcessorCount;
var arrays1 = new byte[cores * 8 + 2][]; // 1 for the cache, and 8 * CPU for the stacks
var weakReferences1 = new WeakReference[cores * 8 + 2];
var arrays2 = new byte[cores * 8 + 2][]; // 1 for the cache, and 8 * CPU for the stacks
var weakReferences2 = new WeakReference[cores * 8 + 2];
for (var i = 0; i < arrays1.Length; i++)
{
arrays1[i] = STArrayPool<byte>.Shared.Rent(32);
weakReferences1[i] = new WeakReference(arrays1[i]);
arrays2[i] = STArrayPool<byte>.Shared.Rent(64);
weakReferences2[i] = new WeakReference(arrays1[i]);
}
for (var i = 0; i < arrays1.Length; i++)
{
STArrayPool<byte>.Shared.Return(arrays1[i]);
arrays1[i] = null;
STArrayPool<byte>.Shared.Return(arrays2[i]);
arrays2[i] = null;
}
GC.Collect();
for (var i = 0; i < weakReferences1.Length; i++)
{
// When the last one is returned, the one right before it is dropped.
Assert.Equal(i != weakReferences1.Length - 2, weakReferences1[i].IsAlive);
Assert.Equal(i != weakReferences2.Length - 2, weakReferences2[i].IsAlive);
}
}
}