优化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;
using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace AlicizaX namespace AlicizaX
@ -6,9 +7,28 @@ namespace AlicizaX
public static class MemoryPool<T> where T : class, IMemory, new() 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 T[] s_Stack = Array.Empty<T>();
private static int s_Count; private static int s_Count;
private static int s_MaxCapacity = 2048; private static int s_MaxCapacity = 2048;
private static Dictionary<T, byte> s_InPoolSet;
private static int s_StrictCheckVersion = -1;
// ---- 回收策略状态 ---- // ---- 回收策略状态 ----
private static int s_HighWaterMark; private static int s_HighWaterMark;
@ -30,8 +50,8 @@ namespace AlicizaX
static MemoryPool() static MemoryPool()
{ {
MemoryPoolRegistry.Register(typeof(T), new MemoryPoolRegistry.MemoryPoolHandle( MemoryPoolRegistry.Register(typeof(T), new MemoryPoolRegistry.MemoryPoolHandle(
acquire: () => Acquire(), acquire: AcquireAsMemory,
release: obj => Release((T)obj), release: ReleaseAsMemory,
clear: ClearAll, clear: ClearAll,
prewarm: Prewarm, prewarm: Prewarm,
getInfo: GetInfo, 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Acquire() public static T Acquire()
{ {
@ -54,6 +86,7 @@ namespace AlicizaX
int idx = --s_Count; int idx = --s_Count;
T item = s_Stack[idx]; T item = s_Stack[idx];
s_Stack[idx] = null; s_Stack[idx] = null;
RemoveFromStrictCheckSet(item);
return item; return item;
} }
@ -67,24 +100,14 @@ namespace AlicizaX
return new T(); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Release(T item) public static void Release(T item)
{ {
if (item == null) return; 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."); throw new InvalidOperationException($"MemoryPool<{typeof(T).Name}>: Double release detected.");
s_ReleaseCount++; s_ReleaseCount++;
@ -100,6 +123,7 @@ namespace AlicizaX
if (s_Count == s_Stack.Length) if (s_Count == s_Stack.Length)
Grow(); Grow();
AddToStrictCheckSet(item);
s_Stack[s_Count++] = item; s_Stack[s_Count++] = item;
} }
@ -132,6 +156,7 @@ namespace AlicizaX
int removeCount = Math.Max((int)(excess * ratio), 1); int removeCount = Math.Max((int)(excess * ratio), 1);
int newCount = s_Count - removeCount; int newCount = s_Count - removeCount;
RemoveRangeFromStrictCheckSet(newCount, removeCount);
Array.Clear(s_Stack, newCount, removeCount); Array.Clear(s_Stack, newCount, removeCount);
s_Count = newCount; s_Count = newCount;
@ -184,7 +209,9 @@ namespace AlicizaX
while (s_Count < count) while (s_Count < count)
{ {
s_Stack[s_Count++] = new T(); T item = new T();
s_Stack[s_Count++] = item;
AddToStrictCheckSet(item);
s_CreateCount++; s_CreateCount++;
} }
} }
@ -194,6 +221,7 @@ namespace AlicizaX
if (keepCount >= s_Count) return; if (keepCount >= s_Count) return;
keepCount = Math.Max(keepCount, 0); keepCount = Math.Max(keepCount, 0);
RemoveRangeFromStrictCheckSet(keepCount, s_Count - keepCount);
Array.Clear(s_Stack, keepCount, s_Count - keepCount); Array.Clear(s_Stack, keepCount, s_Count - keepCount);
s_Count = keepCount; s_Count = keepCount;
TryShrinkArray(); TryShrinkArray();
@ -206,6 +234,7 @@ namespace AlicizaX
public static void ClearAll() public static void ClearAll()
{ {
ResetStrictCheckSet();
Array.Clear(s_Stack, 0, s_Count); Array.Clear(s_Stack, 0, s_Count);
s_Count = 0; s_Count = 0;
s_HighWaterMark = s_CurrentInUse; s_HighWaterMark = s_CurrentInUse;
@ -231,5 +260,83 @@ namespace AlicizaX
s_HighWaterMark, s_MaxCapacity, s_HighWaterMark, s_MaxCapacity,
s_IdleFrames, s_Stack.Length); 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 public static partial class MemoryPool
{ {
private static bool _enableStrictCheck; private static bool _enableStrictCheck;
private static int _strictCheckVersion;
/// <summary> /// <summary>
/// 获取或设置是否开启强制检查。 /// 获取或设置是否开启强制检查。
@ -15,9 +16,18 @@ namespace AlicizaX
public static bool EnableStrictCheck public static bool EnableStrictCheck
{ {
get => _enableStrictCheck; get => _enableStrictCheck;
set => _enableStrictCheck = value; set
{
if (_enableStrictCheck == value)
return;
_enableStrictCheck = value;
_strictCheckVersion++;
}
} }
internal static int StrictCheckVersion => _strictCheckVersion;
/// <summary> /// <summary>
/// 获取内存池的数量。 /// 获取内存池的数量。
/// </summary> /// </summary>

View File

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