优化MemoryPool
This commit is contained in:
parent
d1d86adf09
commit
8c3f2c99cc
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
/// 获取内存池的数量。
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user