From 12f77c017fbb29b59d5d7663a68a2729d40061fb Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 21 Aug 2026 13:51:57 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20JS=20=EC=97=94=EC=A7=84=20?= =?UTF-8?q?=ED=95=A0=EB=8B=B9=20=EB=B0=8F=20GC=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=ED=97=A4=EB=93=9C=20=EC=A0=9C=EA=B1=B0=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=A3=A8=ED=94=84=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 3 +++ app.js | 25 ++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index b08b203a..580abcb5 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-21 - Optimize O(N) Metrics Calculation Loops +**Learning:** For high-performance O(N) loops in JavaScript, Array.prototype.reduce or forEach combined with Map caching introduces measurable overhead due to JS engine callback allocation, garbage collection, and hash-lookup costs. +**Action:** Replace functional array methods and Maps with standard for loops and typed arrays (e.g., Int32Array) in hot paths like computeTaskMetrics. diff --git a/app.js b/app.js index a04aae71..ccd75fc8 100644 --- a/app.js +++ b/app.js @@ -1370,21 +1370,28 @@ 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) => { + // ⚡ Bolt: Cache durationDays during total calculation to avoid recalculating for every task. + // Replaced Array.prototype.reduce and forEach with standard for loops to eliminate callback allocation and GC overhead. + const tasksLen = state.tasks.length; + const durationCache = new Int32Array(tasksLen); + let totalDays = 0; + + for (let i = 0; i < tasksLen; i++) { + const task = state.tasks[i]; const duration = calculateDurationDays(task.plannedStartDate, task.plannedEndDate); - durationCache.set(task.id, duration); - return sum + duration; - }, 0); + durationCache[i] = duration; + totalDays += duration; + } const baseDate = state.baseDate; + // Replaced Map with Map as the contract requires .get() to be called. const byTask = new Map(); let totalWeightedPlannedRatio = 0; let totalWeightedActualRatio = 0; - state.tasks.forEach((task) => { - const durationDays = durationCache.get(task.id); + for (let i = 0; i < tasksLen; i++) { + const task = state.tasks[i]; + const durationDays = durationCache[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 +1415,7 @@ function computeTaskMetrics() { plannedDateWarning, actualDateWarning }); - }); + } return { totalDays,