Skip to content

[benchmarks] Add SO (stream overhead) scenario + collapse PR-comment smallprint#3077

Merged
VaguelySerious merged 1 commit into
mainfrom
peter/bench-stream-overhead
Jul 24, 2026
Merged

[benchmarks] Add SO (stream overhead) scenario + collapse PR-comment smallprint#3077
VaguelySerious merged 1 commit into
mainfrom
peter/bench-stream-overhead

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

What

Adds a new SO (Stream Overhead) benchmark scenario and metric, and hides the benchmark PR-comment smallprint behind a dropdown.

SO scenario

Models a haiku-size LLM streaming tokens: a writer emits 300 fake 4-byte chunks (~one token each) paced at 100 chunks/sec for 3 s while a parallel reader drains the whole stream. Setup, trigger, ready-barrier and deployment-side timing are otherwise identical to the existing SL scenario — the only difference is the reader stamps doneAt after the last chunk rather than readAt on the first.

SO = (doneAt - writtenAt) - 3000ms — the end-to-end write+consume time in excess of the modelled generation window, i.e. the stream's overhead/backpressure on top of the token rate. All timestamps are deployment-side (same methodology as the other metrics).

The 3 s / 100-per-sec constants live once in the runner and are passed to benchSoWorkflow(chunkCount, intervalMs) as args (like benchSequentialStepsWorkflow), so the writer's paced span and the runner's subtraction can never drift. Targets are provisional, to be tightened once main baselines land. No methodology-version bump (SO is a new metric; existing baselines stay comparable).

PR-comment smallprint dropdown

The metric/scenario/target definitions and methodology paragraphs are now wrapped in a collapsed <details><summary>ℹ️ Metric definitions & methodology</summary>, mirroring the existing "📜 Previous results" section, so the comment leads with the results table.

Files

  • workbench/example/workflows/97_bench.tsbenchSoWorkflow + soReaderStep/soWriterStep + BenchStreamOverhead (symlinked into nextjs-turbopack/nitro-v3).
  • packages/core/e2e/benchmark.test.ts — SO constants, runSoIteration, scenario + test(...), config/doc updates.
  • .github/scripts/render-benchmark-comment.mjsso in METRIC_LABELS/METRIC_ORDER; footer collapsed into <details>.
  • .github/scripts/render-benchmark-comment.test.js — collapsed-footer + SO-row coverage.

Testing

  • node --test .github/scripts/render-benchmark-comment.test.js — 12/12 pass.
  • @workflow/core typecheck passes (covers the bench runner); 97_bench.ts compiles via SWC; benchSoWorkflow resolves by name through the workbench registry.
  • Note: the bench suite runs against a Vercel deploy only (local dev fails uniformly on all bench workflows, including the unmodified ones), so SO is exercised end-to-end by the Performance Benchmarks workflow on the preview deploy.

🤖 Generated with Claude Code

…smallprint

Adds a new SO benchmark metric modelling a haiku-size LLM token stream: a
writer streams 300 fake 4-byte chunks paced at 100/s for 3s while a parallel
reader drains the whole stream (same setup as SL). SO = end-to-end
write+consume time beyond the 3s generation window, i.e. stream
overhead/backpressure. The 3s window is single-sourced in the runner and
passed to the workflow as args so the write span and subtraction can't drift.

Also collapses the benchmark PR-comment definitions/methodology smallprint
into a <details> dropdown, mirroring the "Previous results" section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9884f34

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Jul 23, 2026 11:24pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jul 23, 2026 11:24pm
example-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-astro-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-express-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-fastify-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-hono-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-nestjs-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-nitro-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-nuxt-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workbench-vite-workflow Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Jul 23, 2026 11:24pm
workflow-swc-playground Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workflow-tarballs Ready Ready Preview, Comment Jul 23, 2026 11:24pm
workflow-web Ready Ready Preview, Comment Jul 23, 2026 11:24pm

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 1455 0 239 1694
✅ 💻 Local Development 1621 0 227 1848
✅ 📦 Local Production 1621 0 227 1848
✅ 🐘 Local Postgres 1621 0 227 1848
✅ 🪟 Windows 154 0 0 154
✅ 📋 Other 1020 0 212 1232
✅ vercel-multi-region 27 0 0 27
Total 7519 0 1132 8651

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 126 0 28
✅ example 126 0 28
✅ express 126 0 28
✅ fastify 126 0 28
✅ hono 126 0 28
✅ nextjs-turbopack 151 0 3
✅ nextjs-webpack 151 0 3
✅ nitro 126 0 28
✅ nuxt 126 0 28
✅ sveltekit 145 0 9
✅ vite 126 0 28
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 154 0 0
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 128 0 26
✅ e2e-local-dev-tanstack-start- 128 0 26
✅ e2e-local-postgres-nest-stable 128 0 26
✅ e2e-local-postgres-tanstack-start- 128 0 26
✅ e2e-local-prod-nest-stable 128 0 26
✅ e2e-local-prod-tanstack-start- 128 0 26
✅ e2e-vercel-prod-nest 126 0 28
✅ e2e-vercel-prod-tanstack-start 126 0 28
✅ vercel-multi-region
App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit 9884f34 · Thu, 23 Jul 2026 23:39:14 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1302 (+273%) 🔻 1442 🔴 (+41%) 🔻 1463 🔴 (+41%) 🔻 1513 🔴 (+8.9%) 30
TTFS stream 1261 (+38%) 🔻 1360 🔴 (+30%) 🔻 1399 🔴 (+32%) 🔻 1447 🔴 (±0%) 30
TTFS hook + stream 1536 (+36%) 🔻 1631 🔴 (+22%) 🔻 1664 🔴 (+17%) 🔻 1959 🔴 (+29%) 🔻 30
STSO 1020 steps (1-20) 186 (+5.7%) 314 🔴 (+13%) 394 🔴 (-12%) 460 🔴 (+2.2%) 19
STSO 1020 steps (101-120) 198 (+5.3%) 273 🔴 (-1.1%) 315 🔴 (-2.8%) 356 🔴 (+6.6%) 19
STSO 1020 steps (1001-1020) 456 (+0.7%) 613 🔴 (+7.9%) 689 🔴 (±0%) 729 🔴 (-13%) 19
WO 1020 steps 417923 (+6.4%) 417923 (+6.4%) 417923 (+6.4%) 417923 (+6.4%) 1
SL stream latency 117 (+46%) 🔻 193 🔴 (+53%) 🔻 219 🔴 (+50%) 🔻 758 🔴 (+80%) 🔻 30
SO stream overhead 135 233 244 492 30
ℹ️ Metric definitions & methodology

Best/P75/P90/P99 deltas compare against the most recent benchmark run on main at the time of this run. 🔻 flags a delta worse than +15%, 💚 one better than −15%.

Metrics — TTFS: time to first step body (in-deployment start() → first step body, deployment clocks) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) · SL: stream latency (in-deployment write → read propagation, readAt - writtenAt) · SO: stream overhead (end-to-end write+consume time beyond the modelled generation window)

Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · stream latency: parallel reader/writer steps on a dedicated stream; SL is the in-deployment write->read propagation (readAt - writtenAt) · stream overhead: writer streams 300 4-byte chunks paced at 100/s for 3s (a haiku-size LLM's token throughput) while a parallel reader drains the whole stream; SO is the end-to-end write+consume time beyond the 3s generation window (overhead/backpressure)

🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 · SL 50/60/125 · SO 250/500/1000 · STSO (1-20) 20/30/60 · STSO (101-120) 30/45/90 · STSO (1001-1020) 40/60/120

All metrics are measured from deployment-side timestamps only. Runs are triggered by an in-deployment route that stamps the anchor (clientStart) right before start(), so the CI runner’s request and its path through api.vercel.com sit outside every measured window. TTFS = in-deployment start() → first step body (turbo uses the in-process fast path, non-turbo the dispatch path), and includes the VQS dispatch hop plus any /flow cold start. STSO/WO are measured between step bodies on the deployment. SL is measured inside the workflow (parallel reader/writer steps), so it no longer includes the api.vercel.com read path.

Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the /flow invocation for a large fraction of runs, inflating P75+; the Best column shows the fastest (warm-start) sample for comparison.

@VaguelySerious
VaguelySerious marked this pull request as ready for review July 23, 2026 23:46
@VaguelySerious
VaguelySerious requested review from a team and ijjk as code owners July 23, 2026 23:46
@VaguelySerious
VaguelySerious enabled auto-merge (squash) July 23, 2026 23:52
@VaguelySerious
VaguelySerious disabled auto-merge July 23, 2026 23:52
@VaguelySerious
VaguelySerious enabled auto-merge (squash) July 23, 2026 23:52
@karthikscale3

Copy link
Copy Markdown
Contributor

Could we make the text-delta workload deterministic but variable-length instead of repeatedly writing "aaaa"? For example, cycle through fragments such as "The", " quick", ".", and "\n", averaging roughly 4–8 UTF-8 bytes. That should more closely represent real token-stream traffic while keeping the run reproducible.

@karthikscale3

Copy link
Copy Markdown
Contributor

Could we also add a structured-delta variant resembling AI SDK events, for example { type: "text-delta", id: "0", text: " quick" }? I’d keep the same 300 chunks at 100 chunks/sec so payload shape is the only changed variable.

@VaguelySerious
VaguelySerious merged commit 604aecb into main Jul 24, 2026
106 checks passed
@VaguelySerious
VaguelySerious deleted the peter/bench-stream-overhead branch July 24, 2026 00:05
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 604aecb (AI decision).

This commit builds entirely on the new benchmark harness that exists only on main: packages/core/e2e/benchmark.test.ts and .github/scripts/render-benchmark-comment.mjs/.test.js are absent on stable (verified — stable only has the older bench.bench.ts and different render scripts), and stable's workbench/example/workflows/97_bench.ts is an older file without the SL scenario the new SO scenario is derived from. There is nothing on stable for this change to apply to.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

604aecb0211b6872534679ee8d3cfa2aff9fcbb3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants