3
3
const child_process = require ( 'child_process' ) ;
4
4
const http_benchmarkers = require ( './_http-benchmarkers.js' ) ;
5
5
6
- exports . buildType = process . features . debug ? 'Debug' : 'Release' ;
7
-
8
- exports . createBenchmark = function ( fn , configs , options ) {
9
- return new Benchmark ( fn , configs , options ) ;
10
- } ;
11
-
12
- function Benchmark ( fn , configs , options ) {
13
- // Use the file name as the name of the benchmark
14
- this . name = require . main . filename . slice ( __dirname . length + 1 ) ;
15
- // Parse job-specific configuration from the command line arguments
16
- const parsed_args = this . _parseArgs ( process . argv . slice ( 2 ) , configs ) ;
17
- this . options = parsed_args . cli ;
18
- this . extra_options = parsed_args . extra ;
19
- // The configuration list as a queue of jobs
20
- this . queue = this . _queue ( this . options ) ;
21
- // The configuration of the current job, head of the queue
22
- this . config = this . queue [ 0 ] ;
23
- // Execution arguments i.e. flags used to run the jobs
24
- this . flags = [ ] ;
25
- if ( options && options . flags ) {
26
- this . flags = this . flags . concat ( options . flags ) ;
27
- }
28
- if ( process . env . NODE_BENCHMARK_FLAGS ) {
29
- const flags = process . env . NODE_BENCHMARK_FLAGS . split ( / \s + / ) ;
30
- this . flags = this . flags . concat ( flags ) ;
31
- }
32
- // Holds process.hrtime value
33
- this . _time = [ 0 , 0 ] ;
34
- // Used to make sure a benchmark only start a timer once
35
- this . _started = false ;
36
- this . _ended = false ;
37
-
38
- // this._run will use fork() to create a new process for each configuration
39
- // combination.
40
- if ( process . env . hasOwnProperty ( 'NODE_RUN_BENCHMARK_FN' ) ) {
41
- process . nextTick ( ( ) => fn ( this . config ) ) ;
42
- } else {
43
- process . nextTick ( ( ) => this . _run ( ) ) ;
44
- }
45
- }
46
-
47
- Benchmark . prototype . _parseArgs = function ( argv , configs ) {
48
- const cliOptions = { } ;
49
- const extraOptions = { } ;
50
- const validArgRE = / ^ ( .+ ?) = ( [ \s \S ] * ) $ / ;
51
- // Parse configuration arguments
52
- for ( const arg of argv ) {
53
- const match = arg . match ( validArgRE ) ;
54
- if ( ! match ) {
55
- console . error ( `bad argument: ${ arg } ` ) ;
56
- process . exit ( 1 ) ;
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 ) ;
10
+ // Parse job-specific configuration from the command line arguments
11
+ const parsed_args = this . _parseArgs ( process . argv . slice ( 2 ) , configs ) ;
12
+ this . options = parsed_args . cli ;
13
+ this . extra_options = parsed_args . extra ;
14
+ // The configuration list as a queue of jobs
15
+ this . queue = this . _queue ( this . options ) ;
16
+ // The configuration of the current job, head of the queue
17
+ 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 ) ;
57
22
}
58
- const config = match [ 1 ] ;
59
-
60
- if ( configs [ config ] ) {
61
- // Infer the type from the config object and parse accordingly
62
- const isNumber = typeof configs [ config ] [ 0 ] === 'number' ;
63
- const value = isNumber ? + match [ 2 ] : match [ 2 ] ;
64
- if ( ! cliOptions [ config ] )
65
- cliOptions [ config ] = [ ] ;
66
- cliOptions [ config ] . push ( value ) ;
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 ) ) ;
67
37
} else {
68
- extraOptions [ config ] = match [ 2 ] ;
38
+ process . nextTick ( ( ) => this . _run ( ) ) ;
69
39
}
70
40
}
71
- return { cli : Object . assign ( { } , configs , cliOptions ) , extra : extraOptions } ;
72
- } ;
73
-
74
- Benchmark . prototype . _queue = function ( options ) {
75
- const queue = [ ] ;
76
- const keys = Object . keys ( options ) ;
77
-
78
- // Perform a depth-first walk though all options to generate a
79
- // configuration list that contains all combinations.
80
- function recursive ( keyIndex , prevConfig ) {
81
- const key = keys [ keyIndex ] ;
82
- const values = options [ key ] ;
83
- const type = typeof values [ 0 ] ;
84
41
85
- for ( const value of values ) {
86
- if ( typeof value !== 'number' && typeof value !== 'string' ) {
87
- throw new TypeError ( `configuration "${ key } " had type ${ typeof value } ` ) ;
42
+ _parseArgs ( argv , configs ) {
43
+ const cliOptions = { } ;
44
+ const extraOptions = { } ;
45
+ const validArgRE = / ^ ( .+ ?) = ( [ \s \S ] * ) $ / ;
46
+ // Parse configuration arguments
47
+ for ( const arg of argv ) {
48
+ const match = arg . match ( validArgRE ) ;
49
+ if ( ! match ) {
50
+ console . error ( `bad argument: ${ arg } ` ) ;
51
+ process . exit ( 1 ) ;
88
52
}
89
- if ( typeof value !== type ) {
90
- // This is a requirement for being able to consistently and predictably
91
- // parse CLI provided configuration values.
92
- throw new TypeError ( `configuration "${ key } " has mixed types` ) ;
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 ) ;
62
+ } else {
63
+ extraOptions [ config ] = match [ 2 ] ;
93
64
}
65
+ }
66
+ return { cli : Object . assign ( { } , configs , cliOptions ) , extra : extraOptions } ;
67
+ }
94
68
95
- const currConfig = Object . assign ( { [ key ] : value } , prevConfig ) ;
96
-
97
- if ( keyIndex + 1 < keys . length ) {
98
- recursive ( keyIndex + 1 , currConfig ) ;
99
- } else {
100
- queue . push ( currConfig ) ;
69
+ _queue ( options ) {
70
+ const queue = [ ] ;
71
+ const keys = Object . keys ( options ) ;
72
+
73
+ // Perform a depth-first walk though all options to generate a
74
+ // configuration list that contains all combinations.
75
+ function recursive ( keyIndex , prevConfig ) {
76
+ const key = keys [ keyIndex ] ;
77
+ const values = options [ key ] ;
78
+ const type = typeof values [ 0 ] ;
79
+
80
+ for ( const value of values ) {
81
+ if ( typeof value !== 'number' && typeof value !== 'string' ) {
82
+ throw new TypeError (
83
+ `configuration "${ key } " had type ${ typeof value } ` ) ;
84
+ }
85
+ if ( typeof value !== type ) {
86
+ // This is a requirement for being able to consistently and
87
+ // predictably parse CLI provided configuration values.
88
+ throw new TypeError ( `configuration "${ key } " has mixed types` ) ;
89
+ }
90
+
91
+ const currConfig = Object . assign ( { [ key ] : value } , prevConfig ) ;
92
+
93
+ if ( keyIndex + 1 < keys . length ) {
94
+ recursive ( keyIndex + 1 , currConfig ) ;
95
+ } else {
96
+ queue . push ( currConfig ) ;
97
+ }
101
98
}
102
99
}
100
+
101
+ if ( keys . length > 0 ) {
102
+ recursive ( 0 , { } ) ;
103
+ } else {
104
+ queue . push ( { } ) ;
105
+ }
106
+
107
+ return queue ;
103
108
}
104
109
105
- if ( keys . length > 0 ) {
106
- recursive ( 0 , { } ) ;
107
- } else {
108
- queue . push ( { } ) ;
110
+ http ( options , cb ) {
111
+ const self = this ;
112
+ const http_options = Object . assign ( { } , options ) ;
113
+ http_options . benchmarker = http_options . benchmarker ||
114
+ self . config . benchmarker ||
115
+ self . extra_options . benchmarker ||
116
+ exports . default_http_benchmarker ;
117
+ http_benchmarkers . run (
118
+ http_options , ( error , code , used_benchmarker , result , elapsed ) => {
119
+ if ( cb ) {
120
+ cb ( code ) ;
121
+ }
122
+ if ( error ) {
123
+ console . error ( error ) ;
124
+ process . exit ( code || 1 ) ;
125
+ }
126
+ self . config . benchmarker = used_benchmarker ;
127
+ self . report ( result , elapsed ) ;
128
+ }
129
+ ) ;
109
130
}
110
131
111
- return queue ;
112
- } ;
132
+ _run ( ) {
133
+ const self = this ;
134
+ // If forked, report to the parent.
135
+ if ( process . send ) {
136
+ process . send ( {
137
+ type : 'config' ,
138
+ name : this . name ,
139
+ queueLength : this . queue . length ,
140
+ } ) ;
141
+ }
142
+
143
+ ( function recursive ( queueIndex ) {
144
+ const config = self . queue [ queueIndex ] ;
113
145
114
- // Benchmark an http server.
115
- exports . default_http_benchmarker =
116
- http_benchmarkers . default_http_benchmarker ;
117
- exports . PORT = http_benchmarkers . PORT ;
118
-
119
- Benchmark . prototype . http = function ( options , cb ) {
120
- const self = this ;
121
- const http_options = Object . assign ( { } , options ) ;
122
- http_options . benchmarker = http_options . benchmarker ||
123
- self . config . benchmarker ||
124
- self . extra_options . benchmarker ||
125
- exports . default_http_benchmarker ;
126
- http_benchmarkers . run (
127
- http_options , ( error , code , used_benchmarker , result , elapsed ) => {
128
- if ( cb ) {
129
- cb ( code ) ;
146
+ // Set NODE_RUN_BENCHMARK_FN to indicate that the child shouldn't
147
+ // construct a configuration queue, but just execute the benchmark
148
+ // function.
149
+ const childEnv = Object . assign ( { } , process . env ) ;
150
+ childEnv . NODE_RUN_BENCHMARK_FN = '' ;
151
+
152
+ // Create configuration arguments
153
+ const childArgs = [ ] ;
154
+ for ( const key of Object . keys ( config ) ) {
155
+ childArgs . push ( `${ key } =${ config [ key ] } ` ) ;
130
156
}
131
- if ( error ) {
132
- console . error ( error ) ;
133
- process . exit ( code || 1 ) ;
157
+ for ( const key of Object . keys ( self . extra_options ) ) {
158
+ childArgs . push ( `${ key } =${ self . extra_options [ key ] } ` ) ;
134
159
}
135
- self . config . benchmarker = used_benchmarker ;
136
- self . report ( result , elapsed ) ;
137
- }
138
- ) ;
139
- } ;
140
160
141
- Benchmark . prototype . _run = function ( ) {
142
- const self = this ;
143
- // If forked, report to the parent.
144
- if ( process . send ) {
145
- process . send ( {
146
- type : 'config' ,
147
- name : this . name ,
148
- queueLength : this . queue . length ,
149
- } ) ;
161
+ const child = child_process . fork ( require . main . filename , childArgs , {
162
+ env : childEnv ,
163
+ execArgv : self . flags . concat ( process . execArgv ) ,
164
+ } ) ;
165
+ child . on ( 'message' , sendResult ) ;
166
+ child . on ( 'close' , ( code ) => {
167
+ if ( code ) {
168
+ process . exit ( code ) ;
169
+ }
170
+
171
+ if ( queueIndex + 1 < self . queue . length ) {
172
+ recursive ( queueIndex + 1 ) ;
173
+ }
174
+ } ) ;
175
+ } ) ( 0 ) ;
150
176
}
151
177
152
- ( function recursive ( queueIndex ) {
153
- const config = self . queue [ queueIndex ] ;
178
+ start ( ) {
179
+ if ( this . _started ) {
180
+ throw new Error ( 'Called start more than once in a single benchmark' ) ;
181
+ }
182
+ this . _started = true ;
183
+ this . _time = process . hrtime ( ) ;
184
+ }
154
185
155
- // Set NODE_RUN_BENCHMARK_FN to indicate that the child shouldn't construct
156
- // a configuration queue, but just execute the benchmark function.
157
- const childEnv = Object . assign ( { } , process . env ) ;
158
- childEnv . NODE_RUN_BENCHMARK_FN = '' ;
186
+ end ( operations ) {
187
+ // Get elapsed time now and do error checking later for accuracy.
188
+ const elapsed = process . hrtime ( this . _time ) ;
159
189
160
- // Create configuration arguments
161
- const childArgs = [ ] ;
162
- for ( const key of Object . keys ( config ) ) {
163
- childArgs . push ( `${ key } =${ config [ key ] } ` ) ;
190
+ if ( ! this . _started ) {
191
+ throw new Error ( 'called end without start' ) ;
164
192
}
165
- for ( const key of Object . keys ( self . extra_options ) ) {
166
- childArgs . push ( `${ key } =${ self . extra_options [ key ] } ` ) ;
193
+ if ( this . _ended ) {
194
+ throw new Error ( 'called end multiple times' ) ;
195
+ }
196
+ if ( typeof operations !== 'number' ) {
197
+ throw new Error ( 'called end() without specifying operation count' ) ;
198
+ }
199
+ if ( ! process . env . NODEJS_BENCHMARK_ZERO_ALLOWED && operations <= 0 ) {
200
+ throw new Error ( 'called end() with operation count <= 0' ) ;
201
+ }
202
+ if ( elapsed [ 0 ] === 0 && elapsed [ 1 ] === 0 ) {
203
+ if ( ! process . env . NODEJS_BENCHMARK_ZERO_ALLOWED )
204
+ throw new Error ( 'insufficient clock precision for short benchmark' ) ;
205
+ // Avoid dividing by zero
206
+ elapsed [ 1 ] = 1 ;
167
207
}
168
208
169
- const child = child_process . fork ( require . main . filename , childArgs , {
170
- env : childEnv ,
171
- execArgv : self . flags . concat ( process . execArgv ) ,
172
- } ) ;
173
- child . on ( 'message' , sendResult ) ;
174
- child . on ( 'close' , ( code ) => {
175
- if ( code ) {
176
- process . exit ( code ) ;
177
- }
178
-
179
- if ( queueIndex + 1 < self . queue . length ) {
180
- recursive ( queueIndex + 1 ) ;
181
- }
182
- } ) ;
183
- } ) ( 0 ) ;
184
- } ;
185
-
186
- Benchmark . prototype . start = function ( ) {
187
- if ( this . _started ) {
188
- throw new Error ( 'Called start more than once in a single benchmark' ) ;
209
+ this . _ended = true ;
210
+ const time = elapsed [ 0 ] + elapsed [ 1 ] / 1e9 ;
211
+ const rate = operations / time ;
212
+ this . report ( rate , elapsed ) ;
189
213
}
190
- this . _started = true ;
191
- this . _time = process . hrtime ( ) ;
192
- } ;
193
214
194
- Benchmark . prototype . end = function ( operations ) {
195
- // Get elapsed time now and do error checking later for accuracy.
196
- const elapsed = process . hrtime ( this . _time ) ;
197
-
198
- if ( ! this . _started ) {
199
- throw new Error ( 'called end without start' ) ;
200
- }
201
- if ( this . _ended ) {
202
- throw new Error ( 'called end multiple times' ) ;
203
- }
204
- if ( typeof operations !== 'number' ) {
205
- throw new Error ( 'called end() without specifying operation count' ) ;
206
- }
207
- if ( ! process . env . NODEJS_BENCHMARK_ZERO_ALLOWED && operations <= 0 ) {
208
- throw new Error ( 'called end() with operation count <= 0' ) ;
209
- }
210
- if ( elapsed [ 0 ] === 0 && elapsed [ 1 ] === 0 ) {
211
- if ( ! process . env . NODEJS_BENCHMARK_ZERO_ALLOWED )
212
- throw new Error ( 'insufficient clock precision for short benchmark' ) ;
213
- // Avoid dividing by zero
214
- elapsed [ 1 ] = 1 ;
215
+ report ( rate , elapsed ) {
216
+ sendResult ( {
217
+ name : this . name ,
218
+ conf : this . config ,
219
+ rate : rate ,
220
+ time : elapsed [ 0 ] + elapsed [ 1 ] / 1e9 ,
221
+ type : 'report' ,
222
+ } ) ;
215
223
}
216
-
217
- this . _ended = true ;
218
- const time = elapsed [ 0 ] + elapsed [ 1 ] / 1e9 ;
219
- const rate = operations / time ;
220
- this . report ( rate , elapsed ) ;
221
- } ;
224
+ }
222
225
223
226
function formatResult ( data ) {
224
227
// Construct configuration string, " A=a, B=b, ..."
@@ -242,27 +245,6 @@ function sendResult(data) {
242
245
console . log ( formatResult ( data ) ) ;
243
246
}
244
247
}
245
- exports . sendResult = sendResult ;
246
-
247
- Benchmark . prototype . report = function ( rate , elapsed ) {
248
- sendResult ( {
249
- name : this . name ,
250
- conf : this . config ,
251
- rate : rate ,
252
- time : elapsed [ 0 ] + elapsed [ 1 ] / 1e9 ,
253
- type : 'report' ,
254
- } ) ;
255
- } ;
256
-
257
- exports . binding = function ( bindingName ) {
258
- try {
259
- const { internalBinding } = require ( 'internal/test/binding' ) ;
260
-
261
- return internalBinding ( bindingName ) ;
262
- } catch {
263
- return process . binding ( bindingName ) ;
264
- }
265
- } ;
266
248
267
249
const urls = {
268
250
long : 'http://nodejs.org:89/docs/latest/api/foo/bar/qua/13949281/0f28b/' +
@@ -278,7 +260,6 @@ const urls = {
278
260
percent : 'https://%E4%BD%A0/foo' ,
279
261
dot : 'https://example.org/./a/../b/./c' ,
280
262
} ;
281
- exports . urls = urls ;
282
263
283
264
const searchParams = {
284
265
noencode : 'foo=bar&baz=quux&xyzzy=thud' ,
@@ -293,7 +274,6 @@ const searchParams = {
293
274
manyblankpairs : '&&&&&&&&&&&&&&&&&&&&&&&&' ,
294
275
altspaces : 'foo+bar=baz+quux&xyzzy+thud=quuy+quuz&abc=def+ghi' ,
295
276
} ;
296
- exports . searchParams = searchParams ;
297
277
298
278
function getUrlData ( withBase ) {
299
279
const data = require ( '../test/fixtures/wpt/url/resources/urltestdata.json' ) ;
@@ -309,8 +289,6 @@ function getUrlData(withBase) {
309
289
return result ;
310
290
}
311
291
312
- exports . urlDataTypes = Object . keys ( urls ) . concat ( [ 'wpt' ] ) ;
313
-
314
292
/**
315
293
* Generate an array of data for URL benchmarks to use.
316
294
* The size of the resulting data set is the original data size * 2 ** `e`.
@@ -354,4 +332,27 @@ function bakeUrlData(type, e = 0, withBase = false, asUrl = false) {
354
332
}
355
333
return result ;
356
334
}
357
- exports . bakeUrlData = bakeUrlData ;
335
+
336
+ module . exports = {
337
+ PORT : http_benchmarkers . PORT ,
338
+ bakeUrlData,
339
+ binding ( bindingName ) {
340
+ try {
341
+ const { internalBinding } = require ( 'internal/test/binding' ) ;
342
+
343
+ return internalBinding ( bindingName ) ;
344
+ } catch {
345
+ return process . binding ( bindingName ) ;
346
+ }
347
+ } ,
348
+ buildType : process . features . debug ? 'Debug' : 'Release' ,
349
+ createBenchmark ( fn , configs , options ) {
350
+ return new Benchmark ( fn , configs , options ) ;
351
+ } ,
352
+ // Benchmark an http server.
353
+ default_http_benchmarker : http_benchmarkers . default_http_benchmarker ,
354
+ sendResult,
355
+ searchParams,
356
+ urlDataTypes : Object . keys ( urls ) . concat ( [ 'wpt' ] ) ,
357
+ urls,
358
+ } ;
0 commit comments