diff --git a/bench/dicer/dicer-bench.js b/bench/dicer/dicer-bench.js new file mode 100644 index 0000000..7e26a57 --- /dev/null +++ b/bench/dicer/dicer-bench.js @@ -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()) + }) diff --git a/bench/form-bench.js b/bench/form-bench.js new file mode 100644 index 0000000..632594c --- /dev/null +++ b/bench/form-bench.js @@ -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()) + }) diff --git a/benchmarks/busboy/executioner.js b/benchmarks/busboy/executioner.js index 524912c..eabf2e8 100644 --- a/benchmarks/busboy/executioner.js +++ b/benchmarks/busboy/executioner.js @@ -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') @@ -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 () { diff --git a/benchmarks/busboy/validator.js b/benchmarks/busboy/validator.js index a86cc33..77ad488 100644 --- a/benchmarks/busboy/validator.js +++ b/benchmarks/busboy/validator.js @@ -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 +} diff --git a/benchmarks/common/commonBuilder.js b/benchmarks/common/commonBuilder.js index b5707aa..e8a97e7 100644 --- a/benchmarks/common/commonBuilder.js +++ b/benchmarks/common/commonBuilder.js @@ -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), { @@ -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 } diff --git a/benchmarks/common/resultUtils.js b/benchmarks/common/resultUtils.js index ec7bce7..368ee49 100644 --- a/benchmarks/common/resultUtils.js +++ b/benchmarks/common/resultUtils.js @@ -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 = { diff --git a/benchmarks/common/resultsCombinator.js b/benchmarks/common/resultsCombinator.js index 253211b..c04b49d 100644 --- a/benchmarks/common/resultsCombinator.js +++ b/benchmarks/common/resultsCombinator.js @@ -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() diff --git a/benchmarks/package.json b/benchmarks/package.json index 2574b8b..a35f4fb 100644 --- a/benchmarks/package.json +++ b/benchmarks/package.json @@ -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", diff --git a/package.json b/package.json index 3cb7ecf..99856ee 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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",