diff --git a/.jules/bolt.md b/.jules/bolt.md index 38d4b732..d9841ed7 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -41,3 +41,7 @@ ## 2025-02-15 - Replace Array.from(map.values()).map with a for...of loop **Learning:** Using `Array.from(map.values()).map(...)` creates an unnecessary intermediate array which wastes memory allocation and garbage collection time, particularly for frequently re-rendered components handling large collections. **Action:** Use a `for...of` loop over `map.values()` to iterate and push mapped elements directly into the final array for O(1) memory and avoiding intermediate array allocations. + +## 2023-07-05 - Avoid intermediate array iteration and apply early breaks for minimum search +**Learning:** `song?.sections.reduce(...)` causes unconditional O(N) operations and `Array.from({ length: X }).map(...)` creates intermediate arrays of `undefined` values, reducing React rendering performance. +**Action:** Replace `reduce` with a `for...of` loop with an early `break` when searching for absolute bounds (like a low confidence level), and use the built-in mapping argument `Array.from({ length: X }, ...)` to avoid allocating intermediate arrays. diff --git a/CHANGELOG.md b/CHANGELOG.md index 071a641d..65f8a866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [0.1.4] - 2023-07-05 +### 성능 (Performance) +- React 컴포넌트(`App.tsx`, `Workspace.tsx`)에서 `Array.from` 매핑 시 중간 배열이 생성되지 않도록 built-in 인자를 활용하여 최적화 +- `ConfidenceMetric` 컴포넌트 내 `reduce`를 이용한 최솟값 탐색 시 `low` confidence를 찾으면 바로 조기 종료(early break)하도록 개선하여 O(N) 연산을 최적화 + # Changelog ## [Unreleased] diff --git a/apps/desktop/src/App.test.tsx b/apps/desktop/src/App.test.tsx index c039dfba..6dec742d 100644 --- a/apps/desktop/src/App.test.tsx +++ b/apps/desktop/src/App.test.tsx @@ -293,6 +293,33 @@ describe("App", () => { expect(screen.getAllByText(/2 sections/i).length).toBeGreaterThan(0); }); + it("short-circuits confidence summarization when encountering a low-confidence section", async () => { + const loadedProject = succeededResult().result; + loadedProject.sections.push( + { + ...loadedProject.sections[0], + id: "verse-2", + label: "verse", + confidence: { level: "low", source: "model", notes: "Hard to hear." } + }, + { + ...loadedProject.sections[0], + id: "chorus-2", + label: "chorus", + confidence: { level: "high", source: "model", notes: "Very clear." } + } + ); + mockLoadProject.mockResolvedValueOnce(loadedProject); + render(); + + fireEvent.click(screen.getByRole("button", { name: /open project/i })); + + await waitFor(() => { + expect(screen.getByText(/^Low$/i)).toBeTruthy(); + }); + expect(screen.getAllByText(/3 sections/i).length).toBeGreaterThan(0); + }); + it("selects a local audio source and starts a local-audio analysis job", async () => { tauriInvoke .mockResolvedValueOnce(bootstrapResponse()) diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 24f5fb09..b47c9bdf 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -180,15 +180,18 @@ function MetricCard({ function ConfidenceMetric({ song }: { song: RehearsalSong | null }) { const sectionCount = song?.sections.length ?? 0; const confidenceOrder = { high: 3, medium: 2, low: 1 } as const; - const lowestConfidence = song?.sections.reduce( - (current, section) => { - if (!current || confidenceOrder[section.confidence.level] < confidenceOrder[current]) { - return section.confidence.level; + let lowestConfidence: RehearsalSong["sections"][number]["confidence"]["level"] | null = null; + if (song && song.sections.length > 0) { + lowestConfidence = "high"; + for (const section of song.sections) { + if (confidenceOrder[section.confidence.level] < confidenceOrder[lowestConfidence]) { + lowestConfidence = section.confidence.level; + if (lowestConfidence === "low") { + break; // O(1) early exit when finding lowest possible confidence + } } - return current; - }, - null - ); + } + } const confidence = lowestConfidence ? `${lowestConfidence[0].toUpperCase()}${lowestConfidence.slice(1)}` : "Ready"; const detail = sectionCount > 0 ? `${sectionCount} section${sectionCount === 1 ? "" : "s"}` : "Local analysis"; @@ -523,7 +526,7 @@ export function App() {