mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
a39366938a
## Summary Development scripts for profiling and benchmarking Next.js dev server boot time. ### Benchmarking Scripts - `benchmark-boot-time.sh` - Wall-clock benchmarking with two metrics: - **Listen time**: When TCP port accepts connections - **Ready time**: When first HTTP request succeeds - `benchmark-next-dev-boot.js` - Multi-iteration benchmarking with statistics (median, p95, stddev) ### Profiling Scripts - `profile-next-dev-boot.js` - CPU profiling infrastructure using V8 inspector - `analyze-profile.js` - Analyze CPU profiles to identify hot modules by CPU time ### Analysis Scripts - `analyze-dev-server-bundle.js` - Bundle analyzer for dev server (generates treemap report) - `trace-cli-startup.js` - Module loading trace to identify slow imports ## Usage ```bash # Benchmark dev server boot time (5 runs by default) ./scripts/benchmark-boot-time.sh # Multi-iteration benchmark with stats node scripts/benchmark-next-dev-boot.js --iterations 10 # Generate CPU profile node scripts/profile-next-dev-boot.js # Analyze profile output node scripts/analyze-profile.js .next/cpu-profiles/*.cpuprofile # Analyze dev server bundle node scripts/analyze-dev-server-bundle.js --open ``` ## Test Plan - [x] Run `./scripts/benchmark-boot-time.sh` locally - [x] Verify scripts execute without errors
50 lines
1.3 KiB
JavaScript
50 lines
1.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Analyze a CPU profile to identify hot modules
|
|
*/
|
|
|
|
const fs = require('fs')
|
|
|
|
const profilePath = process.argv[2]
|
|
if (!profilePath) {
|
|
console.error('Usage: node analyze-profile.js <profile.cpuprofile>')
|
|
process.exit(1)
|
|
}
|
|
|
|
const profile = JSON.parse(fs.readFileSync(profilePath, 'utf-8'))
|
|
|
|
// Extract nodes with their hit counts
|
|
const nodes = profile.nodes || []
|
|
|
|
// Group by file/module
|
|
const moduleHits = {}
|
|
nodes.forEach((node) => {
|
|
const fn = node.callFrame
|
|
if (fn && fn.url) {
|
|
const url = fn.url
|
|
// Extract module name from path
|
|
let moduleName = url
|
|
if (url.includes('next/dist/')) {
|
|
moduleName = url.split('next/dist/')[1]
|
|
} else if (url.includes('node_modules/')) {
|
|
moduleName = 'node_modules/' + url.split('node_modules/').pop()
|
|
}
|
|
if (!moduleHits[moduleName]) {
|
|
moduleHits[moduleName] = { hits: 0 }
|
|
}
|
|
moduleHits[moduleName].hits += node.hitCount || 0
|
|
}
|
|
})
|
|
|
|
// Sort by hits
|
|
const sorted = Object.entries(moduleHits)
|
|
.filter(([_, v]) => v.hits > 0)
|
|
.sort((a, b) => b[1].hits - a[1].hits)
|
|
.slice(0, 40)
|
|
|
|
console.log('Top 40 modules by CPU time:')
|
|
console.log('='.repeat(70))
|
|
sorted.forEach(([name, data], i) => {
|
|
console.log(`${String(i + 1).padStart(2)}. ${name} (${data.hits} hits)`)
|
|
})
|