@@ -17,10 +17,10 @@ import ansi = require('ansi-escape-sequences');
17
17
18
18
import { jsonOutput , legacyJsonOutput } from './json-output' ;
19
19
import { browserSignature , makeDriver , openAndSwitchToNewTab } from './browser' ;
20
- import { measure } from './measure' ;
20
+ import { measure , measurementName } from './measure' ;
21
21
import { BenchmarkResult , BenchmarkSpec } from './types' ;
22
22
import { formatCsvStats , formatCsvRaw } from './csv' ;
23
- import { ResultStats , ResultStatsWithDifferences , horizonsResolved , summaryStats , computeDifferences } from './stats' ;
23
+ import { ResultStatsWithDifferences , horizonsResolved , summaryStats , computeDifferences } from './stats' ;
24
24
import { verticalTermResultTable , horizontalTermResultTable , verticalHtmlResultTable , horizontalHtmlResultTable , automaticResultTable , spinner , benchmarkOneLiner } from './format' ;
25
25
import { Config } from './config' ;
26
26
import * as github from './github' ;
@@ -41,7 +41,7 @@ export class AutomaticMode {
41
41
private browsers = new Map < string , Browser > ( ) ;
42
42
private bar : ProgressBar ;
43
43
private completeGithubCheck ?: ( markdown : string ) => void ;
44
- private specResults = new Map < BenchmarkSpec , BenchmarkResult [ ] > ( ) ;
44
+ private results = new Map < BenchmarkSpec , BenchmarkResult [ ] > ( ) ;
45
45
private hitTimeout = false ;
46
46
47
47
constructor ( config : Config , servers : Map < BenchmarkSpec , Server > ) {
@@ -62,9 +62,6 @@ export class AutomaticMode {
62
62
}
63
63
console . log ( 'Running benchmarks\n' ) ;
64
64
await this . warmup ( ) ;
65
- for ( const spec of this . specs ) {
66
- this . specResults . set ( spec , [ ] ) ;
67
- }
68
65
await this . takeMinimumSamples ( ) ;
69
66
await this . takeAdditionalSamples ( ) ;
70
67
await this . closeBrowsers ( ) ;
@@ -111,22 +108,39 @@ export class AutomaticMode {
111
108
bar . tick ( 0 , {
112
109
status : `warmup ${ i + 1 } /${ specs . length } ${ benchmarkOneLiner ( spec ) } ` ,
113
110
} ) ;
114
- await this . takeSample ( spec ) ;
111
+ await this . takeSamples ( spec ) ;
115
112
bar . tick ( 1 ) ;
116
113
}
117
114
}
118
115
116
+ private recordSamples ( spec : BenchmarkSpec , newResults : BenchmarkResult [ ] ) {
117
+ let specResults = this . results . get ( spec ) ;
118
+ if ( specResults === undefined ) {
119
+ specResults = [ ] ;
120
+ this . results . set ( spec , specResults ) ;
121
+ }
122
+
123
+ for ( const newResult of newResults ) {
124
+ const primary = specResults [ newResult . measurementIdx ] ;
125
+ if ( primary === undefined ) {
126
+ specResults [ newResult . measurementIdx ] = newResult ;
127
+ } else {
128
+ primary . millis . push ( ...newResult . millis ) ;
129
+ }
130
+ }
131
+ }
132
+
119
133
private async takeMinimumSamples ( ) {
120
134
// Always collect our minimum number of samples.
121
- const { config, specs, bar, specResults } = this ;
135
+ const { config, specs, bar} = this ;
122
136
const numRuns = specs . length * config . sampleSize ;
123
137
let run = 0 ;
124
138
for ( let sample = 0 ; sample < config . sampleSize ; sample ++ ) {
125
139
for ( const spec of specs ) {
126
140
bar . tick ( 0 , {
127
141
status : `${ ++ run } /${ numRuns } ${ benchmarkOneLiner ( spec ) } ` ,
128
142
} ) ;
129
- specResults . get ( spec ) ! . push ( await this . takeSample ( spec ) ) ;
143
+ this . recordSamples ( spec , await this . takeSamples ( spec ) ) ;
130
144
if ( bar . curr === bar . total - 1 ) {
131
145
// Note if we tick with 0 after we've completed, the status is
132
146
// rendered on the next line for some reason.
@@ -139,7 +153,7 @@ export class AutomaticMode {
139
153
}
140
154
141
155
private async takeAdditionalSamples ( ) {
142
- const { config, specs, specResults } = this ;
156
+ const { config, specs} = this ;
143
157
if ( config . timeout > 0 ) {
144
158
console . log ( ) ;
145
159
const timeoutMs = config . timeout * 60 * 1000 ; // minutes -> millis
@@ -170,14 +184,14 @@ export class AutomaticMode {
170
184
process . stdout . write (
171
185
`\r${ spinner [ run % spinner . length ] } Auto-sample ${ sample } ` +
172
186
`(timeout in ${ mins } m${ secs } s)` + ansi . erase . inLine ( 0 ) ) ;
173
- specResults . get ( spec ) ! . push ( await this . takeSample ( spec ) ) ;
187
+ this . recordSamples ( spec , await this . takeSamples ( spec ) ) ;
174
188
}
175
189
}
176
190
}
177
191
}
178
192
}
179
193
180
- private async takeSample ( spec : BenchmarkSpec ) : Promise < BenchmarkResult > {
194
+ private async takeSamples ( spec : BenchmarkSpec ) : Promise < BenchmarkResult [ ] > {
181
195
const { servers, config, browsers} = this ;
182
196
183
197
let server ;
@@ -192,19 +206,31 @@ export class AutomaticMode {
192
206
const { driver, initialTabHandle} =
193
207
browsers . get ( browserSignature ( spec . browser ) ) ! ;
194
208
195
- let millis : number | undefined ;
196
209
let bytesSent = 0 ;
197
210
let userAgent = '' ;
198
211
// TODO(aomarks) Make maxAttempts and timeouts configurable.
199
212
const maxAttempts = 3 ;
213
+ const measurements = spec . measurement ;
214
+ let millis : number [ ] ;
215
+ let numPending : number ;
200
216
for ( let attempt = 1 ; ; attempt ++ ) {
217
+ millis = [ ] ;
218
+ numPending = measurements . length ;
201
219
await openAndSwitchToNewTab ( driver , spec . browser ) ;
202
220
await driver . get ( url ) ;
203
- for ( let waited = 0 ; millis === undefined && waited <= 10000 ;
204
- waited += 50 ) {
221
+ for ( let waited = 0 ; numPending > 0 && waited <= 10000 ; waited += 50 ) {
205
222
// TODO(aomarks) You don't have to wait in callback mode!
206
223
await wait ( 50 ) ;
207
- millis = await measure ( driver , spec . measurement , server ) ;
224
+ for ( let i = 0 ; i < measurements . length ; i ++ ) {
225
+ if ( millis [ i ] !== undefined ) {
226
+ continue ;
227
+ }
228
+ const result = await measure ( driver , measurements [ i ] , server ) ;
229
+ if ( result !== undefined ) {
230
+ millis [ i ] = result ;
231
+ numPending -- ;
232
+ }
233
+ }
208
234
}
209
235
210
236
// Close the active tab (but not the whole browser, since the
@@ -218,7 +244,7 @@ export class AutomaticMode {
218
244
userAgent = session . userAgent ;
219
245
}
220
246
221
- if ( millis !== undefined || attempt >= maxAttempts ) {
247
+ if ( numPending === 0 || attempt >= maxAttempts ) {
222
248
break ;
223
249
}
224
250
@@ -228,44 +254,40 @@ export class AutomaticMode {
228
254
`in ${ spec . browser . name } from ${ url } . Retrying.` ) ;
229
255
}
230
256
231
- if ( millis === undefined ) {
257
+ if ( numPending > 0 ) {
232
258
console . log ( ) ;
233
259
throw new Error (
234
260
`\n\nFailed ${ maxAttempts } /${ maxAttempts } times ` +
235
261
`to get a measurement ` +
236
262
`in ${ spec . browser . name } from ${ url } . Retrying.` ) ;
237
263
}
238
264
239
- return {
240
- name : spec . name ,
241
- queryString : spec . url . kind === 'local' ? spec . url . queryString : '' ,
242
- version : spec . url . kind === 'local' && spec . url . version !== undefined ?
243
- spec . url . version . label :
244
- '' ,
245
- millis : [ millis ] ,
246
- bytesSent,
247
- browser : spec . browser ,
248
- userAgent,
249
- } ;
265
+ return measurements . map (
266
+ ( measurement , measurementIdx ) => ( {
267
+ name : measurements . length === 1 ?
268
+ spec . name :
269
+ `${ spec . name } [${ measurementName ( measurement ) } ]` ,
270
+ measurementIdx,
271
+ queryString : spec . url . kind === 'local' ? spec . url . queryString : '' ,
272
+ version : spec . url . kind === 'local' && spec . url . version !== undefined ?
273
+ spec . url . version . label :
274
+ '' ,
275
+ millis : [ millis [ measurementIdx ] ] ,
276
+ bytesSent,
277
+ browser : spec . browser ,
278
+ userAgent,
279
+ } ) ) ;
250
280
}
251
281
252
282
makeResults ( ) {
253
- const results : BenchmarkResult [ ] = [ ] ;
254
- for ( const sr of this . specResults . values ( ) ) {
255
- const combined : BenchmarkResult = {
256
- ...sr [ 0 ] ,
257
- millis : [ ] ,
258
- } ;
259
- for ( const result of sr ) {
260
- combined . millis . push ( ...result . millis ) ;
283
+ const resultStats = [ ] ;
284
+ for ( const results of this . results . values ( ) ) {
285
+ for ( let r = 0 ; r < results . length ; r ++ ) {
286
+ const result = results [ r ] ;
287
+ resultStats . push ( { result, stats : summaryStats ( result . millis ) } ) ;
261
288
}
262
- results . push ( combined ) ;
263
289
}
264
- const withStats = results . map ( ( result ) : ResultStats => ( {
265
- result,
266
- stats : summaryStats ( result . millis ) ,
267
- } ) ) ;
268
- return computeDifferences ( withStats ) ;
290
+ return computeDifferences ( resultStats ) ;
269
291
}
270
292
271
293
private async outputResults ( withDifferences : ResultStatsWithDifferences [ ] ) {
0 commit comments