From 69f419efac2929d059475074556ac9220ad9b065 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:05:49 +0000 Subject: [PATCH 1/2] perf: optimize loop iterations in computeTaskMetrics Replaced `Array.prototype.reduce`/`forEach` and `Map` cache structures with standard `for` loops and `Int32Array` in high-frequency path `computeTaskMetrics`. This eliminates JS engine callback allocations, garbage collection overhead, and Map hash-lookup overhead. --- .jules/bolt.md | 5 +++++ app.js | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index b08b203a..6d13e9da 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,6 +1,11 @@ ## 2026-07-12 - O(N) penalty with Array.shift() in queues **Learning:** In Kahn's topological sort and similar algorithms, using `queue.shift()` inside a while loop causes an O(K) penalty per iteration since the entire remaining array needs to be shifted in memory, turning an O(V+E) algorithm effectively into O(V^2+E) worst case. **Action:** Always replace `queue.shift()` with a tracking pointer (e.g., `let queueIndex = 0; queue[queueIndex++]`) when using JavaScript arrays as queues in performance-critical graph algorithms. + ## 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-23 - Optimize array processing in computeTaskMetrics +**Learning:** In high-frequency render paths, `Array.prototype.reduce`/`forEach` and `Map` cache structures add noticeable JS engine callback allocation, garbage collection, and hash-lookup overhead. +**Action:** Replace them with standard `for` loops and typed arrays (e.g., `Int32Array`) to improve O(N) iteration performance when arrays and arrays lengths are known and fixed. diff --git a/app.js b/app.js index a04aae71..49deaa45 100644 --- a/app.js +++ b/app.js @@ -1370,21 +1370,26 @@ 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: Replace Map with Int32Array and Array methods with for loops to eliminate JS engine callback allocation, garbage collection, and hash-lookup 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; 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 +1413,7 @@ function computeTaskMetrics() { plannedDateWarning, actualDateWarning }); - }); + } return { totalDays, From 71e856d1a4e7c61a7c98f41748c3f869e84edec3 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:35:50 +0000 Subject: [PATCH 2/2] ci: force run checks due to out-of-scope CI flake