main
md 64 lines 3.18 KB
Rendered Raw
1 # Flight SSR Benchmark
2
3 Measures the performance overhead of the React Server Components (RSC) Flight pipeline compared to plain Fizz server-side rendering, across both Node and Edge (web streams) APIs.
4
5 ## Prerequisites
6
7 Build React from the repo root first:
8
9 ```sh
10 yarn build-for-flight-prod
11 ```
12
13 Then install the fixture's dependencies:
14
15 ```sh
16 cd fixtures/flight-ssr-bench
17 yarn install
18 ```
19
20 ## Scripts
21
22 | Script | Purpose |
23 | --- | --- |
24 | `yarn bench` | Sequential benchmark with Flight script injection (realistic framework pipeline). Best for measuring Edge vs Node overhead. |
25 | `yarn bench:bare` | Sequential benchmark without script injection. Best for measuring React-internal changes (e.g. Flight serialization optimizations) with less noise from stream plumbing. |
26 | `yarn bench:server` | HTTP server benchmark using autocannon at c=1 and c=10. Best for measuring real-world req/s. The c=1 results are also useful for tracking React-internal changes. |
27 | `yarn bench:concurrent` | In-process concurrent benchmark (50 in-flight renders). Measures throughput under load without HTTP overhead. |
28 | `yarn bench:profile` | CPU profiling via V8 inspector. Saves `.cpuprofile` files to `build/profiles/`. |
29 | `yarn start` | Starts the HTTP server for manual browser testing at `http://localhost:3001`. Append `.rsc` to any Flight URL to see the raw Flight payload. |
30
31 ## What it measures
32
33 Each script benchmarks 8 render variants:
34
35 - **Fizz (Node, sync/async)** -- plain `renderToPipeableStream`, no RSC
36 - **Fizz (Edge, sync/async)** -- plain `renderToReadableStream`, no RSC
37 - **Flight + Fizz (Node, sync/async)** -- full RSC pipeline: Flight server (`renderToPipeableStream`) -> Flight client (`createFromNodeStream`) -> Fizz (`renderToPipeableStream`)
38 - **Flight + Fizz (Edge, sync/async)** -- full RSC pipeline: Flight server (`renderToReadableStream`) -> Flight client (`createFromReadableStream`) -> Fizz (`renderToReadableStream`)
39
40 The "sync" variants use a fully synchronous app (no Suspense boundaries). The "async" variants use per-row async components with staggered delays and individual Suspense boundaries (~250 boundaries per render).
41
42 ### Script injection
43
44 The `yarn bench` and `yarn bench:server` scripts simulate what real frameworks do: tee the Flight stream and inject `<script>` hydration tags into the HTML output. This uses a `setTimeout(0)`-buffered Transform/TransformStream to avoid splitting mid-HTML-tag. `yarn bench:bare` skips this for cleaner React-internal measurement.
45
46 ## Test app
47
48 A dashboard with ~25 components (16 client components), rendering:
49
50 - 200 product rows with nested reviews, specifications, and supplier data (~325KB Flight payload)
51 - 50 activity feed items
52 - Stats grid with 24-month chart data
53 - Sidebar with navigation and recent activity
54
55 ## Output
56
57 Each variant reports render latency stats, GC pauses, and (when run with `--expose-gc`, which the `yarn bench*` scripts do) the heap retained after the run settles.
58
59 The overhead tables show two comparisons:
60
61 1. **Flight overhead** -- Flight+Fizz vs Fizz-only (how much RSC adds)
62 2. **Edge vs Node** -- web streams vs Node streams (stream implementation cost)
63
64 Delta is shown as percentage change plus a factor (e.g. `+120% 2.20x` means 2.2x slower).