From 3ce20b8f72f921467be4dcf830cb4cb4c204c1f2 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 15 Jul 2026 04:05:22 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Avoid=20intermediate=20?= =?UTF-8?q?array=20allocations=20in=20loops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 3 +++ .../src/features/workspace/GrooveMap.tsx | 9 ++++++++- .../src/features/workspace/SectionRoadmap.tsx | 20 +++++++++++-------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index d54cf10f..7892f661 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -61,3 +61,6 @@ ## 2026-07-13 - Array.from mapping optimization **Learning:** Using `Array.from({ length: N }).map(...)` creates an intermediate array of `undefined` values which requires memory allocation and garbage collection, adding O(N) unnecessary overhead in frequently re-rendered UI components. **Action:** Use `Array.from({ length: N }, (_, index) => ...)` to map elements directly during array creation, avoiding intermediate allocations. +## 2023-10-27 - [Filter and Map chaining overhead] +**Learning:** [Chaining `.filter().map()` causes an intermediate array allocation for the result of the filter, which adds memory and garbage collection overhead, especially in frequent renders or large collections.] +**Action:** [Use `.reduce()` to combine both operations in a single loop, pushing into an accumulator array.] diff --git a/apps/desktop/src/features/workspace/GrooveMap.tsx b/apps/desktop/src/features/workspace/GrooveMap.tsx index 2745d4d7..810efc88 100644 --- a/apps/desktop/src/features/workspace/GrooveMap.tsx +++ b/apps/desktop/src/features/workspace/GrooveMap.tsx @@ -16,8 +16,15 @@ function GrooveMapComponent({ notes, isLoading }: GrooveMapProps) { const renderedNotes = notes ?? EMPTY_NOTES; // Find max offset to determine timeline width + // Performance: Avoid O(N) array overhead from .reduce() on large note arrays. const maxTime = useMemo(() => { - return renderedNotes.reduce((max, n) => Math.max(max, n.offset), 10); + let max = 10; + for (let i = 0; i < renderedNotes.length; i++) { + if (renderedNotes[i]!.offset > max) { + max = renderedNotes[i]!.offset; + } + } + return max; }, [renderedNotes]); // Unique pitches to determine vertical lanes (avoiding 88-key piano roll) diff --git a/apps/desktop/src/features/workspace/SectionRoadmap.tsx b/apps/desktop/src/features/workspace/SectionRoadmap.tsx index 6f27c250..ebd25954 100644 --- a/apps/desktop/src/features/workspace/SectionRoadmap.tsx +++ b/apps/desktop/src/features/workspace/SectionRoadmap.tsx @@ -122,13 +122,14 @@ export function SectionRoadmap({ song, activeRole, onSongUpdate }: SectionRoadma - {section.roles - .filter(role => !activeRole || role.id === activeRole) - .map(role => ( -
+ {// Performance: Avoid intermediate array allocations by combining filter and map in a single loop via reduce + section.roles.reduce((acc, role) => { + if (!activeRole || role.id === activeRole) { + acc.push( +
@@ -209,7 +210,10 @@ export function SectionRoadmap({ song, activeRole, onSongUpdate }: SectionRoadma
- ))} + ); + } + return acc; + }, [] as React.ReactElement[])} ))} From aee62dbbd7702ff81e5f00ec82ff66ec4532107a Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 15 Jul 2026 04:47:41 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Avoid=20intermediate=20?= =?UTF-8?q?array=20allocations=20in=20loops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit