Tighten the per-point loop in history line chart data

Profiling generateStateHistoryChartLineData on a large history put most
of the remaining time in the per-point path. Iterate the states with an
indexed loop instead of forEach, read the series array once, and track
the y range inline instead of through trackY, since the value is always
finite there. The output is unchanged.

These only help together. On their own, the hoisted array and the inline
min/max measured no change, and for...of loses most of the gain, so
@typescript-eslint/prefer-for-of is disabled for this one loop.

Cold first call in a fresh headless Brave, median of 7 runs, on 1.34
million states: 87.9 -> 77.2 ms on top of the previous commit (98.7 ms
on dev). At 100k states: 5.2 -> 4.1 ms.

Assisted-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Claudio Wunder committed 2026-10-07 17:49:45 +02:00
1 parent f5ce7a3355
commit 952022b917
1 file changed
+11 -5
@@ -378,12 +378,15 @@ export function generateStateHistoryChartLineData(
// pushData(timestamp, [value]) for one dataset, minus the per-point array
const latestValues: (number | null)[] = [null];
const seriesData = data[0].data!;
const pushValue = (timestamp: number, value: number) => {
if (timestamp > endTimeMs) {
return;
}
data[0].data!.push([timestamp, value]);
trackY(value);
seriesData.push([timestamp, value]);
// trackY, but value is always finite here
if (value < yMin) yMin = value;
if (value > yMax) yMax = value;
latestValues[0] = value;
prevValues = latestValues;
};
@@ -429,9 +432,12 @@ export function generateStateHistoryChartLineData(
processData(statistic);
}
}
states.states.forEach((entityState) => {
processData(entityState);
});
const stateList = states.states;
// An indexed loop is noticeably faster than for...of on large histories
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < stateList.length; i++) {
processData(stateList[i]);
}
if (lastNullDate !== null) {
pushData(lastNullDate, [null]);
}