rsnext/bench/vercel/chart.js
Jimmy Lai 2c23da077f
misc: fix benchmark script (#44592)
Fixing + doing some maintenance as I was using it for some things.

Changes:
- fixed a bug related to how React is injected
- added a progress tracker for the numbers of requests
- retry on 500
- added a param for crashing the lambda manually (useful for cold boots)
- added a param to skip build, useful if you want to retest the same variation
- don't crash when there's no data to compare

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2023-01-05 13:46:41 +00:00

104 lines
2.8 KiB
JavaScript

import downsampler from 'downsample-lttb'
import asciichart from 'asciichart'
import terminalSize from 'term-size'
const CHART_WIDTH = terminalSize().columns - 15 // space for the labels
function getMetrics(data) {
const sorted = [...data].sort((a, b) => a - b)
const getPercentile = (percentile) => {
const index = Math.floor((sorted.length - 1) * percentile)
return sorted[index]
}
return {
hits: sorted.length,
confidenceInterval: round(getConfidenceInterval(sorted)),
median: getPercentile(0.5),
avg: sorted.reduce((a, b) => a + b, 0) / sorted.length,
p75: getPercentile(0.75),
p95: getPercentile(0.95),
p99: getPercentile(0.99),
p25: getPercentile(0.25),
min: sorted[0],
max: sorted[sorted.length - 1],
}
}
function round(num) {
return Math.round(num * 100) / 100
}
// thanks Copilot
function getConfidenceInterval(data) {
const n = data.length
const m = data.reduce((a, b) => a + b) / n
const s = Math.sqrt(
data.map((x) => Math.pow(x - m, 2)).reduce((a, b) => a + b) / n
)
const z = 1.96 // 95% confidence
const e = z * (s / Math.sqrt(n))
return e
}
export function downsample(data, maxPoints) {
const sortedData = [...data].sort((a, b) => a - b)
return downsampler
.processData(
// the downsampler expects a 2d array of [x, y] values, so we need to add an index
sortedData.map((p, i) => [p, i]),
maxPoints
)
.map((p) => p[0])
}
export function printBenchmarkResults({ origin, head }, metricSelector) {
const [processedOriginData, processedHeadData] = [origin, head].map(
(results) => results.map(metricSelector).filter(Boolean)
)
if (processedHeadData.length === 0 || processedOriginData.length === 0) {
console.log('No data to compare, skipping')
return
}
const [originMetrics, headMetrics] = [
processedOriginData,
processedHeadData,
].map(getMetrics)
const deltaMetrics = {
min: headMetrics.min - originMetrics.min,
max: headMetrics.max - originMetrics.max,
avg: headMetrics.avg - originMetrics.avg,
median: headMetrics.median - originMetrics.median,
p95: headMetrics.p95 - originMetrics.p95,
p99: headMetrics.p99 - originMetrics.p99,
p75: headMetrics.p75 - originMetrics.p75,
p25: headMetrics.p25 - originMetrics.p25,
}
console.table({
origin: originMetrics,
head: headMetrics,
delta: deltaMetrics,
})
const [originData, headData] = [processedOriginData, processedHeadData].map(
(data) =>
downsample(
data,
Math.min(
CHART_WIDTH,
processedOriginData.length,
processedHeadData.length
)
)
)
console.log(
asciichart.plot([originData, headData], {
height: 15,
colors: [asciichart.blue, asciichart.red],
})
)
}