Skip to content

Add Flight SSR benchmark fixture#36180

Open
unstubbable wants to merge 40 commits intofacebook:mainfrom
unstubbable:flight-ssr-bench
Open

Add Flight SSR benchmark fixture#36180
unstubbable wants to merge 40 commits intofacebook:mainfrom
unstubbable:flight-ssr-bench

Conversation

@unstubbable
Copy link
Copy Markdown
Collaborator

@unstubbable unstubbable commented Apr 1, 2026

This PR adds a benchmark fixture for measuring the performance overhead of the React Server Components (RSC) Flight rendering compared to plain Fizz server-side rendering.

Motivation

Performance discussions around RSC (e.g. #36143, #35125) have highlighted the need for reproducible benchmarks that accurately measure the cost that Flight adds on top of Fizz. This fixture provides multiple benchmark modes that can be used to track performance improvements across commits, compare Node vs Edge (web streams) overhead, and identify bottlenecks in Flight serialization and deserialization.

What it measures

The benchmark renders a dashboard app with ~25 components (16 client components), 200 product rows with nested data (~325KB Flight payload), and ~250 Suspense boundaries in the async variant. It compares 8 render variants: Fizz-only and Flight+Fizz, across Node and Edge stream APIs, with both synchronous and asynchronous apps.

Benchmark modes

  • yarn bench runs a sequential in-process benchmark with realistic Flight script injection (tee + TransformStream/Transform buffered injection), matching what real frameworks do when inlining the RSC payload into the HTML response for hydration.
  • yarn bench:bare runs the same benchmark without script injection, isolating the React-internal rendering cost. This is best for tracking changes to Flight serialization or Fizz rendering.
  • yarn bench:server starts an HTTP server and uses autocannon to measure real req/s at c=1 and c=10. The c=1 results provide a clean signal for tracking React-internal changes, while c=10 reflects throughput under concurrent load.
  • yarn bench:concurrent runs an in-process concurrent benchmark with 50 in-flight renders via Promise.all, measuring throughput without HTTP overhead.
  • yarn bench:profile collects CPU profiles via the V8 inspector and reports the top functions by self-time along with GC pause data.
  • yarn start starts the HTTP server for manual browser testing. Appending .rsc to any Flight URL serves the raw Flight payload.

Key findings during development

On Node 22, the Flight+Fizz overhead compared to Fizz-only rendering is roughly:

  • Without script injection (bench:bare): ~2.2x for sync, ~1.3x for async
  • With script injection (bench:server, c=1): ~2.9x for sync, ~1.8x for async
  • Edge vs Node adds another ~30% for sync and ~10% for async, driven by the stream plumbing for script injection (tee + TransformStream buffering)

The async variant better represents real-world applications where server components fetch data asynchronously. Its lower overhead reflects the fact that Flight serialization and Fizz rendering can overlap with I/O wait times, making the added Flight cost a smaller fraction of total request time.

The benchmark also revealed that the Edge vs Node gap is negligible for Fizz-only rendering (~1-2%) but grows to ~15% for Flight+Fizz sync even without script injection. With script injection (tee + TransformStream buffering), the gap roughly doubles to ~30% for sync. The async variants show smaller gaps (~5% without, ~10% with injection).

@meta-cla meta-cla bot added the CLA Signed label Apr 1, 2026
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Apr 1, 2026
@react-sizebot
Copy link
Copy Markdown

react-sizebot commented Apr 1, 2026

Comparing: 80b1cab...cd406da

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 612.91 kB 612.91 kB = 108.30 kB 108.30 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 678.85 kB 678.85 kB = 119.27 kB 119.27 kB
facebook-www/ReactDOM-prod.classic.js = 698.24 kB 698.24 kB = 122.65 kB 122.65 kB
facebook-www/ReactDOM-prod.modern.js = 688.55 kB 688.55 kB = 121.03 kB 121.03 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against cd406da

@unstubbable unstubbable marked this pull request as ready for review April 1, 2026 22:08
@unstubbable unstubbable requested review from eps1lon and gnoff April 1, 2026 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants