diff --git a/.jules/bolt.md b/.jules/bolt.md index 57daf471..150e9ca5 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -3,3 +3,9 @@ **Learning:** `Date.parse(value)` returns the timestamp primitive directly, while `new Date(value).getTime()` also constructs a `Date` object. Both use the same ECMAScript string-parsing semantics for these call sites. **Action:** In frequently executed paths that only need a timestamp primitive, prefer `Date.parse(value)`. Treat the allocation reduction as a bounded micro-optimization unless a committed benchmark establishes a larger runtime effect. + +## 2026-09-09 - Avoid O(N log N) `Date.parse` in arrays.sort + +**Learning:** Array sorting with complex transformations in the comparison function (like `Date.parse`) calls the transformation function O(N log N) times. Pre-computing the value transforms the work to O(N) mapping + O(N log N) sorting. + +**Action:** Use a Schwartzian transform (map-sort-map) for expensive sort keys. Do not spread (`...item`) original objects in the wrapper, which causes O(N) shallow copies; wrap them instead (`{ original: item, parsedValue }`). \ No newline at end of file diff --git a/packages/web/src/components/dashboard/session-timeline-chart.tsx b/packages/web/src/components/dashboard/session-timeline-chart.tsx index 222d0b22..38d7b329 100644 --- a/packages/web/src/components/dashboard/session-timeline-chart.tsx +++ b/packages/web/src/components/dashboard/session-timeline-chart.tsx @@ -67,9 +67,11 @@ function buildChartData( toolCalls: ToolCallPoint[], sessionStartedAt: string ): ChartDataItem[] { - const sortedUsage = [...usageTimeline].sort( - (a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp) - ) + // Schwartzian transform: pre-compute parsed timestamps to avoid O(N log N) Date.parse calls during sort + const sortedUsage = usageTimeline + .map((usage) => ({ original: usage, parsedTimestamp: Date.parse(usage.timestamp) })) + .sort((a, b) => a.parsedTimestamp - b.parsedTimestamp) + const sortedTools = [...toolCalls].sort( (a, b) => a.parsedTimestamp - b.parsedTimestamp ) @@ -77,8 +79,7 @@ function buildChartData( let toolIndex = 0 const cumulativeToolCounts = new Map() - return sortedUsage.map((usage) => { - const currentTimestamp = Date.parse(usage.timestamp) + return sortedUsage.map(({ original: usage, parsedTimestamp: currentTimestamp }) => { while ( toolIndex < sortedTools.length &&