修改
This commit is contained in:
parent
03c0fe6723
commit
d06c59ddf8
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
Runtime/ABase/Event/UnsubscribeRegistry.cs
Normal file
30
Runtime/ABase/Event/UnsubscribeRegistry.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Runtime/ABase/Event/UnsubscribeRegistry.cs.meta
Normal file
3
Runtime/ABase/Event/UnsubscribeRegistry.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5342adccb3de43ef8edcf1cfe8b41712
|
||||
timeCreated: 1766556356
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user