优化MemoryPool
This commit is contained in:
parent
d1d86adf09
commit
8c3f2c99cc
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user