@@ -4,43 +4,83 @@ const child_process = require('child_process');
4
4
const http_benchmarkers = require ( './_http-benchmarkers.js' ) ;
5
5
6
6
class Benchmark {
7
- constructor ( fn , configs , options ) {
8
- // Use the file name as the name of the benchmark
9
- this . name = require . main . filename . slice ( __dirname . length + 1 ) ;
7
+ // Used to make sure a benchmark only start a timer once
8
+ #started = false ;
9
+
10
+ // Indicate that the benchmark ended
11
+ #ended = false ;
12
+
13
+ // Holds process.hrtime value
14
+ #time = [ 0 , 0 ] ;
15
+
16
+ // Use the file name as the name of the benchmark
17
+ name = require . main . filename . slice ( __dirname . length + 1 ) ;
18
+
19
+ // Execution arguments i.e. flags used to run the jobs
20
+ flags = process . env . NODE_BENCHMARK_FLAGS ?
21
+ process . env . NODE_BENCHMARK_FLAGS . split ( / \s + / ) :
22
+ [ ] ;
23
+
24
+ constructor ( fn , configs , options = { } ) {
10
25
// Parse job-specific configuration from the command line arguments
11
- const parsed_args = this . _parseArgs ( process . argv . slice ( 2 ) , configs ) ;
26
+ const argv = process . argv . slice ( 2 ) ;
27
+ const parsed_args = this . _parseArgs ( argv , configs , options ) ;
12
28
this . options = parsed_args . cli ;
13
29
this . extra_options = parsed_args . extra ;
30
+ if ( options . flags ) {
31
+ this . flags = this . flags . concat ( options . flags ) ;
32
+ }
33
+
14
34
// The configuration list as a queue of jobs
15
35
this . queue = this . _queue ( this . options ) ;
36
+
16
37
// The configuration of the current job, head of the queue
17
38
this . config = this . queue [ 0 ] ;
18
- // Execution arguments i.e. flags used to run the jobs
19
- this . flags = [ ] ;
20
- if ( options && options . flags ) {
21
- this . flags = this . flags . concat ( options . flags ) ;
22
- }
23
- if ( process . env . NODE_BENCHMARK_FLAGS ) {
24
- const flags = process . env . NODE_BENCHMARK_FLAGS . split ( / \s + / ) ;
25
- this . flags = this . flags . concat ( flags ) ;
26
- }
27
- // Holds process.hrtime value
28
- this . _time = [ 0 , 0 ] ;
29
- // Used to make sure a benchmark only start a timer once
30
- this . _started = false ;
31
- this . _ended = false ;
32
-
33
- // this._run will use fork() to create a new process for each configuration
34
- // combination.
35
- if ( process . env . hasOwnProperty ( 'NODE_RUN_BENCHMARK_FN' ) ) {
36
- process . nextTick ( ( ) => fn ( this . config ) ) ;
37
- } else {
38
- process . nextTick ( ( ) => this . _run ( ) ) ;
39
- }
39
+
40
+ process . nextTick ( ( ) => {
41
+ if ( process . env . hasOwnProperty ( 'NODE_RUN_BENCHMARK_FN' ) ) {
42
+ fn ( this . config ) ;
43
+ } else {
44
+ // _run will use fork() to create a new process for each configuration
45
+ // combination.
46
+ this . _run ( ) ;
47
+ }
48
+ } ) ;
40
49
}
41
50
42
- _parseArgs ( argv , configs ) {
51
+ _parseArgs ( argv , configs , options ) {
43
52
const cliOptions = { } ;
53
+
54
+ // Check for the test mode first.
55
+ const testIndex = argv . indexOf ( '--test' ) ;
56
+ if ( testIndex !== - 1 ) {
57
+ for ( const [ key , rawValue ] of Object . entries ( configs ) ) {
58
+ let value = Array . isArray ( rawValue ) ? rawValue [ 0 ] : rawValue ;
59
+ // Set numbers to one by default to reduce the runtime.
60
+ if ( typeof value === 'number' ) {
61
+ if ( key === 'dur' || key === 'duration' ) {
62
+ value = 0.05 ;
63
+ } else if ( value > 1 ) {
64
+ value = 1 ;
65
+ }
66
+ }
67
+ cliOptions [ key ] = [ value ] ;
68
+ }
69
+ // Override specific test options.
70
+ if ( options . test ) {
71
+ for ( const [ key , value ] of Object . entries ( options . test ) ) {
72
+ cliOptions [ key ] = Array . isArray ( value ) ? value : [ value ] ;
73
+ }
74
+ }
75
+ argv . splice ( testIndex , 1 ) ;
76
+ } else {
77
+ // Accept single values instead of arrays.
78
+ for ( const [ key , value ] of Object . entries ( configs ) ) {
79
+ if ( ! Array . isArray ( value ) )
80
+ configs [ key ] = [ value ] ;
81
+ }
82
+ }
83
+
44
84
const extraOptions = { } ;
45
85
const validArgRE = / ^ ( .+ ?) = ( [ \s \S ] * ) $ / ;
46
86
// Parse configuration arguments
@@ -50,45 +90,43 @@ class Benchmark {
50
90
console . error ( `bad argument: ${ arg } ` ) ;
51
91
process . exit ( 1 ) ;
52
92
}
53
- const config = match [ 1 ] ;
54
-
55
- if ( configs [ config ] ) {
56
- // Infer the type from the config object and parse accordingly
57
- const isNumber = typeof configs [ config ] [ 0 ] === 'number' ;
58
- const value = isNumber ? + match [ 2 ] : match [ 2 ] ;
59
- if ( ! cliOptions [ config ] )
60
- cliOptions [ config ] = [ ] ;
61
- cliOptions [ config ] . push ( value ) ;
93
+ const [ , key , value ] = match ;
94
+ if ( Object . prototype . hasOwnProperty . call ( configs , key ) ) {
95
+ if ( ! cliOptions [ key ] )
96
+ cliOptions [ key ] = [ ] ;
97
+ cliOptions [ key ] . push (
98
+ // Infer the type from the config object and parse accordingly
99
+ typeof configs [ key ] [ 0 ] === 'number' ? + value : value
100
+ ) ;
62
101
} else {
63
- extraOptions [ config ] = match [ 2 ] ;
102
+ extraOptions [ key ] = value ;
64
103
}
65
104
}
66
- return { cli : Object . assign ( { } , configs , cliOptions ) , extra : extraOptions } ;
105
+ return { cli : { ... configs , ... cliOptions } , extra : extraOptions } ;
67
106
}
68
107
69
108
_queue ( options ) {
70
109
const queue = [ ] ;
71
110
const keys = Object . keys ( options ) ;
72
111
73
- // Perform a depth-first walk though all options to generate a
112
+ // Perform a depth-first walk through all options to generate a
74
113
// configuration list that contains all combinations.
75
114
function recursive ( keyIndex , prevConfig ) {
76
115
const key = keys [ keyIndex ] ;
77
116
const values = options [ key ] ;
78
- const type = typeof values [ 0 ] ;
79
117
80
118
for ( const value of values ) {
81
119
if ( typeof value !== 'number' && typeof value !== 'string' ) {
82
120
throw new TypeError (
83
121
`configuration "${ key } " had type ${ typeof value } ` ) ;
84
122
}
85
- if ( typeof value !== type ) {
123
+ if ( typeof value !== typeof values [ 0 ] ) {
86
124
// This is a requirement for being able to consistently and
87
125
// predictably parse CLI provided configuration values.
88
126
throw new TypeError ( `configuration "${ key } " has mixed types` ) ;
89
127
}
90
128
91
- const currConfig = Object . assign ( { [ key ] : value } , prevConfig ) ;
129
+ const currConfig = { [ key ] : value , ... prevConfig } ;
92
130
93
131
if ( keyIndex + 1 < keys . length ) {
94
132
recursive ( keyIndex + 1 , currConfig ) ;
@@ -108,12 +146,11 @@ class Benchmark {
108
146
}
109
147
110
148
http ( options , cb ) {
111
- const self = this ;
112
- const http_options = Object . assign ( { } , options ) ;
149
+ const http_options = { ...options } ;
113
150
http_options . benchmarker = http_options . benchmarker ||
114
- self . config . benchmarker ||
115
- self . extra_options . benchmarker ||
116
- exports . default_http_benchmarker ;
151
+ this . config . benchmarker ||
152
+ this . extra_options . benchmarker ||
153
+ http_benchmarkers . default_http_benchmarker ;
117
154
http_benchmarkers . run (
118
155
http_options , ( error , code , used_benchmarker , result , elapsed ) => {
119
156
if ( cb ) {
@@ -123,14 +160,13 @@ class Benchmark {
123
160
console . error ( error ) ;
124
161
process . exit ( code || 1 ) ;
125
162
}
126
- self . config . benchmarker = used_benchmarker ;
127
- self . report ( result , elapsed ) ;
163
+ this . config . benchmarker = used_benchmarker ;
164
+ this . report ( result , elapsed ) ;
128
165
}
129
166
) ;
130
167
}
131
168
132
169
_run ( ) {
133
- const self = this ;
134
170
// If forked, report to the parent.
135
171
if ( process . send ) {
136
172
process . send ( {
@@ -140,57 +176,59 @@ class Benchmark {
140
176
} ) ;
141
177
}
142
178
143
- ( function recursive ( queueIndex ) {
144
- const config = self . queue [ queueIndex ] ;
179
+ const recursive = ( queueIndex ) => {
180
+ const config = this . queue [ queueIndex ] ;
145
181
146
182
// Set NODE_RUN_BENCHMARK_FN to indicate that the child shouldn't
147
183
// construct a configuration queue, but just execute the benchmark
148
184
// function.
149
- const childEnv = Object . assign ( { } , process . env ) ;
185
+ const childEnv = { ... process . env } ;
150
186
childEnv . NODE_RUN_BENCHMARK_FN = '' ;
151
187
152
188
// Create configuration arguments
153
189
const childArgs = [ ] ;
154
- for ( const key of Object . keys ( config ) ) {
155
- childArgs . push ( `${ key } =${ config [ key ] } ` ) ;
190
+ for ( const [ key , value ] of Object . entries ( config ) ) {
191
+ childArgs . push ( `${ key } =${ value } ` ) ;
156
192
}
157
- for ( const key of Object . keys ( self . extra_options ) ) {
158
- childArgs . push ( `${ key } =${ self . extra_options [ key ] } ` ) ;
193
+ for ( const [ key , value ] of Object . entries ( this . extra_options ) ) {
194
+ childArgs . push ( `${ key } =${ value } ` ) ;
159
195
}
160
196
161
197
const child = child_process . fork ( require . main . filename , childArgs , {
162
198
env : childEnv ,
163
- execArgv : self . flags . concat ( process . execArgv ) ,
199
+ execArgv : this . flags . concat ( process . execArgv ) ,
164
200
} ) ;
165
201
child . on ( 'message' , sendResult ) ;
166
202
child . on ( 'close' , ( code ) => {
167
203
if ( code ) {
168
204
process . exit ( code ) ;
169
205
}
170
206
171
- if ( queueIndex + 1 < self . queue . length ) {
207
+ if ( queueIndex + 1 < this . queue . length ) {
172
208
recursive ( queueIndex + 1 ) ;
173
209
}
174
210
} ) ;
175
- } ) ( 0 ) ;
211
+ } ;
212
+
213
+ recursive ( 0 ) ;
176
214
}
177
215
178
216
start ( ) {
179
- if ( this . _started ) {
217
+ if ( this . #started ) {
180
218
throw new Error ( 'Called start more than once in a single benchmark' ) ;
181
219
}
182
- this . _started = true ;
183
- this . _time = process . hrtime ( ) ;
220
+ this . #started = true ;
221
+ this . #time = process . hrtime ( ) ;
184
222
}
185
223
186
224
end ( operations ) {
187
225
// Get elapsed time now and do error checking later for accuracy.
188
- const elapsed = process . hrtime ( this . _time ) ;
226
+ const elapsed = process . hrtime ( this . #time ) ;
189
227
190
- if ( ! this . _started ) {
228
+ if ( ! this . #started ) {
191
229
throw new Error ( 'called end without start' ) ;
192
230
}
193
- if ( this . _ended ) {
231
+ if ( this . #ended ) {
194
232
throw new Error ( 'called end multiple times' ) ;
195
233
}
196
234
if ( typeof operations !== 'number' ) {
@@ -206,7 +244,7 @@ class Benchmark {
206
244
elapsed [ 1 ] = 1 ;
207
245
}
208
246
209
- this . _ended = true ;
247
+ this . #ended = true ;
210
248
const time = elapsed [ 0 ] + elapsed [ 1 ] / 1e9 ;
211
249
const rate = operations / time ;
212
250
this . report ( rate , elapsed ) ;
@@ -216,7 +254,7 @@ class Benchmark {
216
254
sendResult ( {
217
255
name : this . name ,
218
256
conf : this . config ,
219
- rate : rate ,
257
+ rate,
220
258
time : elapsed [ 0 ] + elapsed [ 1 ] / 1e9 ,
221
259
type : 'report' ,
222
260
} ) ;
@@ -334,6 +372,7 @@ function bakeUrlData(type, e = 0, withBase = false, asUrl = false) {
334
372
}
335
373
336
374
module . exports = {
375
+ Benchmark,
337
376
PORT : http_benchmarkers . PORT ,
338
377
bakeUrlData,
339
378
binding ( bindingName ) {
@@ -349,8 +388,6 @@ module.exports = {
349
388
createBenchmark ( fn , configs , options ) {
350
389
return new Benchmark ( fn , configs , options ) ;
351
390
} ,
352
- // Benchmark an http server.
353
- default_http_benchmarker : http_benchmarkers . default_http_benchmarker ,
354
391
sendResult,
355
392
searchParams,
356
393
urlDataTypes : Object . keys ( urls ) . concat ( [ 'wpt' ] ) ,
0 commit comments