using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace AlicizaX { public static class MemoryPool where T : class, IMemory, new() { private sealed class ReferenceComparer : IEqualityComparer { 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(); private static int s_Count; private static int s_MaxCapacity = 2048; private static Dictionary s_InPoolSet; private static int s_StrictCheckVersion = -1; // ---- 回收策略状态 ---- 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: AcquireAsMemory, release: ReleaseAsMemory, clear: ClearAll, prewarm: Prewarm, getInfo: GetInfo, tick: Tick, shrink: Shrink )); } [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() { 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; RemoveFromStrictCheckSet(item); return item; } return CreateNew(); } [MethodImpl(MethodImplOptions.NoInlining)] private static T CreateNew() { s_CreateCount++; return new T(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Release(T item) { if (item == null) return; EnsureStrictCheckState(); if (MemoryPool.EnableStrictCheck && s_InPoolSet.ContainsKey(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(); AddToStrictCheckSet(item); 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; RemoveRangeFromStrictCheckSet(newCount, 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) { T item = new T(); s_Stack[s_Count++] = item; AddToStrictCheckSet(item); s_CreateCount++; } } public static void Shrink(int keepCount) { 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(); } public static void SetMaxCapacity(int max) { s_MaxCapacity = Math.Max(max, MIN_KEEP); } public static void ClearAll() { ResetStrictCheckSet(); 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(); } public static int UnusedCount { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => s_Count; } internal static void GetInfo(ref MemoryPoolInfo info) { info.Set( typeof(T), s_Count, s_CurrentInUse, s_AcquireCount, s_ReleaseCount, s_CreateCount, 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(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(); } } }