perf: hoist update() inner functions out of the tick path

updateIfIdle and updateIfWaitingToRoll were declared inside update(),
allocating two closures on every 32ms tick. They are now module-level
functions receiving the state explicitly. Behaviour is unchanged, as
proven by the characterisation suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PVTnCfesGNGwPQJwJ9FwoD
This commit is contained in:
Claude
2026-07-04 09:57:06 +00:00
parent b10c38514f
commit 56257adcbd
+6 -4
View File
@@ -637,7 +637,7 @@ export function updateCore(state: RuntimeState): UpdateResult {
// 2. are we waiting to roll? // 2. are we waiting to roll?
if (state.timer.playback === Playback.Roll && state.timer.secondaryTimer !== null) { if (state.timer.playback === Playback.Roll && state.timer.secondaryTimer !== null) {
const clockHasCrossedMidnight = hasCrossedMidnight(previousClock, now); const clockHasCrossedMidnight = hasCrossedMidnight(previousClock, now);
return updateIfWaitingToRoll(clockHasCrossedMidnight); return updateIfWaitingToRoll(state, clockHasCrossedMidnight);
} }
// 3. at this point we know that we are playing an event // 3. at this point we know that we are playing an event
@@ -674,13 +674,16 @@ export function updateCore(state: RuntimeState): UpdateResult {
getExpectedTimesCore(state); getExpectedTimesCore(state);
return { hasTimerFinished: finishedNow, hasSecondaryTimerFinished: false }; return { hasTimerFinished: finishedNow, hasSecondaryTimerFinished: false };
}
function updateIfIdle() { // module level so the hot path does not allocate a closure on every tick
function updateIfIdle(): UpdateResult {
// if nothing is running, nothing to do // if nothing is running, nothing to do
return { hasTimerFinished: false, hasSecondaryTimerFinished: false }; return { hasTimerFinished: false, hasSecondaryTimerFinished: false };
} }
function updateIfWaitingToRoll(hasCrossedMidnight: boolean) { // module level so the hot path does not allocate a closure on every tick
function updateIfWaitingToRoll(state: RuntimeState, hasCrossedMidnight: boolean): UpdateResult {
// eslint-disable-next-line no-unused-labels -- dev code path // eslint-disable-next-line no-unused-labels -- dev code path
DEV: { DEV: {
if (state.eventNow === null || state._timer.secondaryTarget === null) { if (state.eventNow === null || state._timer.secondaryTarget === null) {
@@ -704,7 +707,6 @@ export function updateCore(state: RuntimeState): UpdateResult {
hasSecondaryTimerFinished: state.timer.secondaryTimer <= 0, hasSecondaryTimerFinished: state.timer.secondaryTimer <= 0,
}; };
} }
}
export function roll( export function roll(
rundown: Rundown, rundown: Rundown,