优化Timer模块

This commit is contained in:
陈思海 2026-03-24 17:45:15 +08:00
parent d5518c46b1
commit d2b3a36da6
3 changed files with 129 additions and 23 deletions

View File

@ -8,6 +8,18 @@ namespace AlicizaX
{ {
public delegate void TimerHandler(params object[] args); public delegate void TimerHandler(params object[] args);
public delegate void TimerHandlerNoArgs(); public delegate void TimerHandlerNoArgs();
internal delegate void TimerGenericInvoker(Delegate handler, object arg);
internal static class TimerGenericInvokerCache<T>
{
public static readonly TimerGenericInvoker Invoke = InvokeGeneric;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InvokeGeneric(Delegate handler, object arg)
{
((Action<T>)handler)?.Invoke((T)arg);
}
}
[Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
@ -21,6 +33,7 @@ namespace AlicizaX
public TimerHandler Handler; public TimerHandler Handler;
public TimerHandlerNoArgs HandlerNoArgs; public TimerHandlerNoArgs HandlerNoArgs;
public Delegate HandlerGeneric; public Delegate HandlerGeneric;
public TimerGenericInvoker GenericInvoker;
public bool IsLoop; public bool IsLoop;
public bool IsRunning; public bool IsRunning;
@ -40,11 +53,19 @@ namespace AlicizaX
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() public void Clear()
{ {
TimerId = 0;
TriggerTime = 0f;
Interval = 0f;
Handler = null; Handler = null;
HandlerNoArgs = null; HandlerNoArgs = null;
HandlerGeneric = null; HandlerGeneric = null;
GenericInvoker = null;
IsLoop = false;
IsRunning = false;
IsUnscaled = false;
Args = null; Args = null;
GenericArg = null; GenericArg = null;
HandlerType = 0;
IsActive = false; IsActive = false;
Level = -1; Level = -1;
SlotIndex = -1; SlotIndex = -1;
@ -58,6 +79,8 @@ namespace AlicizaX
[Il2CppSetOption(Option.ArrayBoundsChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)]
internal sealed class TimerModule : ITimerModule internal sealed class TimerModule : ITimerModule
{ {
private const float TimeWheelSlotInterval = 0.001f;
private int _curTimerId; private int _curTimerId;
private HierarchicalTimeWheel _scaledTimeWheel; private HierarchicalTimeWheel _scaledTimeWheel;
private HierarchicalTimeWheel _unscaledTimeWheel; private HierarchicalTimeWheel _unscaledTimeWheel;
@ -76,10 +99,14 @@ namespace AlicizaX
private const int SLOT_COUNT_LEVEL0 = 256; private const int SLOT_COUNT_LEVEL0 = 256;
private const int SLOT_COUNT_LEVEL1 = 64; private const int SLOT_COUNT_LEVEL1 = 64;
private const int SLOT_COUNT_LEVEL2 = 64; private const int SLOT_COUNT_LEVEL2 = 64;
private const int SLOT_MASK_LEVEL0 = SLOT_COUNT_LEVEL0 - 1;
private const int SLOT_MASK_LEVEL1 = SLOT_COUNT_LEVEL1 - 1;
private const int SLOT_MASK_LEVEL2 = SLOT_COUNT_LEVEL2 - 1;
private readonly float _slotInterval; private readonly float _slotInterval;
private readonly TimeWheelLevel[] _levels; private readonly TimeWheelLevel[] _levels;
private float _currentTime; private float _currentTime;
private bool _isInitialized;
private class TimeWheelLevel private class TimeWheelLevel
{ {
@ -108,6 +135,8 @@ namespace AlicizaX
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTimer(int poolIndex, TimerInfo[] pool, float currentTime) public void AddTimer(int poolIndex, TimerInfo[] pool, float currentTime)
{ {
EnsureInitialized(currentTime);
ref TimerInfo timer = ref pool[poolIndex]; ref TimerInfo timer = ref pool[poolIndex];
if (!timer.IsActive) return; if (!timer.IsActive) return;
@ -121,19 +150,19 @@ namespace AlicizaX
if (totalSlots < SLOT_COUNT_LEVEL0) if (totalSlots < SLOT_COUNT_LEVEL0)
{ {
level = 0; level = 0;
slotIndex = (_levels[0].CurrentSlot + totalSlots) % SLOT_COUNT_LEVEL0; slotIndex = (_levels[0].CurrentSlot + totalSlots) & SLOT_MASK_LEVEL0;
} }
else if (totalSlots < SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1) else if (totalSlots < SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)
{ {
level = 1; level = 1;
int level1Slots = totalSlots / SLOT_COUNT_LEVEL0; int level1Slots = totalSlots / SLOT_COUNT_LEVEL0;
slotIndex = (_levels[1].CurrentSlot + level1Slots) % SLOT_COUNT_LEVEL1; slotIndex = (_levels[1].CurrentSlot + level1Slots) & SLOT_MASK_LEVEL1;
} }
else else
{ {
level = 2; level = 2;
int level2Slots = totalSlots / (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1); int level2Slots = totalSlots / (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1);
slotIndex = (_levels[2].CurrentSlot + level2Slots) % SLOT_COUNT_LEVEL2; slotIndex = (_levels[2].CurrentSlot + level2Slots) & SLOT_MASK_LEVEL2;
} }
timer.Level = level; timer.Level = level;
@ -152,6 +181,8 @@ namespace AlicizaX
public void Advance(float currentTime, TimerInfo[] pool, Action<int> processTimer) public void Advance(float currentTime, TimerInfo[] pool, Action<int> processTimer)
{ {
EnsureInitialized(currentTime);
float timeDelta = currentTime - _currentTime; float timeDelta = currentTime - _currentTime;
if (timeDelta <= 0) return; if (timeDelta <= 0) return;
@ -162,23 +193,45 @@ namespace AlicizaX
_currentTime += _slotInterval; _currentTime += _slotInterval;
ProcessLevel(0, pool, processTimer); ProcessLevel(0, pool, processTimer);
if ((_levels[0].CurrentSlot % SLOT_COUNT_LEVEL0) == 0) if (_levels[0].CurrentSlot == 0)
{ {
ProcessLevel(1, pool, processTimer); ProcessLevel(1, pool, processTimer);
} if (_levels[1].CurrentSlot == 0)
if ((_levels[0].CurrentSlot % (SLOT_COUNT_LEVEL0 * SLOT_COUNT_LEVEL1)) == 0)
{ {
ProcessLevel(2, pool, processTimer); ProcessLevel(2, pool, processTimer);
} }
} }
} }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureInitialized(float currentTime)
{
if (_isInitialized)
{
return;
}
_currentTime = currentTime;
_isInitialized = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessLevel(int level, TimerInfo[] pool, Action<int> processTimer) private void ProcessLevel(int level, TimerInfo[] pool, Action<int> processTimer)
{ {
TimeWheelLevel wheelLevel = _levels[level]; TimeWheelLevel wheelLevel = _levels[level];
wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) % wheelLevel.SlotCount; if (level == 0)
{
wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) & SLOT_MASK_LEVEL0;
}
else if (level == 1)
{
wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) & SLOT_MASK_LEVEL1;
}
else
{
wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) & SLOT_MASK_LEVEL2;
}
int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot]; int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot];
wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1; wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1;
@ -263,6 +316,8 @@ namespace AlicizaX
_pendingRemoveTimers = new int[64]; _pendingRemoveTimers = new int[64];
_pendingRemoveCount = 0; _pendingRemoveCount = 0;
InitializeTimerIdMapping(_timerIdToPoolIndex, 0);
for (int i = 0; i < _timerPoolCapacity; i++) for (int i = 0; i < _timerPoolCapacity; i++)
{ {
_freeIndices[i] = i; _freeIndices[i] = i;
@ -272,8 +327,8 @@ namespace AlicizaX
_timerPool[i].SlotIndex = -1; _timerPool[i].SlotIndex = -1;
} }
_scaledTimeWheel = new HierarchicalTimeWheel(0.001f); _scaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval);
_unscaledTimeWheel = new HierarchicalTimeWheel(0.001f); _unscaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -285,7 +340,10 @@ namespace AlicizaX
timer.TimerId = ++_curTimerId; timer.TimerId = ++_curTimerId;
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
timer.Interval = isLoop ? time : 0f; timer.Interval = isLoop ? time : 0f;
timer.Handler = null;
timer.HandlerNoArgs = callback; timer.HandlerNoArgs = callback;
timer.HandlerGeneric = null;
timer.GenericInvoker = null;
timer.HandlerType = 1; timer.HandlerType = 1;
timer.IsLoop = isLoop; timer.IsLoop = isLoop;
timer.IsRunning = true; timer.IsRunning = true;
@ -311,7 +369,10 @@ namespace AlicizaX
timer.TimerId = ++_curTimerId; timer.TimerId = ++_curTimerId;
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
timer.Interval = isLoop ? time : 0f; timer.Interval = isLoop ? time : 0f;
timer.Handler = null;
timer.HandlerNoArgs = null;
timer.HandlerGeneric = callback; timer.HandlerGeneric = callback;
timer.GenericInvoker = TimerGenericInvokerCache<T>.Invoke;
timer.GenericArg = arg; timer.GenericArg = arg;
timer.HandlerType = 2; timer.HandlerType = 2;
timer.IsLoop = isLoop; timer.IsLoop = isLoop;
@ -337,6 +398,9 @@ namespace AlicizaX
timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time;
timer.Interval = isLoop ? time : 0f; timer.Interval = isLoop ? time : 0f;
timer.Handler = callback; timer.Handler = callback;
timer.HandlerNoArgs = null;
timer.HandlerGeneric = null;
timer.GenericInvoker = null;
timer.HandlerType = 0; timer.HandlerType = 0;
timer.IsLoop = isLoop; timer.IsLoop = isLoop;
timer.IsRunning = true; timer.IsRunning = true;
@ -383,7 +447,15 @@ namespace AlicizaX
{ {
if (timerId >= _timerIdToPoolIndex.Length) if (timerId >= _timerIdToPoolIndex.Length)
{ {
Array.Resize(ref _timerIdToPoolIndex, _timerIdToPoolIndex.Length * 2); int oldLength = _timerIdToPoolIndex.Length;
int newLength = oldLength;
while (timerId >= newLength)
{
newLength *= 2;
}
Array.Resize(ref _timerIdToPoolIndex, newLength);
InitializeTimerIdMapping(_timerIdToPoolIndex, oldLength);
} }
_timerIdToPoolIndex[timerId] = poolIndex; _timerIdToPoolIndex[timerId] = poolIndex;
@ -392,7 +464,28 @@ namespace AlicizaX
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetPoolIndex(int timerId) private int GetPoolIndex(int timerId)
{ {
return timerId > 0 && timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1; if (timerId <= 0 || timerId >= _timerIdToPoolIndex.Length)
{
return -1;
}
int poolIndex = _timerIdToPoolIndex[timerId];
if ((uint)poolIndex >= (uint)_timerPool.Length)
{
return -1;
}
ref TimerInfo timer = ref _timerPool[poolIndex];
return timer.IsActive && timer.TimerId == timerId ? poolIndex : -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InitializeTimerIdMapping(int[] mapping, int startIndex)
{
for (int i = startIndex; i < mapping.Length; i++)
{
mapping[i] = -1;
}
} }
public void Stop(int timerId) public void Stop(int timerId)
@ -465,10 +558,20 @@ namespace AlicizaX
{ {
if (_timerPool[i].IsActive) if (_timerPool[i].IsActive)
{ {
int timerId = _timerPool[i].TimerId;
if (timerId > 0 && timerId < _timerIdToPoolIndex.Length)
{
_timerIdToPoolIndex[timerId] = -1;
}
_timerPool[i].Clear(); _timerPool[i].Clear();
_freeIndices[_freeCount++] = i; _freeIndices[_freeCount++] = i;
} }
} }
_pendingRemoveCount = 0;
_scaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval);
_unscaledTimeWheel = new HierarchicalTimeWheel(TimeWheelSlotInterval);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -476,6 +579,12 @@ namespace AlicizaX
{ {
if (poolIndex >= 0 && poolIndex < _timerPool.Length) if (poolIndex >= 0 && poolIndex < _timerPool.Length)
{ {
int timerId = _timerPool[poolIndex].TimerId;
if (timerId > 0 && timerId < _timerIdToPoolIndex.Length && _timerIdToPoolIndex[timerId] == poolIndex)
{
_timerIdToPoolIndex[timerId] = -1;
}
_timerPool[poolIndex].Clear(); _timerPool[poolIndex].Clear();
if (_freeCount >= _freeIndices.Length) if (_freeCount >= _freeIndices.Length)
@ -516,7 +625,7 @@ namespace AlicizaX
timer.HandlerNoArgs?.Invoke(); timer.HandlerNoArgs?.Invoke();
break; break;
case 2: // Generic case 2: // Generic
((dynamic)timer.HandlerGeneric)?.Invoke((dynamic)timer.GenericArg); timer.GenericInvoker?.Invoke(timer.HandlerGeneric, timer.GenericArg);
break; break;
} }
} }

View File

@ -49,7 +49,7 @@ namespace AlicizaX.UI.Runtime
RecoverLayerOptionAll(); RecoverLayerOptionAll();
} }
private void OnBlockCountDown(object[] args) private void OnBlockCountDown()
{ {
RecoverLayerOptionAll(); RecoverLayerOptionAll();
} }

View File

@ -42,10 +42,10 @@ namespace AlicizaX.UI.Runtime
{ {
timerId = _timerModule.AddTimer( timerId = _timerModule.AddTimer(
OnTimerDisposeWindow, OnTimerDisposeWindow,
uiMetadata,
uiMetadata.MetaInfo.CacheTime, uiMetadata.MetaInfo.CacheTime,
isLoop: false, isLoop: false,
isUnscaled: true, isUnscaled: true
uiMetadata
); );
if (timerId <= 0) if (timerId <= 0)
@ -58,11 +58,8 @@ namespace AlicizaX.UI.Runtime
m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, new CacheEntry(uiMetadata, timerId)); m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, new CacheEntry(uiMetadata, timerId));
} }
private void OnTimerDisposeWindow(object[] args) private void OnTimerDisposeWindow(UIMetadata meta)
{ {
if (args == null || args.Length == 0) return;
UIMetadata meta = args[0] as UIMetadata;
if (meta != null) if (meta != null)
{ {
meta.Dispose(); meta.Dispose();