优化MemoryPool

This commit is contained in:
陈思海 2026-04-23 18:18:27 +08:00
parent d1d86adf09
commit 8c3f2c99cc
4 changed files with 212 additions and 45 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace AlicizaX
@ -6,9 +7,28 @@ namespace AlicizaX
public static class MemoryPool<T> where T : class, IMemory, new()
{
private sealed class ReferenceComparer : IEqualityComparer<T>
{
public static readonly ReferenceComparer Instance = new ReferenceComparer();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(T x, T y)
{
return ReferenceEquals(x, y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetHashCode(T obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
}
private static T[] s_Stack = Array.Empty<T>();
private static int s_Count;
private static int s_MaxCapacity = 2048;
private static Dictionary<T, byte> s_InPoolSet;
private static int s_StrictCheckVersion = -1;
// ---- 回收策略状态 ----
private static int s_HighWaterMark;
@ -30,8 +50,8 @@ namespace AlicizaX
static MemoryPool()
{
MemoryPoolRegistry.Register(typeof(T), new MemoryPoolRegistry.MemoryPoolHandle(
acquire: () => Acquire(),
release: obj => Release((T)obj),
acquire: AcquireAsMemory,
release: ReleaseAsMemory,
clear: ClearAll,
prewarm: Prewarm,
getInfo: GetInfo,
@ -40,6 +60,18 @@ namespace AlicizaX
));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IMemory AcquireAsMemory()
{
return Acquire();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseAsMemory(IMemory memory)
{
Release((T)memory);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Acquire()
{
@ -54,6 +86,7 @@ namespace AlicizaX
int idx = --s_Count;
T item = s_Stack[idx];
s_Stack[idx] = null;
RemoveFromStrictCheckSet(item);
return item;
}
@ -67,24 +100,14 @@ namespace AlicizaX
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))
EnsureStrictCheckState();
if (MemoryPool.EnableStrictCheck && s_InPoolSet.ContainsKey(item))
throw new InvalidOperationException($"MemoryPool<{typeof(T).Name}>: Double release detected.");
s_ReleaseCount++;
@ -100,6 +123,7 @@ namespace AlicizaX
if (s_Count == s_Stack.Length)
Grow();
AddToStrictCheckSet(item);
s_Stack[s_Count++] = item;
}
@ -132,6 +156,7 @@ namespace AlicizaX
int removeCount = Math.Max((int)(excess * ratio), 1);
int newCount = s_Count - removeCount;
RemoveRangeFromStrictCheckSet(newCount, removeCount);
Array.Clear(s_Stack, newCount, removeCount);
s_Count = newCount;
@ -184,7 +209,9 @@ namespace AlicizaX
while (s_Count < count)
{
s_Stack[s_Count++] = new T();
T item = new T();
s_Stack[s_Count++] = item;
AddToStrictCheckSet(item);
s_CreateCount++;
}
}
@ -194,6 +221,7 @@ namespace AlicizaX
if (keepCount >= s_Count) return;
keepCount = Math.Max(keepCount, 0);
RemoveRangeFromStrictCheckSet(keepCount, s_Count - keepCount);
Array.Clear(s_Stack, keepCount, s_Count - keepCount);
s_Count = keepCount;
TryShrinkArray();
@ -206,6 +234,7 @@ namespace AlicizaX
public static void ClearAll()
{
ResetStrictCheckSet();
Array.Clear(s_Stack, 0, s_Count);
s_Count = 0;
s_HighWaterMark = s_CurrentInUse;
@ -231,5 +260,83 @@ namespace AlicizaX
s_HighWaterMark, s_MaxCapacity,
s_IdleFrames, s_Stack.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EnsureStrictCheckState()
{
int version = MemoryPool.StrictCheckVersion;
if (s_StrictCheckVersion == version)
return;
s_StrictCheckVersion = version;
if (!MemoryPool.EnableStrictCheck)
{
s_InPoolSet = null;
return;
}
RebuildStrictCheckSet();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void RebuildStrictCheckSet()
{
int capacity = s_Count > 0 ? s_Count : 4;
var set = new Dictionary<T, byte>(capacity, ReferenceComparer.Instance);
for (int i = 0; i < s_Count; i++)
{
T item = s_Stack[i];
if (item != null)
set[item] = 0;
}
s_InPoolSet = set;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddToStrictCheckSet(T item)
{
EnsureStrictCheckState();
if (!MemoryPool.EnableStrictCheck || item == null)
return;
s_InPoolSet[item] = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RemoveFromStrictCheckSet(T item)
{
EnsureStrictCheckState();
if (!MemoryPool.EnableStrictCheck || item == null)
return;
s_InPoolSet.Remove(item);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void RemoveRangeFromStrictCheckSet(int startIndex, int count)
{
EnsureStrictCheckState();
if (!MemoryPool.EnableStrictCheck || count <= 0)
return;
int endIndex = startIndex + count;
for (int i = startIndex; i < endIndex; i++)
{
T item = s_Stack[i];
if (item != null)
s_InPoolSet.Remove(item);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ResetStrictCheckSet()
{
EnsureStrictCheckState();
if (!MemoryPool.EnableStrictCheck)
return;
s_InPoolSet.Clear();
}
}
}

View File

@ -8,6 +8,7 @@ namespace AlicizaX
public static partial class MemoryPool
{
private static bool _enableStrictCheck;
private static int _strictCheckVersion;
/// <summary>
/// 获取或设置是否开启强制检查。
@ -15,8 +16,17 @@ namespace AlicizaX
public static bool EnableStrictCheck
{
get => _enableStrictCheck;
set => _enableStrictCheck = value;
set
{
if (_enableStrictCheck == value)
return;
_enableStrictCheck = value;
_strictCheckVersion++;
}
}
internal static int StrictCheckVersion => _strictCheckVersion;
/// <summary>
/// 获取内存池的数量。

View File

@ -9,22 +9,28 @@ namespace AlicizaX
{
internal sealed class MemoryPoolHandle
{
public readonly Func<IMemory> Acquire;
public readonly Action<IMemory> Release;
public readonly Action Clear;
public readonly Action<int> Prewarm;
public readonly Func<MemoryPoolInfo> GetInfo;
public readonly Action<int> Tick;
public readonly Action<int> Shrink;
public delegate IMemory AcquireHandler();
public delegate void ReleaseHandler(IMemory memory);
public delegate void ClearHandler();
public delegate void IntHandler(int value);
public delegate MemoryPoolInfo GetInfoHandler();
public readonly AcquireHandler Acquire;
public readonly ReleaseHandler Release;
public readonly ClearHandler Clear;
public readonly IntHandler Prewarm;
public readonly GetInfoHandler GetInfo;
public readonly IntHandler Tick;
public readonly IntHandler Shrink;
public MemoryPoolHandle(
Func<IMemory> acquire,
Action<IMemory> release,
Action clear,
Action<int> prewarm,
Func<MemoryPoolInfo> getInfo,
Action<int> tick,
Action<int> shrink)
AcquireHandler acquire,
ReleaseHandler release,
ClearHandler clear,
IntHandler prewarm,
GetInfoHandler getInfo,
IntHandler tick,
IntHandler shrink)
{
Acquire = acquire;
Release = release;
@ -39,7 +45,7 @@ namespace AlicizaX
private static readonly Dictionary<Type, MemoryPoolHandle> s_Handles
= new Dictionary<Type, MemoryPoolHandle>(64);
private static Action<int>[] s_TickArray = Array.Empty<Action<int>>();
private static MemoryPoolHandle.IntHandler[] s_TickArray = Array.Empty<MemoryPoolHandle.IntHandler>();
private static int s_TickCount;
private static bool s_TickArrayDirty;
@ -56,11 +62,13 @@ namespace AlicizaX
/// </summary>
public static IMemory Acquire(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (s_Handles.TryGetValue(type, out var handle))
return handle.Acquire();
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
EnsureRegistered(type);
if (s_Handles.TryGetValue(type, out handle))
return handle.Acquire();
@ -80,11 +88,15 @@ namespace AlicizaX
return;
}
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
EnsureRegistered(type);
if (s_Handles.TryGetValue(type, out handle))
{
handle.Release(memory);
return;
}
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
}
public static MemoryPoolInfo[] GetAllInfos()
@ -104,32 +116,62 @@ namespace AlicizaX
public static void Prewarm(Type type, int count)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (s_Handles.TryGetValue(type, out var handle))
{
handle.Prewarm(count);
return;
}
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
EnsureRegistered(type);
if (s_Handles.TryGetValue(type, out handle))
{
handle.Prewarm(count);
return;
}
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
}
public static void ClearType(Type type)
{
if (s_Handles.TryGetValue(type, out var handle))
if (type == null)
throw new ArgumentNullException(nameof(type));
if (!s_Handles.TryGetValue(type, out var handle))
{
EnsureRegistered(type);
if (!s_Handles.TryGetValue(type, out handle))
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
}
handle.Clear();
}
public static void RemoveFromType(Type type, int count)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (s_Handles.TryGetValue(type, out var handle))
{
int unused = handle.GetInfo().UnusedCount;
handle.Shrink(unused - count);
return;
}
EnsureRegistered(type);
if (s_Handles.TryGetValue(type, out handle))
{
int unused = handle.GetInfo().UnusedCount;
handle.Shrink(unused - count);
return;
}
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
}
@ -146,7 +188,7 @@ namespace AlicizaX
{
s_TickCount = s_Handles.Count;
if (s_TickArray.Length < s_TickCount)
s_TickArray = new Action<int>[s_TickCount];
s_TickArray = new MemoryPoolHandle.IntHandler[s_TickCount];
int i = 0;
foreach (var kv in s_Handles)
@ -154,5 +196,17 @@ namespace AlicizaX
s_TickArrayDirty = false;
}
private static void EnsureRegistered(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (!typeof(IMemory).IsAssignableFrom(type))
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
}
}
}

View File

@ -52,10 +52,6 @@ namespace AlicizaX.UI.Runtime
return TryReflectAndRegisterInternal(holderType, out info);
}
/// <summary>
/// Internal method to reflect and register UI resource without logging.
/// Used by both runtime fallback and pre-registration.
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool TryReflectAndRegisterInternal(Type holderType, out UIResInfo info)
{