-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ Bolt: 연산 캐싱 제거 및 O(N) 반복문으로 성능 최적화 #606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,17 +1,14 @@ | ||||||||||||||||||||||||||
| ## 💡 What: | ||||||||||||||||||||||||||
| `app.js`에서 O(N)으로 동작하던 배열 검색(`findIndex`, `find`)을 O(1) 시간 복잡도를 가진 Map 캐시(`taskIdToIndexCache`) 조회로 최적화했습니다. O(1) 조회를 수행하기 위해 지연 초기화(lazy initialization)되는 캐시를 구축하고, `state.tasks` 배열의 구조적 변경(삽입, 삭제, 순서 변경 등)이 일어나는 모든 지점에서 캐시를 무효화하여(`invalidateTaskIndexCache()`) 데이터 무결성을 보장했습니다. | ||||||||||||||||||||||||||
| ## 💡 무엇을 | ||||||||||||||||||||||||||
| `app.js`의 `computeTaskMetrics` 함수에서 기존 `Map` 캐싱과 `Array.prototype.reduce`/`forEach` 메서드를 사용하던 로직을 `Float64Array`와 네이티브 `for` 반복문으로 교체했습니다. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## 🎯 Why: | ||||||||||||||||||||||||||
| 트리 구조의 특성 상, 자식 탐색이나 계층 구조 재조정을 위해 `getLastDescendantId`, `getTaskSubtreeRange` 등의 헬퍼 함수가 빈번하게 호출됩니다. 해당 함수들 내부에서 매번 `findIndex`를 사용하여 선형 탐색을 수행하면 태스크가 많아질수록 UI가 멈추거나 병목 현상이 발생할 수 있습니다. 이를 해결하여 대규모 데이터에서도 원활하고 빠른 성능을 유지하기 위함입니다. | ||||||||||||||||||||||||||
| ## 🎯 왜 | ||||||||||||||||||||||||||
| 반복적인 `reduce`와 `forEach` 호출은 JavaScript 엔진에서 매번 콜백 함수 할당 및 가비지 컬렉션을 발생시키고, `Map`을 이용한 잦은 키 해시 탐색은 오버헤드를 증가시킵니다. 작업 데이터가 많아지고 지표 연산이 빈번하게 수행될 때 발생하는 O(N) 병목을 제거하여 성능을 끌어올리기 위함입니다. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## 📊 Measured Improvement: | ||||||||||||||||||||||||||
| 약 10,000개의 태스크로 구성된 계층적 데이터를 임의 생성하여 Node.js 환경에서 성능 측정을 수행한 결과는 다음과 같습니다 (반복 10,000회 수행 기준): | ||||||||||||||||||||||||||
| ## 📊 영향 | ||||||||||||||||||||||||||
| - JS 엔진의 함수 콜백 스택 호출 및 잦은 해시 맵 조회 오버헤드가 제거되어 작업 개수에 비례하는 O(N) 렌더링 성능이 최적화되었습니다. | ||||||||||||||||||||||||||
| - 가비지 컬렉션(GC) 압력이 줄어들어 렌더링 스파이크 빈도가 줄어듭니다. | ||||||||||||||||||||||||||
|
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 성능 효과 설명을 실제 구현에 맞게 좁혀 주세요.
📝 제안 수정-반복적인 `reduce`와 `forEach` 호출은 JavaScript 엔진에서 매번 콜백 함수 할당 및 가비지 컬렉션을 발생시키고, `Map`을 이용한 잦은 키 해시 탐색은 오버헤드를 증가시킵니다. 작업 데이터가 많아지고 지표 연산이 빈번하게 수행될 때 발생하는 O(N) 병목을 제거하여 성능을 끌어올리기 위함입니다.
+반복적인 `reduce`와 `forEach` 호출 및 기간 캐시의 키 조회 비용을 줄입니다. 작업 데이터가 많고 지표 연산이 빈번할 때 O(N) 계산의 상수 비용을 낮추는 것이 목표입니다.
...
-- JS 엔진의 함수 콜백 스택 호출 및 잦은 해시 맵 조회 오버헤드가 제거되어 작업 개수에 비례하는 O(N) 렌더링 성능이 최적화되었습니다.
+- JS 엔진의 함수 콜백 호출과 기간 캐시의 키 조회 오버헤드를 줄여 O(N) 계산의 상수 비용을 낮춥니다.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| * **최적화 전 (Baseline):** | ||||||||||||||||||||||||||
| * `getLastDescendantId`: ~1189 ms 소요 | ||||||||||||||||||||||||||
| * `getTaskSubtreeRange`: ~1224 ms 소요 | ||||||||||||||||||||||||||
| * **최적화 후 (Optimized):** | ||||||||||||||||||||||||||
| * `getLastDescendantId`: ~5 ms 소요 | ||||||||||||||||||||||||||
| * `getTaskSubtreeRange`: ~5 ms 소요 | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| 캐시를 도입하여 배열 선형 탐색의 병목을 완벽히 해소하였으며, E2E 테스트(Playwright)를 통해 기능의 부수 효과(side effects)가 없음을 확인했습니다. | ||||||||||||||||||||||||||
| ## 🔬 측정 | ||||||||||||||||||||||||||
| 1. WBS에 많은 수의 작업(예: 1000개 이상)을 생성합니다. | ||||||||||||||||||||||||||
| 2. 각 행의 편집을 취소하거나 갱신하여 `renderAll` -> `computeTaskMetrics` 가 동기적으로 호출되게 합니다. | ||||||||||||||||||||||||||
| 3. Chrome DevTools의 Performance 탭에서 스크립팅 시간을 측정해보면 기존 배열 메서드와 Map 탐색에 할당되던 시간이 크게 감소된 것을 확인할 수 있습니다. | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Info: Index-based durations equivalent to id-keyed Map
The refactor replaces the
task.id-keyeddurationCacheMap with aFloat64Arrayindexed by position. Both loops walkstate.tasksby index in the same synchronous pass, sodurations[i]aligns with eachbyTask.set(task.id, ...). Behavior matches the old code and avoids overwrites on duplicate ids.durations[i]is always a number, socalculatePlannedProgressRatio'sdurationDays !== undefinedguard is unaffected.(Refers to this code)
Was this helpful? React with 👍 or 👎 to provide feedback.