mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## Summary Benchmark infrastructure for measuring render pipeline performance. - **render-pipeline benchmark** (`bench/render-pipeline/`): `benchmark.ts` for profiling render paths, `analyze-profiles.ts` for CPU profile analysis - **Stress routes** (`bench/basic-app/app/streaming/`): light, medium, heavy, bulk, wide, chunkstorm variants for different streaming load profiles - **Basic app harness**: `benchmark.sh` runner script, `next.config.js` - **Minimal server**: `bench/next-minimal-server/bin/minimal-server.js` - **Docs**: `bench/BENCHMARKING.md` guide - **Config**: eslint exclusion for bench paths, package.json bench scripts ## Test plan - [ ] No runtime behavior changes - [ ] Benchmark scripts are standalone tooling --------- Co-authored-by: Tim Neutkens <[email protected]>
106 lines
2.7 KiB
JavaScript
106 lines
2.7 KiB
JavaScript
import React, { Suspense } from 'react'
|
|
|
|
import { StreamingClientBoundary } from './client-boundary'
|
|
|
|
function sleep(ms) {
|
|
if (ms <= 0) return Promise.resolve()
|
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
}
|
|
|
|
function createPayload(title, payloadBytes) {
|
|
const prefix = `${title}:`
|
|
if (prefix.length >= payloadBytes) return prefix
|
|
return `${prefix}${'x'.repeat(payloadBytes - prefix.length)}`
|
|
}
|
|
|
|
function createClientPayload({ title, id, payloadBytes, fragmentCount }) {
|
|
const payload = createPayload(`${title}-client-${id}`, payloadBytes)
|
|
const safeFragmentCount = Math.max(1, fragmentCount)
|
|
const fragmentSize = Math.max(
|
|
16,
|
|
Math.floor(payload.length / safeFragmentCount)
|
|
)
|
|
const fragments = Array.from({ length: safeFragmentCount }, (_, index) => {
|
|
const start = Math.min(index * fragmentSize, payload.length)
|
|
const end = Math.min(start + fragmentSize, payload.length)
|
|
return payload.slice(start, end)
|
|
})
|
|
|
|
return {
|
|
chunkId: id,
|
|
payload,
|
|
fragments,
|
|
checksum: payload.length + id * 31 + safeFragmentCount,
|
|
}
|
|
}
|
|
|
|
async function StreamedChunk({
|
|
title,
|
|
id,
|
|
delayMs,
|
|
payload,
|
|
clientPayloadBytes,
|
|
clientPayloadFragments,
|
|
}) {
|
|
await sleep(delayMs)
|
|
|
|
const clientPayload = createClientPayload({
|
|
title,
|
|
id,
|
|
payloadBytes: clientPayloadBytes,
|
|
fragmentCount: clientPayloadFragments,
|
|
})
|
|
|
|
return (
|
|
<article data-chunk-id={id}>
|
|
<h2>chunk-{id}</h2>
|
|
<p>{payload}</p>
|
|
<StreamingClientBoundary {...clientPayload} />
|
|
</article>
|
|
)
|
|
}
|
|
|
|
export function StreamingStressPage({
|
|
title,
|
|
boundaryCount,
|
|
payloadBytes,
|
|
clientPayloadBytes = Math.max(128, Math.floor(payloadBytes / 2)),
|
|
clientPayloadFragments = 4,
|
|
maxDelayMs,
|
|
}) {
|
|
const payload = createPayload(title, payloadBytes)
|
|
const boundaries = Array.from({ length: boundaryCount }, (_, index) => index)
|
|
|
|
return (
|
|
<main>
|
|
<h1>{title}</h1>
|
|
<p>
|
|
boundaries={boundaryCount} payloadBytes={payloadBytes}{' '}
|
|
clientPayloadBytes=
|
|
{clientPayloadBytes} clientPayloadFragments={clientPayloadFragments}{' '}
|
|
maxDelayMs={maxDelayMs}
|
|
</p>
|
|
|
|
{boundaries.map((id) => {
|
|
const delayMs = maxDelayMs === 0 ? 0 : id % (maxDelayMs + 1)
|
|
|
|
return (
|
|
<Suspense
|
|
key={id}
|
|
fallback={<div data-fallback-id={id}>loading-{id}</div>}
|
|
>
|
|
<StreamedChunk
|
|
title={title}
|
|
id={id}
|
|
delayMs={delayMs}
|
|
payload={payload}
|
|
clientPayloadBytes={clientPayloadBytes}
|
|
clientPayloadFragments={clientPayloadFragments}
|
|
/>
|
|
</Suspense>
|
|
)
|
|
})}
|
|
</main>
|
|
)
|
|
}
|