Skip to content

Commit 8376722

Browse files
committed
fix(benchmark): make it fair
1 parent aa86e89 commit 8376722

File tree

4 files changed

+135
-143
lines changed

4 files changed

+135
-143
lines changed

benchmarks/_util/index.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict'
2+
3+
const parallelRequests = parseInt(process.env.PARALLEL, 10) || 100
4+
5+
function makeParallelRequests (cb) {
6+
const promises = new Array(parallelRequests)
7+
for (let i = 0; i < parallelRequests; ++i) {
8+
promises[i] = new Promise(cb)
9+
}
10+
return Promise.all(promises)
11+
}
12+
13+
function printResults (results) {
14+
// Sort results by least performant first, then compare relative performances and also printing padding
15+
let last
16+
17+
const rows = Object.entries(results)
18+
// If any failed, put on the top of the list, otherwise order by mean, ascending
19+
.sort((a, b) => (!a[1].success ? -1 : b[1].mean - a[1].mean))
20+
.map(([name, result]) => {
21+
if (!result.success) {
22+
return {
23+
Tests: name,
24+
Samples: result.size,
25+
Result: 'Errored',
26+
Tolerance: 'N/A',
27+
'Difference with Slowest': 'N/A'
28+
}
29+
}
30+
31+
// Calculate throughput and relative performance
32+
const { size, mean, standardError } = result
33+
const relative = last !== 0 ? (last / mean - 1) * 100 : 0
34+
35+
// Save the slowest for relative comparison
36+
if (typeof last === 'undefined') {
37+
last = mean
38+
}
39+
40+
return {
41+
Tests: name,
42+
Samples: size,
43+
Result: `${((parallelRequests * 1e9) / mean).toFixed(2)} req/sec`,
44+
Tolerance: ${((standardError / mean) * 100).toFixed(2)} %`,
45+
'Difference with slowest':
46+
relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'
47+
}
48+
})
49+
50+
return console.table(rows)
51+
}
52+
53+
module.exports = { makeParallelRequests, printResults }

benchmarks/benchmark.js

Lines changed: 43 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,23 @@ const path = require('node:path')
66
const { Writable } = require('node:stream')
77
const { isMainThread } = require('node:worker_threads')
88

9-
const { Pool, Client, fetch, Agent, setGlobalDispatcher } = require('..')
9+
const { Pool, Client, fetch, Agent, setGlobalDispatcher, util } = require('..')
10+
11+
const { makeParallelRequests, printResults } = require('./_util')
1012

1113
let nodeFetch
1214
const axios = require('axios')
1315
let superagent
1416
let got
1517

16-
const util = require('node:util')
17-
const _request = require('request')
18-
const request = util.promisify(_request)
18+
const { promisify } = require('node:util')
19+
const assert = require('node:assert')
20+
const request = promisify(require('request'))
1921

2022
const iterations = (parseInt(process.env.SAMPLES, 10) || 10) + 1
2123
const errorThreshold = parseInt(process.env.ERROR_THRESHOLD, 10) || 3
2224
const connections = parseInt(process.env.CONNECTIONS, 10) || 50
2325
const pipelining = parseInt(process.env.PIPELINING, 10) || 10
24-
const parallelRequests = parseInt(process.env.PARALLEL, 10) || 100
2526
const headersTimeout = parseInt(process.env.HEADERS_TIMEOUT, 10) || 0
2627
const bodyTimeout = parseInt(process.env.BODY_TIMEOUT, 10) || 0
2728
const dest = {}
@@ -119,7 +120,8 @@ class SimpleRequest {
119120

120121
onConnect (abort) { }
121122

122-
onHeaders (statusCode, headers, resume) {
123+
onHeaders (statusCode, rawHeaders, resume) {
124+
assert(util.parseHeaders(rawHeaders)['content-type'])
123125
this.dst.on('drain', resume)
124126
}
125127

@@ -136,57 +138,11 @@ class SimpleRequest {
136138
}
137139
}
138140

139-
function makeParallelRequests (cb) {
140-
const promises = new Array(parallelRequests)
141-
for (let i = 0; i < parallelRequests; ++i) {
142-
promises[i] = new Promise(cb)
143-
}
144-
return Promise.all(promises)
145-
}
146-
147-
function printResults (results) {
148-
// Sort results by least performant first, then compare relative performances and also printing padding
149-
let last
150-
151-
const rows = Object.entries(results)
152-
// If any failed, put on the top of the list, otherwise order by mean, ascending
153-
.sort((a, b) => (!a[1].success ? -1 : b[1].mean - a[1].mean))
154-
.map(([name, result]) => {
155-
if (!result.success) {
156-
return {
157-
Tests: name,
158-
Samples: result.size,
159-
Result: 'Errored',
160-
Tolerance: 'N/A',
161-
'Difference with Slowest': 'N/A'
162-
}
163-
}
164-
165-
// Calculate throughput and relative performance
166-
const { size, mean, standardError } = result
167-
const relative = last !== 0 ? (last / mean - 1) * 100 : 0
168-
169-
// Save the slowest for relative comparison
170-
if (typeof last === 'undefined') {
171-
last = mean
172-
}
173-
174-
return {
175-
Tests: name,
176-
Samples: size,
177-
Result: `${((parallelRequests * 1e9) / mean).toFixed(2)} req/sec`,
178-
Tolerance: ${((standardError / mean) * 100).toFixed(2)} %`,
179-
'Difference with slowest': relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'
180-
}
181-
})
182-
183-
return console.table(rows)
184-
}
185-
186141
const experiments = {
187142
'http - no keepalive' () {
188143
return makeParallelRequests(resolve => {
189144
http.get(httpNoKeepAliveOptions, res => {
145+
assert(res.headers['content-type'])
190146
res
191147
.pipe(
192148
new Writable({
@@ -202,6 +158,7 @@ const experiments = {
202158
'http - keepalive' () {
203159
return makeParallelRequests(resolve => {
204160
http.get(httpKeepAliveOptions, res => {
161+
assert(res.headers['content-type'])
205162
res
206163
.pipe(
207164
new Writable({
@@ -217,8 +174,8 @@ const experiments = {
217174
'undici - pipeline' () {
218175
return makeParallelRequests(resolve => {
219176
dispatcher
220-
.pipeline(undiciOptions, data => {
221-
return data.body
177+
.pipeline(undiciOptions, ({ body }) => {
178+
return body
222179
})
223180
.end()
224181
.pipe(
@@ -288,9 +245,15 @@ if (process.env.PORT) {
288245
})
289246
}
290247

248+
const axiosOptions = {
249+
url: dest.url,
250+
method: 'GET',
251+
responseType: 'stream',
252+
httpAgent: axiosAgent
253+
}
291254
experiments.axios = () => {
292255
return makeParallelRequests(resolve => {
293-
axios.get(dest.url, { responseType: 'stream', httpAgent: axiosAgent }).then(res => {
256+
axios.request(axiosOptions).then(res => {
294257
res.data.pipe(new Writable({
295258
write (chunk, encoding, callback) {
296259
callback()
@@ -300,26 +263,37 @@ if (process.env.PORT) {
300263
})
301264
}
302265

266+
const gotOptions = {
267+
url: dest.url,
268+
method: 'GET',
269+
agent: {
270+
http: gotAgent
271+
},
272+
// avoid body processing
273+
isStream: true
274+
}
303275
experiments.got = () => {
304276
return makeParallelRequests(resolve => {
305-
got.get(dest.url, { agent: { http: gotAgent } }).then(res => {
306-
res.pipe(new Writable({
307-
write (chunk, encoding, callback) {
308-
callback()
309-
}
310-
})).on('finish', resolve)
311-
}).catch(console.log)
277+
got(gotOptions).pipe(new Writable({
278+
write (chunk, encoding, callback) {
279+
callback()
280+
}
281+
})).on('finish', resolve)
312282
})
313283
}
314284

285+
const requestOptions = {
286+
url: dest.url,
287+
method: 'GET',
288+
agent: requestAgent,
289+
// avoid body toString
290+
encoding: null
291+
}
315292
experiments.request = () => {
316293
return makeParallelRequests(resolve => {
317-
request(dest.url, { agent: requestAgent }).then(res => {
318-
res.pipe(new Writable({
319-
write (chunk, encoding, callback) {
320-
callback()
321-
}
322-
})).on('finish', resolve)
294+
request(requestOptions).then(res => {
295+
// already body consumed
296+
resolve()
323297
}).catch(console.log)
324298
})
325299
}

0 commit comments

Comments
 (0)