From ac8f70a16d38fa490f6eb806660705da00b56125 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:05:30 +0000 Subject: [PATCH 1/2] =?UTF-8?q?perf:=20O(N=20log=20N)=20Date.parse()=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=9D=84=20Map-Sort-Map=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=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 | 6 ++++++ .../components/dashboard/session-timeline-chart.tsx | 11 +++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 57daf471..12a0bb7d 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-06 - Schwartzian transform for expensive sort comparators + +**Learning:** When sorting large arrays based on an expensive data transformation like `Date.parse()` on ISO strings, placing the transformation directly inside the `Array.prototype.sort()` comparator causes it to execute O(N log N) times. A Schwartzian transform (map-sort-map) pre-computes the values in O(N) time. + +**Action:** Wrap items in an object `{ original: item, parsedValue: expensiveOperation(item) }` prior to sorting when the comparator relies on computationally non-trivial operations. diff --git a/packages/web/src/components/dashboard/session-timeline-chart.tsx b/packages/web/src/components/dashboard/session-timeline-chart.tsx index 222d0b22..6e66ee21 100644 --- a/packages/web/src/components/dashboard/session-timeline-chart.tsx +++ b/packages/web/src/components/dashboard/session-timeline-chart.tsx @@ -67,9 +67,10 @@ function buildChartData( toolCalls: ToolCallPoint[], sessionStartedAt: string ): ChartDataItem[] { - const sortedUsage = [...usageTimeline].sort( - (a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp) - ) + const sortedUsage = usageTimeline + .map((item) => ({ original: item, parsedValue: Date.parse(item.timestamp) })) + .sort((a, b) => a.parsedValue - b.parsedValue) + const sortedTools = [...toolCalls].sort( (a, b) => a.parsedTimestamp - b.parsedTimestamp ) @@ -77,9 +78,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, parsedValue: currentTimestamp }) => { while ( toolIndex < sortedTools.length && sortedTools[toolIndex]!.parsedTimestamp <= currentTimestamp From 1e1100f2224a94699fd66f94be573fc516db44cf Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:44:38 +0000 Subject: [PATCH 2/2] =?UTF-8?q?perf:=20O(N=20log=20N)=20Date.parse()=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=9D=84=20Map-Sort-Map=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit