This commit is contained in:
陈思海 2025-12-24 14:34:26 +08:00
parent 03c0fe6723
commit d06c59ddf8
7 changed files with 581 additions and 235 deletions

View File

@ -12,87 +12,94 @@ namespace AlicizaX
public static class EventContainer<TPayload> where TPayload : struct, IEventArgs
{
private static readonly int InitialSize = EventInitialSize<TPayload>.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<TPayload>[] _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<Action<TPayload>> _activeHandlers = new();
#if Event_StrictCheck
private static System.Collections.Generic.HashSet<Action<TPayload>> _activeHandlers;
#endif
static EventContainer()
{
TypeId = UnsubscribeRegistry.Register(Unsubscribe);
_callbacks = new Action<TPayload>[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<Action<TPayload>>();
#endif
}
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct Handler
{
public Action<TPayload> 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<TPayload> 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);
}
#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<TPayload>[] 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
}
}
}

View File

@ -21,17 +21,34 @@ namespace AlicizaX
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Publish<T>(in T evt) where T : struct, IEventArgs
{
EventContainer<T>.Publish(evt);
EventContainer<T>.Publish(in evt);
}
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Publish<T>(Action<T> init) where T : struct, IEventArgs
{
var evt = default(T);
init(evt);
Publish(in evt);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetSubscriberCount<T>() where T : struct, IEventArgs
{
return EventContainer<T>.SubscriberCount;
}
public static void EnsureCapacity<T>(int capacity) where T : struct, IEventArgs
{
EventContainer<T>.EnsureCapacity(capacity);
}
public static void Clear<T>() where T : struct, IEventArgs
{
EventContainer<T>.Clear();
}
}
}

View File

@ -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<int,int> _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<int,int> 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);
}
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.CompilerServices;
namespace AlicizaX
{
internal static class UnsubscribeRegistry
{
private static Action<int, int>[] _handlers = new Action<int, int>[32];
private static int _nextId = 1; // 0表示无效
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int Register(Action<int, int> 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);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5342adccb3de43ef8edcf1cfe8b41712
timeCreated: 1766556356

View File

@ -2,63 +2,22 @@ using System;
using System.Collections.Generic;
using AlicizaX;
namespace AlicizaX.Timer.Runtime
namespace AlicizaX
{
/// <summary>
/// 定时器接口
/// </summary>
[UnityEngine.Scripting.Preserve]
public interface ITimerModule : IModule, IModuleUpdate
{
/// <summary>
/// 添加计时器。
/// </summary>
/// <param name="callback">计时器回调。</param>
/// <param name="time">计时器间隔。</param>
/// <param name="isLoop">是否循环。</param>
/// <param name="isUnscaled">是否不收时间缩放影响。</param>
/// <param name="args">传参。(避免闭包)</param>
/// <returns>计时器Id。</returns>
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);
/// <summary>
/// 暂停计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void Stop(int timerId);
/// <summary>
/// 恢复计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void Resume(int timerId);
/// <summary>
/// 计时器是否在运行中。
/// </summary>
/// <param name="timerId">计时器Id。</param>
/// <returns>否在运行中。</returns>
public bool IsRunning(int timerId);
/// <summary>
/// 获得计时器剩余时间。
/// </summary>
public float GetLeftTime(int timerId);
/// <summary>
/// 重置计时器,恢复到开始状态。
/// </summary>
public void Restart(int timerId);
/// <summary>
/// 移除计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void RemoveTimer(int timerId);
/// <summary>
/// 移除所有计时器。
/// </summary>
public void RemoveAllTimer();
// 优化1: 使用值类型泛型参数完全零GC
int AddTimer<T>(Action<T> 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();
}
}

View File

@ -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<Timer> Node; // 用于时间轮中的链表节点
public bool IsActive;
public byte HandlerType; // 0=Handler, 1=NoArgs, 2=Generic<struct>, 3=Generic<class>
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<int, Timer> _activeTimers = new Dictionary<int, Timer>();
private readonly Action<Timer> _processTimerDelegate;
private class TimeWheel
{
private readonly float _slotInterval; // 每个槽的时间间隔(秒)
private readonly int _slotCount; // 槽的数量
private readonly LinkedList<Timer>[] _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<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Invoke(Delegate callback, object arg)
{
((Action<T>)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 HierarchicalTimeWheel(float slotInterval)
{
_slotInterval = slotInterval;
_slotCount = slotCount;
_slots = new LinkedList<Timer>[slotCount];
for (int i = 0; i < slotCount; i++)
_slots[i] = new LinkedList<Timer>();
_currentTime = 0f;
_currentSlotIndex = 0;
_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);
}
public void AddTimer(Timer timer, float currentTime)
[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<Timer> processTimer)
public void Advance(float currentTime, TimerInfo[] pool, Action<int> 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<Timer> currentSlot = _slots[_currentSlotIndex];
LinkedListNode<Timer> currentNode = currentSlot.First;
while (currentNode != null)
if ((_levels[0].CurrentSlot % SLOT_COUNT_LEVEL0) == 0)
{
LinkedListNode<Timer> nextNode = currentNode.Next;
Timer timer = currentNode.Value;
currentSlot.Remove(currentNode);
if (timer.IsActive && timer.IsRunning)
{
processTimer(timer);
ProcessLevel(1, pool, processTimer);
}
currentNode = nextNode;
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<int> 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<T>(Action<T> 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))
{
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;
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);
}
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<Timer>();
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
{
switch (timer.HandlerType)
{
case 0: // TimerHandler
timer.Handler?.Invoke(timer.Args);
break;
case 1: // NoArgs
timer.HandlerNoArgs?.Invoke();
break;
case 2: // Generic<struct>
case 3: // Generic<class>
// 直接调用利用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
{