Skip to content

Commit 5066af1

Browse files
j15zclaude
andcommitted
refactor(copilot): share the scaling-ratio harness between the two perf tests
Both complexity tests hand-rolled the same 15-line harness — build a repeated tag mention, take the fastest of five runs at two input sizes, assert the ratio — differing only in which function they timed. Changing the run count, the sample sizes, or the threshold meant editing both in lockstep. Extracted to `scalingRatioOver4x`, following the existing `*-test-helpers.ts` convention in this tree. The rationale for asserting a ratio rather than a wall-clock ceiling now lives in one place instead of being paraphrased twice. Also drops a redundant disjunct in the opener scan: `nearestStart` and `nearestTagName` are only ever assigned together, so `nearestStart === -1` holds exactly when the name is empty. Testing the name alone is the same check and is the one that narrows the union for `resolveTagAt`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 1943cfe commit 5066af1

4 files changed

Lines changed: 51 additions & 41 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.test.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @vitest-environment node
33
*/
44
import { describe, expect, it } from 'vitest'
5+
import { scalingRatioOver4x } from '@/app/workspace/[workspaceId]/home/components/message-content/components/scaling-test-helpers'
56
import { sanitizeChatDisplayContent } from './chat-sanitize'
67

78
describe('sanitizeChatDisplayContent', () => {
@@ -91,27 +92,9 @@ describe('sanitizeChatDisplayContent', () => {
9192
// is quadratic — 154ms for this input before the bound, on the main thread,
9293
// for every streamed chunk.
9394
//
94-
// Asserted as a RATIO across a 4x input rather than a wall-clock ceiling. A
95-
// fixed millisecond bound measures the machine as much as the algorithm: it
96-
// fails on a loaded CI box, and set generously enough not to, it lets a
97-
// genuine quadratic through at the single size it happens to sample.
98-
// Quadratic costs ~16x for 4x the input; linear costs ~4x.
99-
const build = (times: number) => 'The <workspace_resource> tag is used here. '.repeat(times)
100-
const fastest = (content: string) => {
101-
let best = Number.POSITIVE_INFINITY
102-
for (let run = 0; run < 5; run++) {
103-
const startedAt = performance.now()
104-
sanitizeChatDisplayContent(content)
105-
best = Math.min(best, performance.now() - startedAt)
106-
}
107-
return best
108-
}
109-
110-
fastest(build(2_000))
111-
const small = fastest(build(2_000))
112-
const large = fastest(build(8_000))
113-
114-
expect(large / small).toBeLessThan(8)
95+
// Asserted as a scaling ratio, not a wall-clock ceiling — see
96+
// {@link scalingRatioOver4x} for why.
97+
expect(scalingRatioOver4x((content) => sanitizeChatDisplayContent(content))).toBeLessThan(8)
11598
})
11699

117100
it('still unwraps a real tag that carries a stray backtick on one side only', () => {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Repeated mention of a tag name that never closes — the shape both the parser
3+
* and the display sanitizer used to be quadratic on, because a scan allowed to
4+
* cross an opener restarts from every opener.
5+
*/
6+
function buildRepeatedTagMentions(times: number): string {
7+
return 'The <workspace_resource> tag is used here. '.repeat(times)
8+
}
9+
10+
/** Fastest of five runs, so a single scheduling hiccup cannot skew the sample. */
11+
function fastest(run: (content: string) => void, content: string): number {
12+
let best = Number.POSITIVE_INFINITY
13+
for (let attempt = 0; attempt < 5; attempt++) {
14+
const startedAt = performance.now()
15+
run(content)
16+
best = Math.min(best, performance.now() - startedAt)
17+
}
18+
return best
19+
}
20+
21+
/**
22+
* How much slower `run` gets when its input grows 4x.
23+
*
24+
* Complexity is asserted as a RATIO rather than a wall-clock ceiling. A fixed
25+
* millisecond bound measures the machine as much as the algorithm: it fails on a
26+
* loaded CI box, and set generously enough not to, it lets a genuine quadratic
27+
* through at the single size it happens to sample. Quadratic costs ~16x for 4x
28+
* the input; linear costs ~4x.
29+
*/
30+
export function scalingRatioOver4x(run: (content: string) => void): number {
31+
// Warm up first — the JIT would otherwise charge the whole compile to the
32+
// small sample and flatter the ratio.
33+
fastest(run, buildRepeatedTagMentions(2_000))
34+
35+
const small = fastest(run, buildRepeatedTagMentions(2_000))
36+
const large = fastest(run, buildRepeatedTagMentions(8_000))
37+
38+
return large / small
39+
}

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ vi.mock('@/lib/auth/auth-client', () => ({
1414
useSession: vi.fn(() => ({ data: null, isPending: false })),
1515
}))
1616

17+
import { scalingRatioOver4x } from '@/app/workspace/[workspaceId]/home/components/message-content/components/scaling-test-helpers'
1718
import type {
1819
ContentSegment,
1920
IndexOfCache,
@@ -612,25 +613,9 @@ describe('parseSpecialTags with <question>', () => {
612613
// or `[` — the common case. Testing that before blanking avoids copying a
613614
// full window per opener per chunk: 43ms to 2ms on this input.
614615
//
615-
// Asserted as a RATIO across a 4x input rather than a wall-clock ceiling, so
616-
// the test pins the complexity instead of the speed of the machine running
617-
// it. Quadratic costs ~16x for 4x the input; linear costs ~4x.
618-
const build = (times: number) => 'The <workspace_resource> tag is used here. '.repeat(times)
619-
const fastest = (content: string) => {
620-
let best = Number.POSITIVE_INFINITY
621-
for (let run = 0; run < 5; run++) {
622-
const startedAt = performance.now()
623-
parseSpecialTags(content, true)
624-
best = Math.min(best, performance.now() - startedAt)
625-
}
626-
return best
627-
}
628-
629-
fastest(build(2_000))
630-
const small = fastest(build(2_000))
631-
const large = fastest(build(8_000))
632-
633-
expect(large / small).toBeLessThan(8)
616+
// Asserted as a scaling ratio, not a wall-clock ceiling — see
617+
// {@link scalingRatioOver4x} for why.
618+
expect(scalingRatioOver4x((content) => parseSpecialTags(content, true))).toBeLessThan(8)
634619
})
635620

636621
it('does not let a late thinking close swallow content already on screen', () => {

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,10 @@ export function parseSpecialTags(content: string, isStreaming: boolean): ParsedS
10421042
}
10431043
}
10441044

1045-
if (nearestStart === -1 || nearestTagName === '') {
1045+
// Only the name is tested: the two are assigned together above, so an empty
1046+
// name and a -1 start are the same state — and the name is the one that
1047+
// needs narrowing before resolveTagAt below.
1048+
if (nearestTagName === '') {
10461049
let remaining = content.slice(cursor)
10471050

10481051
if (isStreaming) {

0 commit comments

Comments
 (0)