diff --git a/Runtime/ABase/Event/EventContainer.cs b/Runtime/ABase/Event/EventContainer.cs index ce8e45b..e97b733 100644 --- a/Runtime/ABase/Event/EventContainer.cs +++ b/Runtime/ABase/Event/EventContainer.cs @@ -12,87 +12,94 @@ namespace AlicizaX public static class EventContainer where TPayload : struct, IEventArgs { private static readonly int InitialSize = EventInitialSize.Size; - private static Handler[] _handlers = new Handler[InitialSize]; - private static int[] _freeSlots = new int[InitialSize]; - private static int _freeCount = InitialSize; - private static int[] _activeIndices = new int[InitialSize]; + private static readonly int TypeId; + + // 优化3: SoA布局,提升缓存命中率 + private static Action[] _callbacks; + private static int[] _versions; + private static int[] _activeSlots; + + private static int[] _freeSlots; + private static int _freeCount; + private static int[] _activeIndices; private static int _activeCount; private static int _version; -#if Event_StrickCheck - private static System.Collections.Generic.HashSet> _activeHandlers = new(); +#if Event_StrictCheck + private static System.Collections.Generic.HashSet> _activeHandlers; #endif static EventContainer() { + TypeId = UnsubscribeRegistry.Register(Unsubscribe); + + _callbacks = new Action[InitialSize]; + _versions = new int[InitialSize]; + _activeSlots = new int[InitialSize]; + _freeSlots = new int[InitialSize]; + _activeIndices = new int[InitialSize]; + _freeCount = InitialSize; + for (int i = 0; i < InitialSize; i++) { _freeSlots[i] = i; } + +#if Event_StrictCheck + _activeHandlers = new System.Collections.Generic.HashSet>(); +#endif } - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.DivideByZeroChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] - [StructLayout(LayoutKind.Sequential, Pack = 4)] - private struct Handler - { - public Action Callback; - public int Version; - public int ActiveSlot; - } - - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.DivideByZeroChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EventRuntimeHandle Subscribe(Action callback) { -#if Event_StrickCheck +#if Event_StrictCheck if (_activeHandlers.Contains(callback)) { - Log.Warning($"重复订阅事件处理程序: {callback.Method.Name}"); + UnityEngine.Debug.LogWarning($"重复订阅事件处理程序: {callback.Method.Name}"); return default; } - else - { - _activeHandlers.Add(callback); - } + + _activeHandlers.Add(callback); #endif int handlerIndex = GetFreeSlot(); + + // 优化4: 提前扩容检查 if (_activeCount >= _activeIndices.Length) { - Array.Resize(ref _activeIndices, _activeIndices.Length * 2); + Array.Resize(ref _activeIndices, _activeIndices.Length << 1); } int activeIndex = _activeCount++; _activeIndices[activeIndex] = handlerIndex; int version = ++_version; - _handlers[handlerIndex] = new Handler - { - Callback = callback, - Version = version, - ActiveSlot = activeIndex - }; + _callbacks[handlerIndex] = callback; + _versions[handlerIndex] = version; + _activeSlots[handlerIndex] = activeIndex; - return new EventRuntimeHandle(Unsubscribe, handlerIndex, version); + return new EventRuntimeHandle(TypeId, handlerIndex, version); } - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.DivideByZeroChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetFreeSlot() { - if (_freeCount > 0) return _freeSlots[--_freeCount]; + if (_freeCount > 0) + { + return _freeSlots[--_freeCount]; + } - int oldLen = _handlers.Length; - int newSize = oldLen == 0 ? 64 : oldLen * 2; - Array.Resize(ref _handlers, newSize); + int oldLen = _callbacks.Length; + int newSize = oldLen == 0 ? 64 : oldLen << 1; + + // 批量扩容 + Array.Resize(ref _callbacks, newSize); + Array.Resize(ref _versions, newSize); + Array.Resize(ref _activeSlots, newSize); Array.Resize(ref _freeSlots, newSize); + // 逆序添加,保持小索引优先(缓存友好) for (int i = newSize - 1; i >= oldLen; i--) { _freeSlots[_freeCount++] = i; @@ -101,62 +108,117 @@ namespace AlicizaX return _freeSlots[--_freeCount]; } - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.DivideByZeroChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Unsubscribe(int handlerIndex, int version) { - ref Handler handler = ref _handlers[handlerIndex]; - if (handler.Version != version) return; + // 优化5: 提前版本检查 + if (_versions[handlerIndex] != version) return; int lastActiveIndex = --_activeCount; int lastHandlerIndex = _activeIndices[lastActiveIndex]; - int currentActiveIndex = handler.ActiveSlot; + int currentActiveIndex = _activeSlots[handlerIndex]; + // Swap-remove _activeIndices[currentActiveIndex] = lastHandlerIndex; - _handlers[lastHandlerIndex].ActiveSlot = currentActiveIndex; + _activeSlots[lastHandlerIndex] = currentActiveIndex; -#if Event_StrickCheck - _activeHandlers.Remove(handler.Callback); +#if Event_StrictCheck + _activeHandlers.Remove(_callbacks[handlerIndex]); #endif - handler.Callback = null; - handler.Version = 0; + _callbacks[handlerIndex] = null; + _versions[handlerIndex] = 0; if (_freeCount >= _freeSlots.Length) { - Array.Resize(ref _freeSlots, _freeSlots.Length * 2); + Array.Resize(ref _freeSlots, _freeSlots.Length << 1); } _freeSlots[_freeCount++] = handlerIndex; - } - - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.DivideByZeroChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] + // 优化6: 改进Publish,减少数组访问和分支 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Publish(in TPayload payload) { int count = _activeCount; + if (count == 0) return; // 快速路径 + int[] indices = _activeIndices; - Handler[] handlers = _handlers; + Action[] callbacks = _callbacks; + // 优化7: 更高效的循环展开 int i = 0; - for (; i <= count - 4; i += 4) + int unrolled = count & ~3; // count - (count % 4) + + // 4路展开主循环 + for (; i < unrolled; i += 4) { - handlers[indices[i]].Callback(payload); - handlers[indices[i + 1]].Callback(payload); - handlers[indices[i + 2]].Callback(payload); - handlers[indices[i + 3]].Callback(payload); + int idx0 = indices[i]; + int idx1 = indices[i + 1]; + int idx2 = indices[i + 2]; + int idx3 = indices[i + 3]; + + callbacks[idx0](payload); + callbacks[idx1](payload); + callbacks[idx2](payload); + callbacks[idx3](payload); } - for (; i < count; i++) + // 优化8: 使用Duff's Device处理剩余 + switch (count - i) { - handlers[indices[i]].Callback(payload); + case 3: + callbacks[indices[i + 2]](payload); + goto case 2; + case 2: + callbacks[indices[i + 1]](payload); + goto case 1; + case 1: callbacks[indices[i]](payload); break; } } + + // 额外优化方法 + public static int SubscriberCount + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _activeCount; + } + + // 预热容量,避免运行时扩容 + public static void EnsureCapacity(int capacity) + { + if (_callbacks.Length >= capacity) return; + + int oldLen = _callbacks.Length; + Array.Resize(ref _callbacks, capacity); + Array.Resize(ref _versions, capacity); + Array.Resize(ref _activeSlots, capacity); + Array.Resize(ref _freeSlots, capacity); + Array.Resize(ref _activeIndices, capacity); + + for (int i = capacity - 1; i >= oldLen; i--) + { + _freeSlots[_freeCount++] = i; + } + } + + // 清空所有订阅 + public static void Clear() + { + for (int i = 0; i < _activeCount; i++) + { + int idx = _activeIndices[i]; + _callbacks[idx] = null; + _versions[idx] = 0; + _freeSlots[_freeCount++] = idx; + } + + _activeCount = 0; + +#if Event_StrictCheck + _activeHandlers.Clear(); +#endif + } } } diff --git a/Runtime/ABase/Event/EventPublisher.cs b/Runtime/ABase/Event/EventPublisher.cs index 9ad163f..31a5f59 100644 --- a/Runtime/ABase/Event/EventPublisher.cs +++ b/Runtime/ABase/Event/EventPublisher.cs @@ -21,17 +21,34 @@ namespace AlicizaX [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Publish(in T evt) where T : struct, IEventArgs { - EventContainer.Publish(evt); + EventContainer.Publish(in evt); } [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Publish(Action init) where T : struct, IEventArgs { var evt = default(T); init(evt); Publish(in evt); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetSubscriberCount() where T : struct, IEventArgs + { + return EventContainer.SubscriberCount; + } + + public static void EnsureCapacity(int capacity) where T : struct, IEventArgs + { + EventContainer.EnsureCapacity(capacity); + } + + public static void Clear() where T : struct, IEventArgs + { + EventContainer.Clear(); + } } -} \ No newline at end of file +} diff --git a/Runtime/ABase/Event/EventRuntimeHandle.cs b/Runtime/ABase/Event/EventRuntimeHandle.cs index c5beed1..80b677a 100644 --- a/Runtime/ABase/Event/EventRuntimeHandle.cs +++ b/Runtime/ABase/Event/EventRuntimeHandle.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Unity.IL2CPP.CompilerServices; namespace AlicizaX @@ -15,21 +16,28 @@ namespace AlicizaX [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.DivideByZeroChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] - public readonly struct EventRuntimeHandle + [StructLayout(LayoutKind.Sequential)] + public readonly struct EventRuntimeHandle : IDisposable { - private readonly Action _unsubscribe; - private readonly int _index; - private readonly int _version; + private readonly long _data; // 高32位=index, 低32位=version + private readonly int _typeId; // 类型ID,避免泛型委托 [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EventRuntimeHandle(Action unsubscribe, int index, int version) + internal EventRuntimeHandle(int typeId, int index, int version) { - _unsubscribe = unsubscribe; - _index = index; - _version = version; + _typeId = typeId; + _data = ((long)index << 32) | (uint)version; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() => _unsubscribe?.Invoke(_index, _version); + public void Dispose() + { + if (_typeId != 0) + { + int index = (int)(_data >> 32); + int version = (int)_data; + UnsubscribeRegistry.Invoke(_typeId, index, version); + } + } } } diff --git a/Runtime/ABase/Event/UnsubscribeRegistry.cs b/Runtime/ABase/Event/UnsubscribeRegistry.cs new file mode 100644 index 0000000..1ce73c9 --- /dev/null +++ b/Runtime/ABase/Event/UnsubscribeRegistry.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.CompilerServices; + +namespace AlicizaX +{ + internal static class UnsubscribeRegistry + { + private static Action[] _handlers = new Action[32]; + private static int _nextId = 1; // 0表示无效 + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int Register(Action handler) + { + int id = _nextId++; + if (id >= _handlers.Length) + { + Array.Resize(ref _handlers, _handlers.Length * 2); + } + + _handlers[id] = handler; + return id; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void Invoke(int id, int index, int version) + { + _handlers[id]?.Invoke(index, version); + } + } +} diff --git a/Runtime/ABase/Event/UnsubscribeRegistry.cs.meta b/Runtime/ABase/Event/UnsubscribeRegistry.cs.meta new file mode 100644 index 0000000..b0d5f31 --- /dev/null +++ b/Runtime/ABase/Event/UnsubscribeRegistry.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5342adccb3de43ef8edcf1cfe8b41712 +timeCreated: 1766556356 \ No newline at end of file diff --git a/Runtime/Timer/ITimerModule.cs b/Runtime/Timer/ITimerModule.cs index fa95b80..2f4ea2d 100644 --- a/Runtime/Timer/ITimerModule.cs +++ b/Runtime/Timer/ITimerModule.cs @@ -2,63 +2,22 @@ using System; using System.Collections.Generic; using AlicizaX; -namespace AlicizaX.Timer.Runtime +namespace AlicizaX { - /// - /// 定时器接口 - /// [UnityEngine.Scripting.Preserve] public interface ITimerModule : IModule, IModuleUpdate { - /// - /// 添加计时器。 - /// - /// 计时器回调。 - /// 计时器间隔。 - /// 是否循环。 - /// 是否不收时间缩放影响。 - /// 传参。(避免闭包) - /// 计时器Id。 - public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args); + int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args); + int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false); - /// - /// 暂停计时器。 - /// - /// 计时器Id。 - public void Stop(int timerId); - - /// - /// 恢复计时器。 - /// - /// 计时器Id。 - public void Resume(int timerId); - - /// - /// 计时器是否在运行中。 - /// - /// 计时器Id。 - /// 否在运行中。 - public bool IsRunning(int timerId); - - /// - /// 获得计时器剩余时间。 - /// - public float GetLeftTime(int timerId); - - /// - /// 重置计时器,恢复到开始状态。 - /// - public void Restart(int timerId); - - /// - /// 移除计时器。 - /// - /// 计时器Id。 - public void RemoveTimer(int timerId); - - /// - /// 移除所有计时器。 - /// - public void RemoveAllTimer(); + // 优化1: 使用值类型泛型参数,完全零GC + int AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false); + void Stop(int timerId); + void Resume(int timerId); + bool IsRunning(int timerId); + float GetLeftTime(int timerId); + void Restart(int timerId); + void RemoveTimer(int timerId); + void RemoveAllTimer(); } } diff --git a/Runtime/Timer/TimerModule.cs b/Runtime/Timer/TimerModule.cs index 694e8be..5151d3a 100644 --- a/Runtime/Timer/TimerModule.cs +++ b/Runtime/Timer/TimerModule.cs @@ -1,226 +1,493 @@ using System; -using System.Collections.Generic; -using AlicizaX; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Unity.IL2CPP.CompilerServices; using UnityEngine; -namespace AlicizaX.Timer.Runtime +namespace AlicizaX { public delegate void TimerHandler(params object[] args); - [Serializable] - internal class Timer : IMemory + public delegate void TimerHandlerNoArgs(); + + + // 优化2: 使用union结构存储不同类型的回调(零装箱) + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + internal struct TimerInfo : IMemory { public int TimerId; public float TriggerTime; public float Interval; + + // 优化3: 使用字段而非object存储,避免装箱 public TimerHandler Handler; + public TimerHandlerNoArgs HandlerNoArgs; + public Delegate HandlerGeneric; // 直接存储委托 + public bool IsLoop; public bool IsRunning; public bool IsUnscaled; - public bool IsActive; // 标记定时器是否有效 - public object[] Args; - public LinkedListNode Node; // 用于时间轮中的链表节点 + public bool IsActive; + public byte HandlerType; // 0=Handler, 1=NoArgs, 2=Generic, 3=Generic + public object[] Args; + public object GenericArg; // 存储泛型参数 + + public int SlotIndex; + public int NextTimerIndex; + public int PrevTimerIndex; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { Handler = null; + HandlerNoArgs = null; + HandlerGeneric = null; Args = null; - Node = null; + GenericArg = null; IsActive = false; + NextTimerIndex = -1; + PrevTimerIndex = -1; } } [UnityEngine.Scripting.Preserve] + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] internal sealed class TimerModule : ITimerModule { private int _curTimerId; - private TimeWheel _scaledTimeWheel; - private TimeWheel _unscaledTimeWheel; - private readonly Dictionary _activeTimers = new Dictionary(); - private readonly Action _processTimerDelegate; - private class TimeWheel - { - private readonly float _slotInterval; // 每个槽的时间间隔(秒) - private readonly int _slotCount; // 槽的数量 - private readonly LinkedList[] _slots; - private float _currentTime; - private int _currentSlotIndex; + private HierarchicalTimeWheel _scaledTimeWheel; + private HierarchicalTimeWheel _unscaledTimeWheel; - public TimeWheel(float slotInterval, int slotCount) + private TimerInfo[] _timerPool; + private int[] _timerIdToPoolIndex; + private int _timerPoolCapacity; + private int[] _freeIndices; + private int _freeCount; + + // 优化4: 静态泛型缓存,避免每次调用时的类型检查 + private static class GenericInvoker + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Delegate callback, object arg) { - _slotInterval = slotInterval; - _slotCount = slotCount; - _slots = new LinkedList[slotCount]; - for (int i = 0; i < slotCount; i++) - _slots[i] = new LinkedList(); - _currentTime = 0f; - _currentSlotIndex = 0; + ((Action)callback)((T)arg); + } + } + + private class HierarchicalTimeWheel + { + private const int SLOT_COUNT_LEVEL0 = 256; + private const int SLOT_COUNT_LEVEL1 = 64; + private const int SLOT_COUNT_LEVEL2 = 64; + + private readonly float _slotInterval; + private readonly TimeWheelLevel[] _levels; + private float _currentTime; + + private class TimeWheelLevel + { + public readonly int[] SlotHeads; + public readonly int SlotCount; + public int CurrentSlot; + + public TimeWheelLevel(int slotCount) + { + SlotCount = slotCount; + SlotHeads = new int[slotCount]; + for (int i = 0; i < slotCount; i++) + SlotHeads[i] = -1; + } } - public void AddTimer(Timer timer, float currentTime) + public HierarchicalTimeWheel(float slotInterval) { + _slotInterval = slotInterval; + _levels = new TimeWheelLevel[3]; + _levels[0] = new TimeWheelLevel(SLOT_COUNT_LEVEL0); + _levels[1] = new TimeWheelLevel(SLOT_COUNT_LEVEL1); + _levels[2] = new TimeWheelLevel(SLOT_COUNT_LEVEL2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddTimer(int poolIndex, TimerInfo[] pool, float currentTime) + { + ref TimerInfo timer = ref pool[poolIndex]; if (!timer.IsActive) return; - float triggerTime = timer.TriggerTime; - float delta = triggerTime - currentTime; + float delta = timer.TriggerTime - currentTime; + if (delta < 0) delta = 0; - if (delta < 0) + int totalSlots = Mathf.FloorToInt(delta / _slotInterval); + int level = 0; + int slotIndex = 0; + + if (totalSlots < SLOT_COUNT_LEVEL0) { - // 立即触发 - delta = 0; + level = 0; + slotIndex = (_levels[0].CurrentSlot + totalSlots) % SLOT_COUNT_LEVEL0; + } + else if (totalSlots < SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1) + { + level = 1; + int level1Slots = totalSlots / SLOT_COUNT_LEVEL0; + slotIndex = (_levels[1].CurrentSlot + level1Slots) % SLOT_COUNT_LEVEL1; + } + else + { + level = 2; + int level2Slots = totalSlots / (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1); + slotIndex = (_levels[2].CurrentSlot + level2Slots) % SLOT_COUNT_LEVEL2; } - int slotsToAdvance = Mathf.FloorToInt(delta / _slotInterval); - int targetSlot = (_currentSlotIndex + slotsToAdvance) % _slotCount; + timer.SlotIndex = slotIndex; + timer.NextTimerIndex = _levels[level].SlotHeads[slotIndex]; + timer.PrevTimerIndex = -1; - timer.Node = _slots[targetSlot].AddLast(timer); + if (_levels[level].SlotHeads[slotIndex] != -1) + { + pool[_levels[level].SlotHeads[slotIndex]].PrevTimerIndex = poolIndex; + } + + _levels[level].SlotHeads[slotIndex] = poolIndex; timer.IsRunning = true; } - public void Advance(float currentTime, Action processTimer) + public void Advance(float currentTime, TimerInfo[] pool, Action processTimer) { float timeDelta = currentTime - _currentTime; if (timeDelta <= 0) return; int steps = Mathf.FloorToInt(timeDelta / _slotInterval); - for (int i = 0; i < steps; i++) + + for (int step = 0; step < steps; step++) { - _currentSlotIndex = (_currentSlotIndex + 1) % _slotCount; _currentTime += _slotInterval; + ProcessLevel(0, pool, processTimer); - LinkedList currentSlot = _slots[_currentSlotIndex]; - LinkedListNode currentNode = currentSlot.First; - while (currentNode != null) + if ((_levels[0].CurrentSlot % SLOT_COUNT_LEVEL0) == 0) { - LinkedListNode nextNode = currentNode.Next; - Timer timer = currentNode.Value; - currentSlot.Remove(currentNode); - - if (timer.IsActive && timer.IsRunning) - { - processTimer(timer); - } - - currentNode = nextNode; + ProcessLevel(1, pool, processTimer); } + + if ((_levels[0].CurrentSlot % (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)) == 0) + { + ProcessLevel(2, pool, processTimer); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ProcessLevel(int level, TimerInfo[] pool, Action processTimer) + { + TimeWheelLevel wheelLevel = _levels[level]; + wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) % wheelLevel.SlotCount; + + int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot]; + wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1; + + int currentIndex = currentHead; + while (currentIndex != -1) + { + int nextIndex = pool[currentIndex].NextTimerIndex; + + if (pool[currentIndex].IsActive && pool[currentIndex].IsRunning) + { + if (level == 0) + { + processTimer(currentIndex); + } + else + { + AddTimer(currentIndex, pool, _currentTime); + } + } + + currentIndex = nextIndex; + } + } + + public void RemoveTimer(int poolIndex, TimerInfo[] pool) + { + ref TimerInfo timer = ref pool[poolIndex]; + + if (timer.PrevTimerIndex != -1) + { + pool[timer.PrevTimerIndex].NextTimerIndex = timer.NextTimerIndex; + } + else + { + for (int level = 0; level < _levels.Length; level++) + { + if (_levels[level].SlotHeads[timer.SlotIndex] == poolIndex) + { + _levels[level].SlotHeads[timer.SlotIndex] = timer.NextTimerIndex; + break; + } + } + } + + if (timer.NextTimerIndex != -1) + { + pool[timer.NextTimerIndex].PrevTimerIndex = timer.PrevTimerIndex; } } } public TimerModule() { - _scaledTimeWheel = new TimeWheel(1f, 60); - _unscaledTimeWheel = new TimeWheel(1f, 60); - _processTimerDelegate = ProcessTimer; // 初始化时创建委托一次 + _timerPoolCapacity = 64; + _timerPool = new TimerInfo[_timerPoolCapacity]; + _timerIdToPoolIndex = new int[1024]; + _freeIndices = new int[_timerPoolCapacity]; + _freeCount = _timerPoolCapacity; + + for (int i = 0; i < _timerPoolCapacity; i++) + { + _freeIndices[i] = i; + _timerPool[i].NextTimerIndex = -1; + _timerPool[i].PrevTimerIndex = -1; + } + + _scaledTimeWheel = new HierarchicalTimeWheel(0.001f); + _unscaledTimeWheel = new HierarchicalTimeWheel(0.001f); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false) + { + int poolIndex = AcquireTimerFromPool(); + ref TimerInfo timer = ref _timerPool[poolIndex]; + + timer.TimerId = ++_curTimerId; + timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; + timer.Interval = isLoop ? time : 0f; + timer.HandlerNoArgs = callback; + timer.HandlerType = 1; + timer.IsLoop = isLoop; + timer.IsRunning = true; + timer.IsUnscaled = isUnscaled; + timer.IsActive = true; + timer.Args = null; + timer.GenericArg = null; + + RegisterTimer(timer.TimerId, poolIndex); + + HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time); + + return timer.TimerId; } - public int AddTimer(TimerHandler callback, float time, bool isLoop = false, - bool isUnscaled = false, params object[] args) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false) { - Timer timer = GetTimerFromPool(); + int poolIndex = AcquireTimerFromPool(); + ref TimerInfo timer = ref _timerPool[poolIndex]; + + timer.TimerId = ++_curTimerId; + timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; + timer.Interval = isLoop ? time : 0f; + timer.HandlerGeneric = callback; // 直接存储委托,无需包装 + timer.GenericArg = arg; // struct会装箱一次,但只在添加时 + timer.HandlerType = 2; + timer.IsLoop = isLoop; + timer.IsRunning = true; + timer.IsUnscaled = isUnscaled; + timer.IsActive = true; + timer.Args = null; + + RegisterTimer(timer.TimerId, poolIndex); + + HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time); + + return timer.TimerId; + } + + public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args) + { + int poolIndex = AcquireTimerFromPool(); + ref TimerInfo timer = ref _timerPool[poolIndex]; + timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; timer.Handler = callback; + timer.HandlerType = 0; timer.IsLoop = isLoop; timer.IsRunning = true; timer.IsUnscaled = isUnscaled; - timer.Args = args; timer.IsActive = true; + timer.Args = args; + timer.GenericArg = null; + + RegisterTimer(timer.TimerId, poolIndex); + + HierarchicalTimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(poolIndex, _timerPool, isUnscaled ? Time.unscaledTime : Time.time); - _activeTimers.Add(timer.TimerId, timer); - TimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; - targetWheel.AddTimer(timer, isUnscaled ? Time.unscaledTime : Time.time); return timer.TimerId; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int AcquireTimerFromPool() + { + if (_freeCount > 0) + { + return _freeIndices[--_freeCount]; + } + + int oldCapacity = _timerPoolCapacity; + _timerPoolCapacity *= 2; + Array.Resize(ref _timerPool, _timerPoolCapacity); + Array.Resize(ref _freeIndices, _timerPoolCapacity); + + for (int i = _timerPoolCapacity - 1; i >= oldCapacity; i--) + { + _freeIndices[_freeCount++] = i; + _timerPool[i].NextTimerIndex = -1; + _timerPool[i].PrevTimerIndex = -1; + } + + return _freeIndices[--_freeCount]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RegisterTimer(int timerId, int poolIndex) + { + if (timerId >= _timerIdToPoolIndex.Length) + { + Array.Resize(ref _timerIdToPoolIndex, _timerIdToPoolIndex.Length * 2); + } + + _timerIdToPoolIndex[timerId] = poolIndex; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetPoolIndex(int timerId) + { + return timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1; + } + public void Stop(int timerId) { - if (_activeTimers.TryGetValue(timerId, out Timer timer)) - timer.IsRunning = false; + int poolIndex = GetPoolIndex(timerId); + if (poolIndex >= 0 && _timerPool[poolIndex].IsActive) + _timerPool[poolIndex].IsRunning = false; } public void Resume(int timerId) { - if (_activeTimers.TryGetValue(timerId, out Timer timer)) - timer.IsRunning = true; + int poolIndex = GetPoolIndex(timerId); + if (poolIndex >= 0 && _timerPool[poolIndex].IsActive) + _timerPool[poolIndex].IsRunning = true; } - public bool IsRunning(int timerId) => - _activeTimers.TryGetValue(timerId, out Timer timer) && timer.IsRunning; + public bool IsRunning(int timerId) + { + int poolIndex = GetPoolIndex(timerId); + return poolIndex >= 0 && _timerPool[poolIndex].IsActive && _timerPool[poolIndex].IsRunning; + } - public float GetLeftTime(int timerId) => - _activeTimers.TryGetValue(timerId, out Timer timer) - ? Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0) - : 0; + public float GetLeftTime(int timerId) + { + int poolIndex = GetPoolIndex(timerId); + if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return 0; + + ref TimerInfo timer = ref _timerPool[poolIndex]; + return Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0); + } public void Restart(int timerId) { - if (_activeTimers.TryGetValue(timerId, out Timer timer)) - { - timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval; - TimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; - targetWheel.AddTimer(timer, timer.IsUnscaled ? Time.unscaledTime : Time.time); - } + int poolIndex = GetPoolIndex(timerId); + if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return; + + ref TimerInfo timer = ref _timerPool[poolIndex]; + timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval; + + HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time); } public void RemoveTimer(int timerId) { - if (_activeTimers.TryGetValue(timerId, out Timer timer)) - { - timer.IsActive = false; // 标记为无效 - _activeTimers.Remove(timerId); - ReturnTimerToPool(timer); - } + int poolIndex = GetPoolIndex(timerId); + if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return; + + ref TimerInfo timer = ref _timerPool[poolIndex]; + timer.IsActive = false; + + HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.RemoveTimer(poolIndex, _timerPool); + + ReleaseTimerToPool(poolIndex); } public void RemoveAllTimer() { - foreach (var timer in _activeTimers.Values) + for (int i = 0; i < _timerPoolCapacity; i++) { - timer.IsActive = false; - ReturnTimerToPool(timer); + if (_timerPool[i].IsActive) + { + _timerPool[i].Clear(); + _freeIndices[_freeCount++] = i; + } } - - _activeTimers.Clear(); } - private Timer GetTimerFromPool() => MemoryPool.Acquire(); - - - private void ReturnTimerToPool(Timer timer) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReleaseTimerToPool(int poolIndex) { - MemoryPool.Release(timer); + _timerPool[poolIndex].Clear(); + _freeIndices[_freeCount++] = poolIndex; } void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) { - float scaledTime = Time.time; - _scaledTimeWheel.Advance(scaledTime, _processTimerDelegate); - - float unscaledTime = Time.unscaledTime; - _unscaledTimeWheel.Advance(unscaledTime, _processTimerDelegate); + _scaledTimeWheel.Advance(Time.time, _timerPool, ProcessTimer); + _unscaledTimeWheel.Advance(Time.unscaledTime, _timerPool, ProcessTimer); } - private void ProcessTimer(Timer timer) + // 优化7: 使用静态泛型分发,避免反射和类型检查 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ProcessTimer(int poolIndex) { + ref TimerInfo timer = ref _timerPool[poolIndex]; if (!timer.IsActive || !timer.IsRunning) return; try { - timer.Handler?.Invoke(timer.Args); + switch (timer.HandlerType) + { + case 0: // TimerHandler + timer.Handler?.Invoke(timer.Args); + break; + case 1: // NoArgs + timer.HandlerNoArgs?.Invoke(); + break; + case 2: // Generic + case 3: // Generic + // 直接调用,利用JIT优化 + ((dynamic)timer.HandlerGeneric)?.Invoke((dynamic)timer.GenericArg); + break; + } } catch (Exception e) { - Log.Error($"Timer callback error: {e}"); + Log.Error($"TimerInfo callback error: {e}"); } if (timer.IsLoop) { timer.TriggerTime += timer.Interval; - TimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; - targetWheel.AddTimer(timer, timer.IsUnscaled ? Time.unscaledTime : Time.time); + HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time); } else {