Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions bench/dicer/dicer-bench.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict'

const { Bench } = require('tinybench')
const Dicer = require('../../deps/dicer/lib/Dicer')

function createMultipartBuffer (boundary, size) {
const head =
'--' + boundary + '\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
'\r\n'
const tail = '\r\n--' + boundary + '--\r\n'
const buffer = Buffer.allocUnsafe(size)
buffer.write(head, 0, 'ascii')
buffer.write(tail, buffer.length - tail.length, 'ascii')
return buffer
}

const boundary = '-----------------------------168072824752491622650073'
const mb = 100
const buffer = createMultipartBuffer(boundary, mb * 1024 * 1024)

const bench = new Bench({ iterations: 10, warmupIterations: 2 })

bench
.add(`dicer multipart (${mb}MB)`, () => {
const d = new Dicer({ boundary })
d.on('part', (p) => {
p.on('header', () => {})
p.on('data', () => {})
p.on('end', () => {})
})
d.on('end', () => {})
d.write(buffer)
})
.run()
.then(() => {
console.table(bench.table())
})
32 changes: 32 additions & 0 deletions bench/form-bench.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict'

const { Bench } = require('tinybench')
const Busboy = require('../index')
const { createMultipartBufferForEncodingBench } = require('./createMultipartBufferForEncodingBench')

const boundary = '-----------------------------168072824752491622650073'

function createBusboyRun (charset) {
const buffer = createMultipartBufferForEncodingBench(boundary, 100, charset)
return () => {
const busboy = new Busboy({
headers: {
'content-type': 'multipart/form-data; boundary=' + boundary
}
})
busboy.on('file', (field, file) => { file.resume() })
busboy.on('error', () => {})
busboy.write(buffer, () => {})
busboy.end()
}
}

const bench = new Bench({ iterations: 1000, warmupIterations: 100 })

bench
.add('fastify-busboy form (latin1)', createBusboyRun('iso-8859-1'))
.add('fastify-busboy form (utf-8)', createBusboyRun('utf-8'))
.run()
.then(() => {
console.table(bench.table())
})
26 changes: 11 additions & 15 deletions benchmarks/busboy/executioner.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const { process: processBusboy } = require('./contestants/busboy')
const { process: processFastify } = require('./contestants/fastify-busboy')
const { getCommonBuilder } = require('../common/commonBuilder')
const { getCommonBench } = require('../common/commonBuilder')
const { validateAccuracy } = require('./validator')
const { resolveContestant } = require('../common/contestantResolver')
const { outputResults } = require('../common/resultUtils')
Expand All @@ -13,23 +13,19 @@ const contestants = {
}

async function measureBusboy () {
const benchmark = getCommonBuilder()
.benchmarkName('Busboy comparison')
.benchmarkEntryName('busboy')
.asyncFunctionUnderTest(processBusboy)
.build()
const benchmarkResults = await benchmark.executeAsync()
outputResults(benchmark, benchmarkResults)
const bench = getCommonBench()
bench.add('busboy', processBusboy)
await bench.run()
console.table(bench.table())
outputResults('busboy', bench)
}

async function measureFastify () {
const benchmark = getCommonBuilder()
.benchmarkName('Busboy comparison')
.benchmarkEntryName('fastify-busboy')
.asyncFunctionUnderTest(processFastify)
.build()
const benchmarkResults = await benchmark.executeAsync()
outputResults(benchmark, benchmarkResults)
const bench = getCommonBench()
bench.add('fastify-busboy', processFastify)
await bench.run()
console.table(bench.table())
outputResults('fastify-busboy', bench)
}

function execute () {
Expand Down
30 changes: 15 additions & 15 deletions benchmarks/busboy/validator.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
'use strict'
const { validateEqual } = require('validation-utils')
const { randomContent } = require('./data')
const EXPECTED_RESULT = randomContent.toString()
async function validateAccuracy (actualResultPromise) {
const result = await actualResultPromise
validateEqual(result, EXPECTED_RESULT)
}
module.exports = {
validateAccuracy
}
'use strict'

const assert = require('node:assert')
const { randomContent } = require('./data')

const EXPECTED_RESULT = randomContent.toString()

async function validateAccuracy (actualResultPromise) {
const result = await actualResultPromise
assert.strictEqual(result, EXPECTED_RESULT)
}

module.exports = {
validateAccuracy
}
40 changes: 12 additions & 28 deletions benchmarks/common/commonBuilder.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict'

const { validateNotNil } = require('validation-utils')
const { BenchmarkBuilder } = require('photofinish')
const { Bench } = require('tinybench')
const getopts = require('getopts')

const options = getopts(process.argv.slice(1), {
Expand All @@ -12,35 +11,20 @@ const options = getopts(process.argv.slice(1), {
})

const PRESET = {
LOW: (builder) => {
return builder
.warmupCycles(1000)
.benchmarkCycles(1000)
},

MEDIUM: (builder) => {
return builder
.warmupCycles(1000)
.benchmarkCycles(2000)
},

HIGH: (builder) => {
return builder
.warmupCycles(1000)
.benchmarkCycles(10000)
}
LOW: { iterations: 1000, warmupIterations: 100 },
MEDIUM: { iterations: 2000, warmupIterations: 100 },
HIGH: { iterations: 10000, warmupIterations: 1000 }
}

function getCommonBuilder () {
const presetId = options.preset || 'MEDIUM'
const preset = validateNotNil(PRESET[presetId.toUpperCase()], `Unknown preset: ${presetId}`)

const builder = new BenchmarkBuilder()
preset(builder)
return builder
.benchmarkCycleSamples(50)
function getCommonBench () {
const presetId = (options.preset || 'MEDIUM').toUpperCase()
const preset = PRESET[presetId]
if (!preset) {
throw new Error(`Unknown preset: ${presetId}`)
}
return new Bench(preset)
}

module.exports = {
getCommonBuilder
getCommonBench
}
32 changes: 24 additions & 8 deletions benchmarks/common/resultUtils.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
'use strict'

const { exportResults } = require('photofinish')
const fs = require('node:fs')
const path = require('node:path')

function outputResults (benchmark, benchmarkResults) {
console.log(
`Mean time for ${
benchmark.benchmarkEntryName
} is ${benchmarkResults.meanTime.getTimeInNanoSeconds()} nanoseconds`
)
function outputResults (name, bench) {
const task = bench.tasks.find(t => t.name === name)
if (task && task.result) {
console.log(
`Mean time for ${name} is ${Math.round(task.result.mean * 1e6)} nanoseconds`
)
}

const resultsDir = path.resolve('_results')
if (!fs.existsSync(resultsDir)) {
fs.mkdirSync(resultsDir, { recursive: true })
}

exportResults(benchmarkResults, { exportPath: '_results' })
const results = bench.tasks.map(t => ({
name: t.name,
meanTimeNs: t.result ? Math.round(t.result.mean * 1e6) : null,
opsPerSecond: t.result ? Math.round(t.result.hz) : null
}))

fs.writeFileSync(
path.resolve(resultsDir, `${name}.json`),
JSON.stringify(results, null, 2)
)
}

module.exports = {
Expand Down
46 changes: 16 additions & 30 deletions benchmarks/common/resultsCombinator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,38 @@
const fs = require('node:fs')
const path = require('node:path')
const getopts = require('getopts')
const systemInformation = require('systeminformation')
const { loadResults } = require('photofinish')

const options = getopts(process.argv.slice(1), {
alias: {
resultsDir: 'r',
precision: 'p'
},
default: {}
})

const { generateTable } = require('photofinish')

async function getSpecs () {
const cpuInfo = await systemInformation.cpu()

return {
cpu: {
brand: cpuInfo.brand,
speed: `${cpuInfo.speed} GHz`
}
default: {
precision: 6
}
}
})

async function saveTable () {
const baseResultsDir = options.resultsDir
const benchmarkResults = await loadResults(baseResultsDir)
const files = fs.readdirSync(baseResultsDir).filter(f => f.endsWith('.json'))

const table = generateTable(benchmarkResults, {
precision: options.precision,
sortBy: [
{ field: 'meanTimeNs', order: 'asc' }
]
})
const allResults = []
for (const file of files) {
const content = JSON.parse(fs.readFileSync(path.resolve(baseResultsDir, file), 'utf8'))
allResults.push(...content)
}

const specs = await getSpecs()
const precision = Number(options.precision)
const lines = ['| Name | Mean Time (ns) | Ops/sec |', '|------|---------------|---------|']
for (const r of allResults.sort((a, b) => (a.meanTimeNs || 0) - (b.meanTimeNs || 0))) {
lines.push(`| ${r.name} | ${r.meanTimeNs?.toFixed(precision) ?? 'N/A'} | ${r.opsPerSecond ?? 'N/A'} |`)
}

console.log(specs)
const table = lines.join('\n')
console.log(table)

const targetFilePath = path.resolve(baseResultsDir, 'results.md')
fs.writeFileSync(
targetFilePath,
`${table}` +
`\n\n**Specs**: ${specs.cpu.brand} (${specs.cpu.speed})`
)
fs.writeFileSync(targetFilePath, table)
}

saveTable()
6 changes: 1 addition & 5 deletions benchmarks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@
"license": "MIT",
"dependencies": {
"getopts": "^2.3.0",
"photofinish": "^1.8.0",
"systeminformation": "^5.9.15",
"tslib": "^2.3.1",
"validation-utils": "^7.0.0"
"tinybench": "^6.0.0"
},
"scripts": {
"install-node": "nvm install 17.2.0 && nvm install 16.13.1 && nvm install 14.18.2 && nvm install 12.22.7",
"benchmark-busboy": "node busboy/executioner.js -c 0",
"benchmark-fastify": "node busboy/executioner.js -c 1",
"benchmark-all": "npm run benchmark-busboy -- -p high && npm run benchmark-fastify -- -p high",
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
"types": "types/index.d.ts",
"scripts": {
"bench:busboy": "cd benchmarks && npm install && npm run benchmark-fastify",
"bench:dicer": "node bench/dicer/dicer-bench-multipart-parser.js",
"bench:form": "node bench/form-bench.js",
"bench:dicer": "node bench/dicer/dicer-bench.js",
"bench:parse-params": "node bench/parse-params.js",
"bench:all": "npm run bench:form && npm run bench:dicer && npm run bench:parse-params",
"coveralls": "nyc report --reporter=lcov",
"lint": "eslint",
"lint:fix": "eslint --fix",
Expand All @@ -33,7 +36,6 @@
"@types/node": "^25.0.3",
"busboy": "^1.6.0",
"c8": "^11.0.0",
"photofinish": "^1.8.0",
"snazzy": "^9.0.0",
"eslint": "^9.39.0",
"neostandard": "^0.12.0",
Expand Down
Loading