From 2dfa7dc351d235e3f9b66b8c793ca21be30410f8 Mon Sep 17 00:00:00 2001
From: seonghobae <8172694+seonghobae@users.noreply.github.com>
Date: Thu, 9 Jul 2026 14:02:15 +0000
Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?=
=?UTF-8?q?=20ConfidenceMetric=20=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81?=
=?UTF-8?q?=ED=99=94=20(reduce=EB=A5=BC=20for...of=20=EB=A3=A8=ED=94=84?=
=?UTF-8?q?=EC=99=80=20early=20break=EB=A1=9C=20=EB=8C=80=EC=B2=B4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
무조건적으로 모든 요소를 순회하는 `Array.prototype.reduce()` 호출을 `for...of` 루프와 조기 종료(`break`) 조건으로 교체하여 성능을 향상시켰습니다. 가장 낮은 신뢰도 등급인 `'low'`에 도달하면 즉시 순회를 중지하여 O(N)의 최악의 경우를 O(K)로 최적화했습니다.
---
.jules/bolt.md | 4 ++++
apps/desktop/src/App.test.tsx | 25 +++++++++++++++++++++++++
apps/desktop/src/App.tsx | 22 ++++++++++++++--------
3 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/.jules/bolt.md b/.jules/bolt.md
index 38d4b732..06be7ce3 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.
+
+## 2025-02-16 - Array reduce Performance with Absolute Bounds
+**Learning:** Using unconditional `.reduce()` to find a minimum/maximum when an absolute bound (e.g. 'low' confidence) is known results in unnecessary iterations over the entire collection, hurting performance O(N).
+**Action:** Replace unconditional `.reduce()` calls with a `for...of` loop and early `break` when absolute limits exist, converting the operation to O(K) where K is the index of the first absolute bound found.
diff --git a/apps/desktop/src/App.test.tsx b/apps/desktop/src/App.test.tsx
index c039dfba..6909debe 100644
--- a/apps/desktop/src/App.test.tsx
+++ b/apps/desktop/src/App.test.tsx
@@ -293,6 +293,31 @@ describe("App", () => {
expect(screen.getAllByText(/2 sections/i).length).toBeGreaterThan(0);
});
+ it("short-circuits confidence summary calculation when encountering low confidence", async () => {
+ const loadedProject = succeededResult().result;
+ loadedProject.sections.push({
+ ...loadedProject.sections[0],
+ id: "low-confidence-section",
+ label: "bridge",
+ confidence: { level: "low", source: "model", notes: "Bridge is uncertain." }
+ });
+ loadedProject.sections.push({
+ ...loadedProject.sections[0],
+ id: "high-confidence-section",
+ label: "outro",
+ confidence: { level: "high", source: "model", notes: "Outro is 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..dbd64a30 100644
--- a/apps/desktop/src/App.tsx
+++ b/apps/desktop/src/App.tsx
@@ -180,15 +180,21 @@ 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;
+
+ // Performance Optimization: Replaced O(N) .reduce() with for...of loop and early return
+ // This allows short-circuiting when the absolute lowest confidence ('low') is found.
+ let lowestConfidence: RehearsalSong["sections"][number]["confidence"]["level"] | null = null;
+ if (song?.sections) {
+ for (const section of song.sections) {
+ if (!lowestConfidence || confidenceOrder[section.confidence.level] < confidenceOrder[lowestConfidence]) {
+ lowestConfidence = section.confidence.level;
}
- return current;
- },
- null
- );
+ if (lowestConfidence === "low") {
+ break; // Early exit since "low" is the absolute minimum possible value
+ }
+ }
+ }
+
const confidence = lowestConfidence ? `${lowestConfidence[0].toUpperCase()}${lowestConfidence.slice(1)}` : "Ready";
const detail = sectionCount > 0 ? `${sectionCount} section${sectionCount === 1 ? "" : "s"}` : "Local analysis";