Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/testing/performancetimer.js
4050 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Performance timer.
9
*
10
* {@see goog.testing.benchmark} for an easy way to use this functionality.
11
*/
12
13
goog.setTestOnly('goog.testing.PerformanceTimer');
14
goog.provide('goog.testing.PerformanceTimer');
15
goog.provide('goog.testing.PerformanceTimer.Task');
16
17
goog.require('goog.Thenable');
18
goog.require('goog.array');
19
goog.require('goog.async.Deferred');
20
goog.require('goog.math');
21
22
23
24
/**
25
* Creates a performance timer that runs test functions a number of times to
26
* generate timing samples, and provides performance statistics (minimum,
27
* maximum, average, and standard deviation).
28
* @param {number=} opt_numSamples Number of times to run the test function;
29
* defaults to 10.
30
* @param {number=} opt_timeoutInterval Number of milliseconds after which the
31
* test is to be aborted; defaults to 5 seconds (5,000ms).
32
* @constructor
33
*/
34
goog.testing.PerformanceTimer = function(opt_numSamples, opt_timeoutInterval) {
35
'use strict';
36
/**
37
* Number of times the test function is to be run; defaults to 10.
38
* @private {number}
39
*/
40
this.numSamples_ = opt_numSamples || 10;
41
42
/**
43
* Number of milliseconds after which the test is to be aborted; defaults to
44
* 5,000ms.
45
* @private {number}
46
*/
47
this.timeoutInterval_ = opt_timeoutInterval || 5000;
48
49
/**
50
* Whether to discard outliers (i.e. the smallest and the largest values)
51
* from the sample set before computing statistics. Defaults to false.
52
* @private {boolean}
53
*/
54
this.discardOutliers_ = false;
55
};
56
57
58
/**
59
* A function whose subsequent calls differ in milliseconds. Used to calculate
60
* the start and stop checkpoint times for runs. Note that high performance
61
* timers do not necessarily return the current time in milliseconds.
62
* @return {number}
63
* @private
64
*/
65
goog.testing.PerformanceTimer.now_ = function() {
66
'use strict';
67
// goog.now is used in DEBUG mode to make the class easier to test.
68
return !goog.DEBUG && window.performance && window.performance.now ?
69
window.performance.now() :
70
goog.now();
71
};
72
73
74
/**
75
* @return {number} The number of times the test function will be run.
76
*/
77
goog.testing.PerformanceTimer.prototype.getNumSamples = function() {
78
'use strict';
79
return this.numSamples_;
80
};
81
82
83
/**
84
* Sets the number of times the test function will be run.
85
* @param {number} numSamples Number of times to run the test function.
86
*/
87
goog.testing.PerformanceTimer.prototype.setNumSamples = function(numSamples) {
88
'use strict';
89
this.numSamples_ = numSamples;
90
};
91
92
93
/**
94
* @return {number} The number of milliseconds after which the test times out.
95
*/
96
goog.testing.PerformanceTimer.prototype.getTimeoutInterval = function() {
97
'use strict';
98
return this.timeoutInterval_;
99
};
100
101
102
/**
103
* Sets the number of milliseconds after which the test times out.
104
* @param {number} timeoutInterval Timeout interval in ms.
105
*/
106
goog.testing.PerformanceTimer.prototype.setTimeoutInterval = function(
107
timeoutInterval) {
108
'use strict';
109
this.timeoutInterval_ = timeoutInterval;
110
};
111
112
113
/**
114
* Sets whether to ignore the smallest and the largest values when computing
115
* stats.
116
* @param {boolean} discard Whether to discard outlier values.
117
*/
118
goog.testing.PerformanceTimer.prototype.setDiscardOutliers = function(discard) {
119
'use strict';
120
this.discardOutliers_ = discard;
121
};
122
123
124
/**
125
* @return {boolean} Whether outlier values are discarded prior to computing
126
* stats.
127
*/
128
goog.testing.PerformanceTimer.prototype.isDiscardOutliers = function() {
129
'use strict';
130
return this.discardOutliers_;
131
};
132
133
134
/**
135
* Executes the test function the required number of times (or until the
136
* test run exceeds the timeout interval, whichever comes first). Returns
137
* an object containing the following:
138
* <pre>
139
* {
140
* 'average': average execution time (ms)
141
* 'count': number of executions (may be fewer than expected due to timeout)
142
* 'maximum': longest execution time (ms)
143
* 'minimum': shortest execution time (ms)
144
* 'standardDeviation': sample standard deviation (ms)
145
* 'total': total execution time (ms)
146
* }
147
* </pre>
148
*
149
* @param {Function} testFn Test function whose performance is to
150
* be measured.
151
* @return {!Object} Object containing performance stats.
152
*/
153
goog.testing.PerformanceTimer.prototype.run = function(testFn) {
154
'use strict';
155
return this.runTask(new goog.testing.PerformanceTimer.Task(
156
/** @type {goog.testing.PerformanceTimer.TestFunction} */ (testFn)));
157
};
158
159
160
/**
161
* Executes the test function of the specified task as described in
162
* `run`. In addition, if specified, the set up and tear down functions of
163
* the task are invoked before and after each invocation of the test function.
164
* @see goog.testing.PerformanceTimer#run
165
* @param {goog.testing.PerformanceTimer.Task} task A task describing the test
166
* function to invoke.
167
* @return {!Object} Object containing performance stats.
168
*/
169
goog.testing.PerformanceTimer.prototype.runTask = function(task) {
170
'use strict';
171
var samples = [];
172
var testStart = goog.testing.PerformanceTimer.now_();
173
var totalRunTime = 0;
174
175
var testFn = task.getTest();
176
var setUpFn = task.getSetUp();
177
var tearDownFn = task.getTearDown();
178
179
for (var i = 0; i < this.numSamples_ && totalRunTime <= this.timeoutInterval_;
180
i++) {
181
setUpFn();
182
var sampleStart = goog.testing.PerformanceTimer.now_();
183
testFn();
184
var sampleEnd = goog.testing.PerformanceTimer.now_();
185
tearDownFn();
186
samples[i] = sampleEnd - sampleStart;
187
totalRunTime = sampleEnd - testStart;
188
}
189
190
return this.finishTask_(samples);
191
};
192
193
194
/**
195
* Finishes the run of a task by creating a result object from samples, in the
196
* format described in `run`.
197
* @see goog.testing.PerformanceTimer#run
198
* @param {!Array<number>} samples The samples to analyze.
199
* @return {!Object} Object containing performance stats.
200
* @private
201
*/
202
goog.testing.PerformanceTimer.prototype.finishTask_ = function(samples) {
203
'use strict';
204
if (this.discardOutliers_ && samples.length > 2) {
205
goog.array.remove(samples, Math.min.apply(null, samples));
206
goog.array.remove(samples, Math.max.apply(null, samples));
207
}
208
209
return goog.testing.PerformanceTimer.createResults(samples);
210
};
211
212
213
/**
214
* Executes the test function of the specified task asynchronously. The test
215
* function may return a Thenable to allow for asynchronous execution. In
216
* addition, if specified, the setUp and tearDown functions of the task are
217
* invoked before and after each invocation of the test function. Note,
218
* setUp/tearDown too may return Thenables for asynchronous execution.
219
* @see goog.testing.PerformanceTimer#run
220
* @param {goog.testing.PerformanceTimer.Task} task A task describing the test
221
* function to invoke.
222
* @return {!goog.async.Deferred} The deferred result, eventually an object
223
* containing performance stats.
224
*/
225
goog.testing.PerformanceTimer.prototype.runAsyncTask = function(task) {
226
'use strict';
227
var samples = [];
228
var testStart = goog.testing.PerformanceTimer.now_();
229
230
var testFn = task.getTest();
231
var setUpFn = task.getSetUp();
232
var tearDownFn = task.getTearDown();
233
234
// Note that this uses a separate code path from runTask() because
235
// implementing runTask() in terms of runAsyncTask() could easily cause
236
// a stack overflow if there are many iterations.
237
return goog.async.Deferred.fromPromise(this.runAsyncTaskSample_(
238
testFn, setUpFn, tearDownFn, samples, testStart));
239
};
240
241
242
/**
243
* Runs a task once, waits for the test function to complete asynchronously
244
* and starts another run if not enough samples have been collected. Otherwise
245
* finishes this task.
246
* @param {goog.testing.PerformanceTimer.TestFunction} testFn The test function.
247
* @param {goog.testing.PerformanceTimer.TestFunction} setUpFn The set up
248
* function that will be called once before the test function is run.
249
* @param {goog.testing.PerformanceTimer.TestFunction} tearDownFn The set up
250
* function that will be called once after the test function completed.
251
* @param {!Array<number>} samples The time samples from all runs of the test
252
* function so far.
253
* @param {number} testStart The timestamp when the first sample was started.
254
* @return {!Promise} A promise that returns the completed performance stats.
255
* @private
256
*/
257
goog.testing.PerformanceTimer.prototype.runAsyncTaskSample_ = function(
258
testFn, setUpFn, tearDownFn, samples, testStart) {
259
'use strict';
260
const timer = this;
261
let promise = Promise.resolve();
262
let sampleStart;
263
let sampleEnd;
264
for (let i = 0; i < timer.numSamples_; i++) {
265
promise = promise.then(setUpFn)
266
.then(() => {
267
'use strict';
268
sampleStart = goog.testing.PerformanceTimer.now_();
269
})
270
.then(testFn)
271
.then(() => {
272
'use strict';
273
sampleEnd = goog.testing.PerformanceTimer.now_();
274
})
275
.then(tearDownFn)
276
.then(() => {
277
'use strict';
278
samples.push(sampleEnd - sampleStart);
279
const totalRunTime = sampleEnd - testStart;
280
if (totalRunTime > timer.timeoutInterval_) {
281
// If timeout is exceeded, bypass remaining samples via
282
// errback.
283
throw Error('PerformanceTimer.Timeout');
284
}
285
});
286
}
287
return promise
288
.catch((err) => {
289
// Convert timeout error to success.
290
if (err instanceof Error &&
291
err.message === 'PerformanceTimer.Timeout') {
292
return true;
293
}
294
throw err;
295
})
296
.then(() => timer.finishTask_(samples));
297
};
298
299
300
/**
301
* Return the median of the samples.
302
* @param {!Array<number>} samples
303
* @return {number}
304
*/
305
goog.testing.PerformanceTimer.median = function(samples) {
306
'use strict';
307
samples.sort(function(a, b) {
308
'use strict';
309
return a - b;
310
});
311
let half = Math.floor(samples.length / 2);
312
if (samples.length % 2) {
313
return samples[half];
314
} else {
315
return (samples[half - 1] + samples[half]) / 2.0;
316
}
317
};
318
319
320
/**
321
* Creates a performance timer results object by analyzing a given array of
322
* sample timings.
323
* @param {!Array<number>} samples The samples to analyze.
324
* @return {!Object} Object containing performance stats.
325
*/
326
goog.testing.PerformanceTimer.createResults = function(samples) {
327
'use strict';
328
return {
329
'average': goog.math.average.apply(null, samples),
330
'count': samples.length,
331
'median': goog.testing.PerformanceTimer.median(samples),
332
'maximum': Math.max.apply(null, samples),
333
'minimum': Math.min.apply(null, samples),
334
'standardDeviation': goog.math.standardDeviation.apply(null, samples),
335
'total': goog.math.sum.apply(null, samples)
336
};
337
};
338
339
340
/**
341
* A test function whose performance should be measured or a setUp/tearDown
342
* function. It may optionally return a Thenable (e.g. a promise) to
343
* for asynchronous execution using the runAsyncTask method.
344
* @see goog.testing.PerformanceTimer#runAsyncTask
345
* @typedef {function():(!goog.Thenable|undefined)}
346
*/
347
goog.testing.PerformanceTimer.TestFunction;
348
349
350
351
/**
352
* A task for the performance timer to measure. Callers can specify optional
353
* setUp and tearDown methods to control state before and after each run of the
354
* test function.
355
* @param {goog.testing.PerformanceTimer.TestFunction} test Test function whose
356
* performance is to be measured.
357
* @constructor
358
* @final
359
*/
360
goog.testing.PerformanceTimer.Task = function(test) {
361
'use strict';
362
/**
363
* The test function to time.
364
* @type {goog.testing.PerformanceTimer.TestFunction}
365
* @private
366
*/
367
this.test_ = test;
368
};
369
370
371
/**
372
* An optional set up function to run before each invocation of the test
373
* function.
374
* @type {goog.testing.PerformanceTimer.TestFunction}
375
* @private
376
*/
377
goog.testing.PerformanceTimer.Task.prototype.setUp_ = function() {};
378
379
380
/**
381
* An optional tear down function to run after each invocation of the test
382
* function.
383
* @type {goog.testing.PerformanceTimer.TestFunction}
384
* @private
385
*/
386
goog.testing.PerformanceTimer.Task.prototype.tearDown_ = function() {};
387
388
389
/**
390
* @return {goog.testing.PerformanceTimer.TestFunction} The test function to
391
* time.
392
*/
393
goog.testing.PerformanceTimer.Task.prototype.getTest = function() {
394
'use strict';
395
return this.test_;
396
};
397
398
399
/**
400
* Specifies a set up function to be invoked before each invocation of the test
401
* function.
402
* @param {goog.testing.PerformanceTimer.TestFunction} setUp The set up
403
* function.
404
* @return {!goog.testing.PerformanceTimer.Task} This task.
405
*/
406
goog.testing.PerformanceTimer.Task.prototype.withSetUp = function(setUp) {
407
'use strict';
408
this.setUp_ = setUp;
409
return this;
410
};
411
412
413
/**
414
* @return {goog.testing.PerformanceTimer.TestFunction} The set up function or
415
* the default no-op function if none was specified.
416
*/
417
goog.testing.PerformanceTimer.Task.prototype.getSetUp = function() {
418
'use strict';
419
return this.setUp_;
420
};
421
422
423
/**
424
* Specifies a tear down function to be invoked after each invocation of the
425
* test function.
426
* @param {goog.testing.PerformanceTimer.TestFunction} tearDown The tear down
427
* function.
428
* @return {!goog.testing.PerformanceTimer.Task} This task.
429
*/
430
goog.testing.PerformanceTimer.Task.prototype.withTearDown = function(tearDown) {
431
'use strict';
432
this.tearDown_ = tearDown;
433
return this;
434
};
435
436
437
/**
438
* @return {goog.testing.PerformanceTimer.TestFunction} The tear down function
439
* or the default no-op function if none was specified.
440
*/
441
goog.testing.PerformanceTimer.Task.prototype.getTearDown = function() {
442
'use strict';
443
return this.tearDown_;
444
};
445
446