com.alicizax.unity.framework/Runtime/MemoryPool/MemoryPool.Core.cs

236 lines
6.7 KiB
C#

using System;
using System.Runtime.CompilerServices;
namespace AlicizaX
{
public static class MemoryPool<T> where T : class, IMemory, new()
{
private static T[] s_Stack = Array.Empty<T>();
private static int s_Count;
private static int s_MaxCapacity = 2048;
// ---- 回收策略状态 ----
private static int s_HighWaterMark;
private static int s_RecentAcquireCount;
private static int s_IdleFrames;
private static int s_LastTickFrame;
private static int s_PeakInUse;
private static int s_CurrentInUse;
private const int IDLE_THRESHOLD = 300; // ~5s @60fps
private const int IDLE_AGGRESSIVE = 900; // ~15s @60fps
private const int MIN_KEEP = 4;
// ---- 统计计数器 ----
private static int s_AcquireCount;
private static int s_ReleaseCount;
private static int s_CreateCount;
static MemoryPool()
{
MemoryPoolRegistry.Register(typeof(T), new MemoryPoolRegistry.MemoryPoolHandle(
acquire: () => Acquire(),
release: obj => Release((T)obj),
clear: ClearAll,
prewarm: Prewarm,
getInfo: GetInfo,
tick: Tick,
shrink: Shrink
));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Acquire()
{
s_AcquireCount++;
s_RecentAcquireCount++;
s_CurrentInUse++;
if (s_CurrentInUse > s_PeakInUse)
s_PeakInUse = s_CurrentInUse;
if (s_Count > 0)
{
int idx = --s_Count;
T item = s_Stack[idx];
s_Stack[idx] = null;
return item;
}
return CreateNew();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static T CreateNew()
{
s_CreateCount++;
return new T();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool ContainsInPool(T item)
{
for (int i = 0; i < s_Count; i++)
{
if (ReferenceEquals(s_Stack[i], item))
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Release(T item)
{
if (item == null) return;
if (MemoryPool.EnableStrictCheck && ContainsInPool(item))
throw new InvalidOperationException($"MemoryPool<{typeof(T).Name}>: Double release detected.");
s_ReleaseCount++;
if (s_CurrentInUse > 0)
s_CurrentInUse--;
item.Clear();
if (s_Count >= s_MaxCapacity)
return;
if (s_Count == s_Stack.Length)
Grow();
s_Stack[s_Count++] = item;
}
internal static void Tick(int frameCount)
{
if (frameCount == s_LastTickFrame) return;
s_LastTickFrame = frameCount;
if (s_PeakInUse > s_HighWaterMark)
s_HighWaterMark = s_PeakInUse;
if (s_RecentAcquireCount == 0)
s_IdleFrames++;
else
s_IdleFrames = 0;
s_RecentAcquireCount = 0;
if (s_Count <= MIN_KEEP) return;
if (s_IdleFrames >= IDLE_THRESHOLD)
{
int target = Math.Max((int)(s_HighWaterMark * 1.5f), MIN_KEEP);
if (s_Count > target)
{
int excess = s_Count - target;
float ratio = s_IdleFrames < IDLE_AGGRESSIVE ? 0.25f : 0.5f;
int removeCount = Math.Max((int)(excess * ratio), 1);
int newCount = s_Count - removeCount;
Array.Clear(s_Stack, newCount, removeCount);
s_Count = newCount;
TryShrinkArray();
}
if (s_IdleFrames >= IDLE_AGGRESSIVE)
{
s_HighWaterMark = Math.Max(s_HighWaterMark >> 1, MIN_KEEP);
s_PeakInUse = s_CurrentInUse;
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Grow()
{
int newLen = s_Stack.Length == 0 ? 8 : s_Stack.Length << 1;
if (newLen > s_MaxCapacity) newLen = s_MaxCapacity;
var newStack = new T[newLen];
Array.Copy(s_Stack, 0, newStack, 0, s_Count);
s_Stack = newStack;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TryShrinkArray()
{
if (s_Stack.Length > 32 && s_Count < s_Stack.Length >> 2)
{
int newLen = Math.Max(s_Count << 1, 8);
var newStack = new T[newLen];
Array.Copy(s_Stack, 0, newStack, 0, s_Count);
s_Stack = newStack;
}
}
public static void Prewarm(int count)
{
count = Math.Min(count, s_MaxCapacity);
if (count <= s_Count) return;
if (count > s_Stack.Length)
{
var newStack = new T[count];
Array.Copy(s_Stack, 0, newStack, 0, s_Count);
s_Stack = newStack;
}
while (s_Count < count)
{
s_Stack[s_Count++] = new T();
s_CreateCount++;
}
}
public static void Shrink(int keepCount)
{
if (keepCount >= s_Count) return;
keepCount = Math.Max(keepCount, 0);
Array.Clear(s_Stack, keepCount, s_Count - keepCount);
s_Count = keepCount;
TryShrinkArray();
}
public static void SetMaxCapacity(int max)
{
s_MaxCapacity = Math.Max(max, MIN_KEEP);
}
public static void ClearAll()
{
Array.Clear(s_Stack, 0, s_Count);
s_Count = 0;
s_HighWaterMark = s_CurrentInUse;
s_PeakInUse = s_CurrentInUse;
s_IdleFrames = 0;
s_RecentAcquireCount = 0;
s_Stack = Array.Empty<T>();
}
public static int UnusedCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => s_Count;
}
internal static MemoryPoolInfo GetInfo()
{
return new MemoryPoolInfo(
typeof(T), s_Count,
s_CurrentInUse,
s_AcquireCount, s_ReleaseCount,
s_CreateCount,
s_HighWaterMark, s_MaxCapacity,
s_IdleFrames, s_Stack.Length);
}
}
}