From 2fa4ebb4ecdec3bec0a4dde86dbc53693efcd2e5 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:58:57 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20computeTaskMetrics=20?= =?UTF-8?q?=EB=B0=B0=EC=97=B4=20=EC=88=9C=ED=9A=8C=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JS 내장 배열 메서드(reduce, forEach)를 일반 for 루프로 교체하여 콜백 할당 오버헤드 제거 - Map 객체 대신 Int32Array를 사용하여 GC 오버헤드 및 해시 검색 비용 절감 --- .jules/bolt.md | 3 +++ app.js | 26 +++++++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index b08b203a..90894549 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -4,3 +4,6 @@ ## 2026-07-12 - Optimize renderTaskRow DOM allocations **Learning:** Caching unattached template nodes and instantiating them via `.cloneNode(false)` reduces DOM instantiation overhead in O(N) render loops significantly. **Action:** Apply this optimization to other hot-path rendering elements such as rows, cells, and stack containers. +## 2026-08-22 - Array reduce/forEach 및 Map 오버헤드 최적화 +**Learning:** computeTaskMetrics와 같이 자주 호출되는 O(N) 순회 로직에서 JS 내장 배열 메서드(reduce, forEach)와 Map을 사용하면 콜백 할당, GC, 해시 검색 오버헤드로 인해 성능 저하가 발생합니다. +**Action:** 성능이 중요한 반복문에서는 일반 for 루프와 TypedArray(Int32Array)를 사용하여 JS 엔진 오버헤드를 최소화합니다. diff --git a/app.js b/app.js index a04aae71..2d09cb24 100644 --- a/app.js +++ b/app.js @@ -1370,21 +1370,29 @@ function validateDateRange(startLabel, startValue, endLabel, endValue, errors) { } function computeTaskMetrics() { - // ⚡ Bolt: Cache durationDays during total calculation to avoid recalculating for every task - const durationCache = new Map(); - const totalDays = state.tasks.reduce((sum, task) => { + const length = state.tasks.length; + // ⚡ Bolt: Use Int32Array to avoid object allocation and GC overhead for numeric caching + const durations = new Int32Array(length); + let totalDays = 0; + + // ⚡ Bolt: Replace Array.reduce with a standard for loop to eliminate callback allocation overhead + for (let i = 0; i < length; i++) { + const task = state.tasks[i]; const duration = calculateDurationDays(task.plannedStartDate, task.plannedEndDate); - durationCache.set(task.id, duration); - return sum + duration; - }, 0); + durations[i] = duration; + totalDays += duration; + } const baseDate = state.baseDate; const byTask = new Map(); let totalWeightedPlannedRatio = 0; let totalWeightedActualRatio = 0; - state.tasks.forEach((task) => { - const durationDays = durationCache.get(task.id); + // ⚡ Bolt: Replace Array.forEach with a standard for loop to eliminate callback allocation overhead + for (let i = 0; i < length; i++) { + const task = state.tasks[i]; + // ⚡ Bolt: Avoid Map hash lookup for durations + const durationDays = durations[i]; const weightRatio = totalDays > 0 ? durationDays / totalDays : 0; const plannedProgressRatio = calculatePlannedProgressRatio(baseDate, task.plannedStartDate, task.plannedEndDate, durationDays); const actualProgressRatio = (ACTUAL_PROGRESS_MAP[task.actualProgressStatus] || 0) / 100; @@ -1408,7 +1416,7 @@ function computeTaskMetrics() { plannedDateWarning, actualDateWarning }); - }); + } return { totalDays,