From 405eee00dbb8971b00ab3899e36778e262a66027 Mon Sep 17 00:00:00 2001 From: Howchie <12265699+Howchie@users.noreply.github.com> Date: Sat, 21 Mar 2026 04:29:54 +0000 Subject: [PATCH] perf: replace chained array methods with single loops in coerce.ts --- .jules/bolt.md | 4 ++ packages/core/src/utils/coerce.ts | 91 ++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 31 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 2acf32f..c74b439 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -19,3 +19,7 @@ ## 2025-03-16 - Consolidating chained array iterations in mathematical routines **Learning:** Found a hot path in `packages/core/src/engines/parameterTransforms.ts` (`fitWaldAnalytic`) where an array of length N was traversed multiple times through `map`, `filter`, and multiple `reduce` calls to compute intermediate values (n, s1, sInv). Because this runs many times in a loop over ~1000 items, the overhead of creating intermediate arrays and performing 4 independent O(N) passes was significant (~630ms in benchmarks). Collapsing this into a single `for` loop eliminated all array allocations and reduced execution time by ~15-20x to ~35ms. **Action:** In high-frequency, performance-sensitive mathematical loops, avoid chaining `.map()`, `.filter()`, and `.reduce()`. Instead, use a standard `for` loop to accumulate multiple variables simultaneously in a single O(N) pass, completely eliminating intermediate array allocations. + +## 2024-05-19 - Avoid Chained Array Methods in Hot Paths +**Learning:** Chained array methods like `.map().filter().map()` are frequently used in config parsing utilities (`coerce.ts`). They generate multiple intermediate arrays which create garbage collection thrashing in hot paths (like sampling or scheduling). +**Action:** Replace `Array.prototype.map().filter()` with a single `for...of` loop and a standard `if` statement to construct arrays directly. diff --git a/packages/core/src/utils/coerce.ts b/packages/core/src/utils/coerce.ts index d6604c7..f548044 100644 --- a/packages/core/src/utils/coerce.ts +++ b/packages/core/src/utils/coerce.ts @@ -44,7 +44,14 @@ export function toFiniteNumber(value: unknown, fallback: number): number { } export function toNumberArray(value: unknown, fallback: number[]): number[] { - const out = asArray(value).map((entry) => Number(entry)).filter((entry) => Number.isFinite(entry)) as number[]; + // ⚡ Bolt: Replaced chained .map().filter() with single loop to avoid intermediate array allocations + const out: number[] = []; + for (const entry of asArray(value)) { + const num = Number(entry); + if (Number.isFinite(num)) { + out.push(num); + } + } return out.length > 0 ? out : fallback; } @@ -80,15 +87,26 @@ export function resolveBlockScreenSlotValue( } export function asStringArray(value: unknown, fallback: string[]): string[] { - const list = asArray(value).map((item) => asString(item)).filter((item): item is string => Boolean(item)); + // ⚡ Bolt: Replaced chained .map().filter() with single loop to avoid intermediate array allocations + const list: string[] = []; + for (const entry of asArray(value)) { + const str = asString(entry); + if (str) { + list.push(str); + } + } return list.length > 0 ? list : [...fallback]; } export function asPositiveNumberArray(value: unknown, fallback: number[]): number[] { - const list = asArray(value) - .map((entry) => Number(entry)) - .filter((entry) => Number.isFinite(entry) && entry > 0) - .map((entry) => Math.floor(entry)); + // ⚡ Bolt: Replaced chained .map().filter().map() with single loop to avoid intermediate array allocations + const list: number[] = []; + for (const entry of asArray(value)) { + const num = Number(entry); + if (Number.isFinite(num) && num > 0) { + list.push(Math.floor(num)); + } + } return list.length > 0 ? list : [...fallback]; } @@ -141,17 +159,28 @@ export function coerceInstructionInsertions(value: unknown): InstructionInsertio const pages = toInstructionScreenSpecs(raw.pages); if (pages.length === 0) continue; const whenRaw = asObject(raw.when); - const blockIndex = asArray(whenRaw?.blockIndex) - .map((item) => Number(item)) - .filter((item) => Number.isInteger(item)) - .map((item) => Math.floor(item)); - const blockLabel = asArray(whenRaw?.blockLabel) - .map((item) => asString(item)) - .filter((item): item is string => Boolean(item)); - const blockType = asArray(whenRaw?.blockType) - .map((item) => asString(item)) - .filter((item): item is string => Boolean(item)) - .map((item) => item.toLowerCase()); + // ⚡ Bolt: Replaced chained .map().filter() with single loops for blockIndex, blockLabel, and blockType to avoid intermediate array allocations + const blockIndex: number[] = []; + for (const item of asArray(whenRaw?.blockIndex)) { + const num = Number(item); + if (Number.isInteger(num)) { + blockIndex.push(Math.floor(num)); + } + } + const blockLabel: string[] = []; + for (const item of asArray(whenRaw?.blockLabel)) { + const str = asString(item); + if (str) { + blockLabel.push(str); + } + } + const blockType: string[] = []; + for (const item of asArray(whenRaw?.blockType)) { + const str = asString(item); + if (str) { + blockType.push(str.toLowerCase()); + } + } const isPractice = typeof whenRaw?.isPractice === "boolean" ? whenRaw.isPractice : undefined; const when: InstructionInsertionWhen | undefined = blockIndex.length > 0 || blockLabel.length > 0 || blockType.length > 0 || typeof isPractice === "boolean" @@ -237,20 +266,20 @@ export function toInstructionScreenSpecs(value: unknown): InstructionScreenSpec[ const title = asString(raw.title) ?? undefined; const html = asString(raw.html) ?? undefined; const text = asString(raw.text) ?? asString(raw.body) ?? asString(raw.content) ?? undefined; - const actions = asArray(raw.actions) - .map((entry): InstructionScreenAction | null => { - const actionRaw = asObject(entry); - if (!actionRaw) return null; - const label = asString(actionRaw.label); - if (!label) return null; - const action = (asString(actionRaw.action) ?? "continue").toLowerCase(); - return { - ...(asString(actionRaw.id) ? { id: asString(actionRaw.id) as string } : {}), - label, - action: action === "exit" ? "exit" : "continue", - }; - }) - .filter((entry): entry is InstructionScreenAction => Boolean(entry)); + // ⚡ Bolt: Replaced chained .map().filter() for actions with a single loop to avoid intermediate array allocations + const actions: InstructionScreenAction[] = []; + for (const entry of asArray(raw.actions)) { + const actionRaw = asObject(entry); + if (!actionRaw) continue; + const label = asString(actionRaw.label); + if (!label) continue; + const action = (asString(actionRaw.action) ?? "continue").toLowerCase(); + actions.push({ + ...(asString(actionRaw.id) ? { id: asString(actionRaw.id) as string } : {}), + label, + action: action === "exit" ? "exit" : "continue", + }); + } if (!html && !text) return null; return { ...(title ? { title } : {}),