Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions packages/web/src/lib/server/daily-rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,16 +438,22 @@ export async function getDailyRollupsForProjects(
const userSet = userSetsByDate.get(r.date)!
for (const u of r.activeUserIds) userSet.add(u)

// [Bolt: Performance Optimization] Use Object.keys() instead of Object.entries() in hot paths.
// Impact: Avoids array allocation for each key-value pair, significantly reducing GC overhead when aggregating large daily rollups.
for (const k of Object.keys(r.skillCounts)) {
prev.skillCounts[k] = (prev.skillCounts[k] ?? 0) + r.skillCounts[k]!
// [Bolt: Performance Optimization] Use for...in guarded by Object.hasOwn() instead of Object.keys() in hot paths.
// Impact: Completely avoids array allocation for keys, eliminating GC overhead when aggregating large daily rollups.
for (const k in r.skillCounts) {
if (Object.hasOwn(r.skillCounts, k)) {
prev.skillCounts[k] = (prev.skillCounts[k] ?? 0) + r.skillCounts[k]!
}
}
for (const k of Object.keys(r.agentCounts)) {
prev.agentCounts[k] = (prev.agentCounts[k] ?? 0) + r.agentCounts[k]!
for (const k in r.agentCounts) {
if (Object.hasOwn(r.agentCounts, k)) {
prev.agentCounts[k] = (prev.agentCounts[k] ?? 0) + r.agentCounts[k]!
}
}
for (const k of Object.keys(r.modelTokens)) {
prev.modelTokens[k] = (prev.modelTokens[k] ?? 0) + r.modelTokens[k]!
for (const k in r.modelTokens) {
if (Object.hasOwn(r.modelTokens, k)) {
prev.modelTokens[k] = (prev.modelTokens[k] ?? 0) + r.modelTokens[k]!
}
}

// userStats: userId 기준 sum (지연된 Map 변환)
Expand Down Expand Up @@ -622,10 +628,16 @@ export function aggregateSummary(
totals.cacheCreationTokens += r.cacheCreationTokens
totals.estimatedCostUsd += r.estimatedCostUsd
for (const u of r.activeUserIds) activeUsers.add(u)
// [Bolt: Performance Optimization] Object.keys() iterations avoid internal array tuples, reducing heap thrashing
for (const k of Object.keys(r.skillCounts)) skillCounts[k] = (skillCounts[k] ?? 0) + r.skillCounts[k]!
for (const k of Object.keys(r.agentCounts)) agentCounts[k] = (agentCounts[k] ?? 0) + r.agentCounts[k]!
for (const k of Object.keys(r.modelTokens)) modelTokens[k] = (modelTokens[k] ?? 0) + r.modelTokens[k]!
// [Bolt: Performance Optimization] for...in loops guarded by Object.hasOwn() avoid array allocation completely, eliminating GC overhead in hot paths
for (const k in r.skillCounts) {
if (Object.hasOwn(r.skillCounts, k)) skillCounts[k] = (skillCounts[k] ?? 0) + r.skillCounts[k]!
}
for (const k in r.agentCounts) {
if (Object.hasOwn(r.agentCounts, k)) agentCounts[k] = (agentCounts[k] ?? 0) + r.agentCounts[k]!
}
for (const k in r.modelTokens) {
if (Object.hasOwn(r.modelTokens, k)) modelTokens[k] = (modelTokens[k] ?? 0) + r.modelTokens[k]!
}
}

// Deterministic tie-break: callCount DESC, skillName ASC (codepoint binary —
Expand Down
12 changes: 8 additions & 4 deletions packages/web/src/lib/server/weekly-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,19 +394,23 @@ export async function getWeeklyReport(
// Insights — delegation
// ⚡ Bolt Optimization:
// 병목 지점: 기존 코드는 `thisWeekRollups`를 3번 순회하고, 매 순회마다 Object.values()로 중간 배열을 생성하여 메모리 할당 비용이 발생했습니다.
// 최적화 방법: 단일 for...of 루프와 Object.keys() 순회를 결합하여 N+1 순회를 1회 순회로 통합하고 중간 배열 할당을 제거했습니다.
// 기대 효과: `thisWeekRollups`의 크기가 클 경우, 불필요한 배열 생성 오버헤드와 O(N) 순회를 1/3로 줄여 리포트 생성 성능이 향상됩니다.
// 최적화 방법: 단일 for...of 루프와 Object.hasOwn()으로 보호된 for...in 루프를 결합하여 N+1 순회를 1회 순회로 통합하고 중간 배열 할당을 완전히 제거했습니다.
// 기대 효과: `thisWeekRollups`의 크기가 클 경우, 불필요한 배열 생성(GC) 오버헤드를 없애고 O(N) 순회를 1/3로 줄여 리포트 생성 성능이 극대화됩니다.
let totalAgentCalls = 0
let totalSkillCalls = 0
const distinctSkillsThisWeek = new Set<string>()

for (const r of thisWeekRollups) {
for (const k of Object.keys(r.agentCounts)) {
for (const k in r.agentCounts) {
if (Object.hasOwn(r.agentCounts, k)) {
totalAgentCalls += r.agentCounts[k]
}
}
for (const k of Object.keys(r.skillCounts)) {
for (const k in r.skillCounts) {
if (Object.hasOwn(r.skillCounts, k)) {
totalSkillCalls += r.skillCounts[k]
distinctSkillsThisWeek.add(k)
}
}
}

Expand Down
Loading