feat(bench press): replace microIterations with microMetrics

This commit is contained in:
Tobias Bosch 2015-03-27 10:37:02 -07:00
parent 3afb744e77
commit 33bfc4c24a
13 changed files with 190 additions and 98 deletions

View File

@ -1,5 +1,5 @@
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
import {document} from 'angular2/src/facade/browser'; import {document, window} from 'angular2/src/facade/browser';
import {NumberWrapper, BaseException, isBlank} from 'angular2/src/facade/lang'; import {NumberWrapper, BaseException, isBlank} from 'angular2/src/facade/lang';
var DOM = new BrowserDomAdapter(); var DOM = new BrowserDomAdapter();
@ -35,3 +35,10 @@ export function bindAction(selector:string, callback:Function) {
callback(); callback();
}); });
} }
export function microBenchmark(name, iterationCount, callback) {
var durationName = `${name}/${iterationCount}`;
window.console.time(durationName);
callback();
window.console.timeEnd(durationName);
}

View File

@ -23,26 +23,21 @@ function runBenchmark(config) {
return getScaleFactor(browser.params.benchmark.scaling).then(function(scaleFactor) { return getScaleFactor(browser.params.benchmark.scaling).then(function(scaleFactor) {
var description = {}; var description = {};
var urlParams = []; var urlParams = [];
var microIterations = config.microIterations || 0; if (config.params) {
var params = config.params || []; config.params.forEach(function(param) {
if (microIterations) {
params = params.concat([{
name: 'iterations', value: microIterations, scale: 'linear'
}]);
}
params.forEach(function(param) {
var name = param.name; var name = param.name;
var value = applyScaleFactor(param.value, scaleFactor, param.scale); var value = applyScaleFactor(param.value, scaleFactor, param.scale);
urlParams.push(name + '=' + value); urlParams.push(name + '=' + value);
description[name] = value; description[name] = value;
}); });
}
var url = encodeURI(config.url + '?' + urlParams.join('&')); var url = encodeURI(config.url + '?' + urlParams.join('&'));
browser.get(url); browser.get(url);
return benchpressRunner.sample({ return benchpressRunner.sample({
id: config.id, id: config.id,
execute: config.work, execute: config.work,
prepare: config.prepare, prepare: config.prepare,
microIterations: microIterations, microMetrics: config.microMetrics,
bindings: [ bindings: [
benchpress.bind(benchpress.Options.SAMPLE_DESCRIPTION).toValue(description) benchpress.bind(benchpress.Options.SAMPLE_DESCRIPTION).toValue(description)
] ]

View File

@ -11,10 +11,13 @@ describe('ng2 change detection benchmark', function () {
url: URL, url: URL,
buttons: ['#ng2ChangeDetectionDynamic'], buttons: ['#ng2ChangeDetectionDynamic'],
id: 'ng2.changeDetection.dynamic', id: 'ng2.changeDetection.dynamic',
params: [{ params: [
name: 'numberOfChecks', value: 900000 {name: 'numberOfChecks', value: 900000},
}], {name: 'iterations', value: 20, scale: 'linear'}
microIterations: 20 ],
microMetrics: {
'detectChangesAvg': 'avg time to detect changes (ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -23,10 +26,13 @@ describe('ng2 change detection benchmark', function () {
url: URL, url: URL,
buttons: ['#ng2ChangeDetectionJit'], buttons: ['#ng2ChangeDetectionJit'],
id: 'ng2.changeDetection.jit', id: 'ng2.changeDetection.jit',
params: [{ params: [
name: 'numberOfChecks', value: 900000 {name: 'numberOfChecks', value: 900000},
}], {name: 'iterations', value: 20, scale: 'linear'}
microIterations: 20 ],
microMetrics: {
'detectChangesAvg': 'avg time to detect changes (ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -35,10 +41,13 @@ describe('ng2 change detection benchmark', function () {
url: URL, url: URL,
buttons: ['#baselineChangeDetection'], buttons: ['#baselineChangeDetection'],
id: 'baseline.changeDetection', id: 'baseline.changeDetection',
params: [{ params: [
name: 'numberOfChecks', value: 900000 {name: 'numberOfChecks', value: 900000},
}], {name: 'iterations', value: 20, scale: 'linear'}
microIterations: 20 ],
microMetrics: {
'detectChangesAvg': 'avg time to detect changes (ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });

View File

@ -14,7 +14,9 @@ describe('ng2 di benchmark', function () {
params: [{ params: [{
name: 'iterations', value: 20000, scale: 'linear' name: 'iterations', value: 20000, scale: 'linear'
}], }],
microIterations: 20000 microMetrics: {
'injectAvg': 'avg time for injection (in ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -23,7 +25,12 @@ describe('ng2 di benchmark', function () {
url: URL, url: URL,
buttons: ['#getByKey'], buttons: ['#getByKey'],
id: 'ng2.di.getByKey', id: 'ng2.di.getByKey',
microIterations: 20000 params: [{
name: 'iterations', value: 20000, scale: 'linear'
}],
microMetrics: {
'injectAvg': 'avg time for injection (in ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -32,7 +39,12 @@ describe('ng2 di benchmark', function () {
url: URL, url: URL,
buttons: ['#getChild'], buttons: ['#getChild'],
id: 'ng2.di.getChild', id: 'ng2.di.getChild',
microIterations: 20000 params: [{
name: 'iterations', value: 20000, scale: 'linear'
}],
microMetrics: {
'injectAvg': 'avg time for getChild (in ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -41,7 +53,12 @@ describe('ng2 di benchmark', function () {
url: URL, url: URL,
buttons: ['#instantiate'], buttons: ['#instantiate'],
id: 'ng2.di.instantiate', id: 'ng2.di.instantiate',
microIterations: 10000 params: [{
name: 'iterations', value: 10000, scale: 'linear'
}],
microMetrics: {
'injectAvg': 'avg time for instantiate (in ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });

View File

@ -11,7 +11,12 @@ describe('ng2 element injector benchmark', function () {
url: URL, url: URL,
buttons: ['#instantiate'], buttons: ['#instantiate'],
id: 'ng2.elementInjector.instantiate', id: 'ng2.elementInjector.instantiate',
microIterations: 20000 params: [{
name: 'iterations', value: 20000, scale: 'linear'
}],
microMetrics: {
'instantiateAvg': 'avg time for injection (in ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -20,7 +25,12 @@ describe('ng2 element injector benchmark', function () {
url: URL, url: URL,
buttons: ['#instantiateDirectives'], buttons: ['#instantiateDirectives'],
id: 'ng2.elementInjector.instantiateDirectives', id: 'ng2.elementInjector.instantiateDirectives',
microIterations: 20000 params: [{
name: 'iterations', value: 20000, scale: 'linear'
}],
microMetrics: {
'instantiateAvg': 'avg time for injection (in ms)'
}
}).then(done, done.fail); }).then(done, done.fail);
}); });

View File

@ -1,7 +1,7 @@
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {isPresent, isJsObject} from 'angular2/src/facade/lang'; import {isPresent, isJsObject} from 'angular2/src/facade/lang';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util'; import {getIntParameter, bindAction, microBenchmark} from 'angular2/src/test_lib/benchmark_util';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
import { import {
@ -172,7 +172,10 @@ export function main () {
} }
} }
runBaselineChangeDetection(); runBaselineChangeDetection();
bindAction('#baselineChangeDetection', baselineChangeDetection); bindAction(
'#baselineChangeDetection',
() => microBenchmark('detectChangesAvg', numberOfRuns, baselineChangeDetection)
);
// -- DYNAMIC // -- DYNAMIC
@ -183,7 +186,10 @@ export function main () {
} }
} }
ng2DynamicChangeDetector.detectChanges(); ng2DynamicChangeDetector.detectChanges();
bindAction('#ng2ChangeDetectionDynamic', ng2ChangeDetectionDynamic); bindAction(
'#ng2ChangeDetectionDynamic',
() => microBenchmark('detectChangesAvg', numberOfRuns, ng2ChangeDetectionDynamic)
);
// -- JIT // -- JIT
@ -198,7 +204,10 @@ export function main () {
} }
ng2JitChangeDetector.detectChanges(); ng2JitChangeDetector.detectChanges();
bindAction('#ng2ChangeDetectionJit', ng2ChangeDetectionJit); bindAction(
'#ng2ChangeDetectionJit',
() => microBenchmark('detectChangesAvg', numberOfRuns, ng2ChangeDetectionJit)
);
} else { } else {
bindAction('#ng2ChangeDetectionJit', () => {}); bindAction('#ng2ChangeDetectionJit', () => {});
} }

View File

@ -1,6 +1,6 @@
import {Injector, Key} from "angular2/di"; import {Injector, Key} from "angular2/di";
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util'; import {getIntParameter, bindAction, microBenchmark} from 'angular2/src/test_lib/benchmark_util';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
var count = 0; var count = 0;
@ -77,10 +77,22 @@ export function main() {
} }
} }
bindAction('#getByToken', getByToken); bindAction(
bindAction('#getByKey', getByKey); '#getByToken',
bindAction('#getChild', getChild); () => microBenchmark('injectAvg', iterations, getByToken)
bindAction('#instantiate', instantiate); );
bindAction(
'#getByKey',
() => microBenchmark('injectAvg', iterations, getByKey)
);
bindAction(
'#getChild',
() => microBenchmark('injectAvg', iterations, getChild)
);
bindAction(
'#instantiate',
() => microBenchmark('injectAvg', iterations, instantiate)
);
} }

View File

@ -1,7 +1,7 @@
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {Injector} from 'angular2/di'; import {Injector} from 'angular2/di';
import {ProtoElementInjector} from 'angular2/src/core/compiler/element_injector'; import {ProtoElementInjector} from 'angular2/src/core/compiler/element_injector';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util'; import {getIntParameter, bindAction, microBenchmark} from 'angular2/src/test_lib/benchmark_util';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
var count = 0; var count = 0;
@ -49,8 +49,14 @@ export function main() {
} }
} }
bindAction('#instantiate', instantiate); bindAction(
bindAction('#instantiateDirectives', instantiateDirectives); '#instantiate',
() => microBenchmark('instantiateAvg', iterations, instantiate)
);
bindAction(
'#instantiateDirectives',
() => microBenchmark('instantiateAvg', iterations, instantiateDirectives)
);
} }
class A { class A {

View File

@ -20,15 +20,11 @@ export class Options {
// TODO(tbosch): use static initializer when our transpiler supports it // TODO(tbosch): use static initializer when our transpiler supports it
static get USER_AGENT() { return _USER_AGENT; } static get USER_AGENT() { return _USER_AGENT; }
// TODO(tbosch): use static initializer when our transpiler supports it // TODO(tbosch): use static initializer when our transpiler supports it
/**
* Number of iterations that run inside the browser by user code.
* Used for micro benchmarks.
**/
static get MICRO_ITERATIONS() { return _MICRO_ITERATIONS; }
// TODO(tbosch): use static initializer when our transpiler supports it
static get NOW() { return _NOW; } static get NOW() { return _NOW; }
// TODO(tbosch): use static values when our transpiler supports them // TODO(tbosch): use static values when our transpiler supports them
static get WRITE_FILE() { return _WRITE_FILE; } static get WRITE_FILE() { return _WRITE_FILE; }
// TODO(tbosch): use static values when our transpiler supports them
static get MICRO_METRICS() { return _MICRO_METRICS; }
} }
var _SAMPLE_ID = new OpaqueToken('Options.sampleId'); var _SAMPLE_ID = new OpaqueToken('Options.sampleId');
@ -39,7 +35,7 @@ var _PREPARE = new OpaqueToken('Options.prepare');
var _EXECUTE = new OpaqueToken('Options.execute'); var _EXECUTE = new OpaqueToken('Options.execute');
var _CAPABILITIES = new OpaqueToken('Options.capabilities'); var _CAPABILITIES = new OpaqueToken('Options.capabilities');
var _USER_AGENT = new OpaqueToken('Options.userAgent'); var _USER_AGENT = new OpaqueToken('Options.userAgent');
var _MICRO_ITERATIONS = new OpaqueToken('Options.microIterations'); var _MICRO_METRICS = new OpaqueToken('Options.microMetrics');
var _NOW = new OpaqueToken('Options.now'); var _NOW = new OpaqueToken('Options.now');
var _WRITE_FILE = new OpaqueToken('Options.writeFile'); var _WRITE_FILE = new OpaqueToken('Options.writeFile');
@ -48,5 +44,6 @@ var _DEFAULT_BINDINGS = [
bind(_SAMPLE_DESCRIPTION).toValue({}), bind(_SAMPLE_DESCRIPTION).toValue({}),
bind(_FORCE_GC).toValue(false), bind(_FORCE_GC).toValue(false),
bind(_PREPARE).toValue(false), bind(_PREPARE).toValue(false),
bind(_MICRO_METRICS).toValue({}),
bind(_NOW).toValue( () => DateWrapper.now() ) bind(_NOW).toValue( () => DateWrapper.now() )
]; ];

View File

@ -1,5 +1,7 @@
import { PromiseWrapper, Promise } from 'angular2/src/facade/async'; import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
import { isPresent, isBlank, int, BaseException, StringWrapper, Math } from 'angular2/src/facade/lang'; import {
isPresent, isBlank, int, BaseException, StringWrapper, Math, RegExpWrapper, NumberWrapper
} from 'angular2/src/facade/lang';
import { ListWrapper, StringMap, StringMapWrapper } from 'angular2/src/facade/collection'; import { ListWrapper, StringMap, StringMapWrapper } from 'angular2/src/facade/collection';
import { bind, OpaqueToken } from 'angular2/di'; import { bind, OpaqueToken } from 'angular2/di';
@ -20,22 +22,21 @@ export class PerflogMetric extends Metric {
_remainingEvents:List; _remainingEvents:List;
_measureCount:int; _measureCount:int;
_setTimeout:Function; _setTimeout:Function;
_microIterations:int; _microMetrics:StringMap<string, string>;
_perfLogFeatures:PerfLogFeatures; _perfLogFeatures:PerfLogFeatures;
/** /**
* @param driverExtension * @param driverExtension
* @param setTimeout * @param setTimeout
* @param microIterations Number of iterations that run inside the browser by user code. * @param microMetrics Name and description of metrics provided via console.time / console.timeEnd
* Used for micro benchmarks.
**/ **/
constructor(driverExtension:WebDriverExtension, setTimeout:Function, microIterations:int) { constructor(driverExtension:WebDriverExtension, setTimeout:Function, microMetrics:StringMap<string, string>) {
super(); super();
this._driverExtension = driverExtension; this._driverExtension = driverExtension;
this._remainingEvents = []; this._remainingEvents = [];
this._measureCount = 0; this._measureCount = 0;
this._setTimeout = setTimeout; this._setTimeout = setTimeout;
this._microIterations = microIterations; this._microMetrics = microMetrics;
this._perfLogFeatures = driverExtension.perfLogFeatures(); this._perfLogFeatures = driverExtension.perfLogFeatures();
} }
@ -52,9 +53,9 @@ export class PerflogMetric extends Metric {
res['gcAmount'] = 'gc amount in kbytes'; res['gcAmount'] = 'gc amount in kbytes';
res['majorGcTime'] = 'time of major gcs in ms'; res['majorGcTime'] = 'time of major gcs in ms';
} }
if (this._microIterations > 0) { StringMapWrapper.forEach(this._microMetrics, (desc, name) => {
res['microScriptTimeAvg'] = 'average script time for a micro iteration'; StringMapWrapper.set(res, name, desc);
} });
return res; return res;
} }
@ -137,6 +138,9 @@ export class PerflogMetric extends Metric {
if (this._perfLogFeatures.render) { if (this._perfLogFeatures.render) {
result['renderTime'] = 0; result['renderTime'] = 0;
} }
StringMapWrapper.forEach(this._microMetrics, (desc, name) => {
result[name] = 0;
});
var markStartEvent = null; var markStartEvent = null;
var markEndEvent = null; var markEndEvent = null;
@ -147,17 +151,24 @@ export class PerflogMetric extends Metric {
events.forEach( (event) => { events.forEach( (event) => {
var ph = event['ph']; var ph = event['ph'];
var name = event['name']; var name = event['name'];
var microIterations = 1;
var microIterationsMatch = RegExpWrapper.firstMatch(_MICRO_ITERATIONS_REGEX, name);
if (isPresent(microIterationsMatch)) {
name = microIterationsMatch[1];
microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10);
}
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) { if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) {
markStartEvent = event; markStartEvent = event;
} else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) { } else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
markEndEvent = event; markEndEvent = event;
} }
if (isPresent(markStartEvent) && isBlank(markEndEvent) && event['pid'] === markStartEvent['pid']) { if (isPresent(markStartEvent) && isBlank(markEndEvent) && event['pid'] === markStartEvent['pid']) {
if (StringWrapper.equals(ph, 'B')) { if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
intervalStarts[name] = event; intervalStarts[name] = event;
} else if (StringWrapper.equals(ph, 'E') && isPresent(intervalStarts[name])) { } else if ((StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) && isPresent(intervalStarts[name])) {
var startEvent = intervalStarts[name]; var startEvent = intervalStarts[name];
var duration = event['ts'] - startEvent['ts']; var duration = (event['ts'] - startEvent['ts']);
intervalStarts[name] = null; intervalStarts[name] = null;
if (StringWrapper.equals(name, 'gc')) { if (StringWrapper.equals(name, 'gc')) {
result['gcTime'] += duration; result['gcTime'] += duration;
@ -177,14 +188,13 @@ export class PerflogMetric extends Metric {
} }
} else if (StringWrapper.equals(name, 'script')) { } else if (StringWrapper.equals(name, 'script')) {
result['scriptTime'] += duration; result['scriptTime'] += duration;
} else if (isPresent(this._microMetrics[name])) {
result[name] += duration / microIterations;
} }
} }
} }
}); });
result['pureScriptTime'] = result['scriptTime'] - gcTimeInScript - renderTimeInScript; result['pureScriptTime'] = result['scriptTime'] - gcTimeInScript - renderTimeInScript;
if (this._microIterations > 0) {
result['microScriptTimeAvg'] = result['scriptTime'] / this._microIterations;
}
return isPresent(markStartEvent) && isPresent(markEndEvent) ? result : null; return isPresent(markStartEvent) && isPresent(markEndEvent) ? result : null;
} }
@ -193,15 +203,16 @@ export class PerflogMetric extends Metric {
} }
} }
var _MICRO_ITERATIONS_REGEX = RegExpWrapper.create('(.+)\\*(\\d+)$');
var _MAX_RETRY_COUNT = 20; var _MAX_RETRY_COUNT = 20;
var _MARK_NAME_PREFIX = 'benchpress'; var _MARK_NAME_PREFIX = 'benchpress';
var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout'); var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
var _BINDINGS = [ var _BINDINGS = [
bind(PerflogMetric).toFactory( bind(PerflogMetric).toFactory(
(driverExtension, setTimeout, microIterations) => (driverExtension, setTimeout, microMetrics) =>
new PerflogMetric(driverExtension, setTimeout, microIterations), new PerflogMetric(driverExtension, setTimeout, microMetrics),
[WebDriverExtension, _SET_TIMEOUT, Options.MICRO_ITERATIONS] [WebDriverExtension, _SET_TIMEOUT, Options.MICRO_METRICS]
), ),
bind(_SET_TIMEOUT).toValue( (fn, millis) => PromiseWrapper.setTimeout(fn, millis) ), bind(_SET_TIMEOUT).toValue( (fn, millis) => PromiseWrapper.setTimeout(fn, millis) )
bind(Options.MICRO_ITERATIONS).toValue(0)
]; ];

View File

@ -34,7 +34,7 @@ export class Runner {
this._defaultBindings = defaultBindings; this._defaultBindings = defaultBindings;
} }
sample({id, execute, prepare, microIterations, bindings}):Promise<SampleState> { sample({id, execute, prepare, microMetrics, bindings}):Promise<SampleState> {
var sampleBindings = [ var sampleBindings = [
_DEFAULT_BINDINGS, _DEFAULT_BINDINGS,
this._defaultBindings, this._defaultBindings,
@ -44,8 +44,8 @@ export class Runner {
if (isPresent(prepare)) { if (isPresent(prepare)) {
ListWrapper.push(sampleBindings, bind(Options.PREPARE).toValue(prepare)); ListWrapper.push(sampleBindings, bind(Options.PREPARE).toValue(prepare));
} }
if (isPresent(microIterations)) { if (isPresent(microMetrics)) {
ListWrapper.push(sampleBindings, bind(Options.MICRO_ITERATIONS).toValue(microIterations)); ListWrapper.push(sampleBindings, bind(Options.MICRO_METRICS).toValue(microMetrics));
} }
if (isPresent(bindings)) { if (isPresent(bindings)) {
ListWrapper.push(sampleBindings, bindings); ListWrapper.push(sampleBindings, bindings);

View File

@ -11,7 +11,7 @@ import {
xit, xit,
} from 'angular2/test_lib'; } from 'angular2/test_lib';
import { List, ListWrapper } from 'angular2/src/facade/collection'; import { List, ListWrapper, StringMapWrapper } from 'angular2/src/facade/collection';
import { PromiseWrapper, Promise } from 'angular2/src/facade/async'; import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
import { isPresent, isBlank } from 'angular2/src/facade/lang'; import { isPresent, isBlank } from 'angular2/src/facade/lang';
@ -27,20 +27,23 @@ export function main() {
var commandLog; var commandLog;
var eventFactory = new TraceEventFactory('timeline', 'pid0'); var eventFactory = new TraceEventFactory('timeline', 'pid0');
function createMetric(perfLogs, microIterations = 0, perfLogFeatures = null) { function createMetric(perfLogs, microMetrics = null, perfLogFeatures = null) {
commandLog = []; commandLog = [];
if (isBlank(perfLogFeatures)) { if (isBlank(perfLogFeatures)) {
perfLogFeatures = new PerfLogFeatures({render: true, gc: true}); perfLogFeatures = new PerfLogFeatures({render: true, gc: true});
} }
if (isBlank(microMetrics)) {
microMetrics = StringMapWrapper.create();
}
var bindings = [ var bindings = [
Options.DEFAULT_BINDINGS, Options.DEFAULT_BINDINGS,
PerflogMetric.BINDINGS, PerflogMetric.BINDINGS,
bind(Options.MICRO_METRICS).toValue(microMetrics),
bind(PerflogMetric.SET_TIMEOUT).toValue( (fn, millis) => { bind(PerflogMetric.SET_TIMEOUT).toValue( (fn, millis) => {
ListWrapper.push(commandLog, ['setTimeout', millis]); ListWrapper.push(commandLog, ['setTimeout', millis]);
fn(); fn();
}), }),
bind(WebDriverExtension).toValue(new MockDriverExtension(perfLogs, commandLog, perfLogFeatures)), bind(WebDriverExtension).toValue(new MockDriverExtension(perfLogs, commandLog, perfLogFeatures))
bind(Options.MICRO_ITERATIONS).toValue(microIterations)
]; ];
return new Injector(bindings).get(PerflogMetric); return new Injector(bindings).get(PerflogMetric);
} }
@ -48,12 +51,12 @@ export function main() {
describe('perflog metric', () => { describe('perflog metric', () => {
it('should describe itself based on the perfLogFeatrues', () => { it('should describe itself based on the perfLogFeatrues', () => {
expect(createMetric([[]], 0, new PerfLogFeatures()).describe()).toEqual({ expect(createMetric([[]], null, new PerfLogFeatures()).describe()).toEqual({
'scriptTime': 'script execution time in ms, including gc and render', 'scriptTime': 'script execution time in ms, including gc and render',
'pureScriptTime': 'script execution time in ms, without gc nor render' 'pureScriptTime': 'script execution time in ms, without gc nor render'
}); });
expect(createMetric([[]], 0, new PerfLogFeatures({ expect(createMetric([[]], null, new PerfLogFeatures({
render: true, render: true,
gc: false gc: false
})).describe()).toEqual({ })).describe()).toEqual({
@ -72,6 +75,13 @@ export function main() {
}); });
}); });
it('should describe itself based on micro metrics', () => {
var description = createMetric([[]], {
'myMicroMetric': 'someDesc'
}).describe();
expect(description['myMicroMetric']).toEqual('someDesc');
});
describe('beginMeasure', () => { describe('beginMeasure', () => {
it('should mark the timeline', inject([AsyncTestCompleter], (async) => { it('should mark the timeline', inject([AsyncTestCompleter], (async) => {
@ -194,10 +204,10 @@ export function main() {
describe('aggregation', () => { describe('aggregation', () => {
function aggregate(events, microIterations = 0) { function aggregate(events, microMetrics = null) {
ListWrapper.insert(events, 0, eventFactory.markStart('benchpress0', 0)); ListWrapper.insert(events, 0, eventFactory.markStart('benchpress0', 0));
ListWrapper.push(events, eventFactory.markEnd('benchpress0', 10)); ListWrapper.push(events, eventFactory.markEnd('benchpress0', 10));
var metric = createMetric([events], microIterations); var metric = createMetric([events], microMetrics);
return metric return metric
.beginMeasure().then( (_) => metric.endMeasure(false) ); .beginMeasure().then( (_) => metric.endMeasure(false) );
} }
@ -319,25 +329,34 @@ export function main() {
}); });
})); }));
describe('microIterations', () => { describe('microMetrics', () => {
it('should not report microScriptTimeAvg if microIterations = 0', inject([AsyncTestCompleter], (async) => { it('should report micro metrics', inject([AsyncTestCompleter], (async) => {
aggregate([ aggregate([
eventFactory.start('script', 0), eventFactory.markStart('mm1', 0),
eventFactory.end('script', 5) eventFactory.markEnd('mm1', 5),
], 0).then((data) => { ], {'mm1': 'micro metric 1'}).then((data) => {
expect(isPresent(data['microScriptTimeAvg'])).toBe(false); expect(data['mm1']).toBe(5.0);
async.done(); async.done();
}); });
})); }));
it('should report microScriptTimeAvg', inject([AsyncTestCompleter], (async) => { it('should ignore micro metrics that were not specified', inject([AsyncTestCompleter], (async) => {
aggregate([ aggregate([
eventFactory.start('script', 0), eventFactory.markStart('mm1', 0),
eventFactory.end('script', 5) eventFactory.markEnd('mm1', 5),
], 4).then((data) => { ]).then((data) => {
expect(data['scriptTime']).toBe(5); expect(data['mm1']).toBeFalsy();
expect(data['microScriptTimeAvg']).toBe(5/4); async.done();
});
}));
it('should report micro metric averages', inject([AsyncTestCompleter], (async) => {
aggregate([
eventFactory.markStart('mm1*20', 0),
eventFactory.markEnd('mm1*20', 5),
], {'mm1': 'micro metric 1'}).then((data) => {
expect(data['mm1']).toBe(5/20);
async.done(); async.done();
}); });
})); }));

View File

@ -96,9 +96,9 @@ export function main() {
}); });
})); }));
it('should bind Options.MICRO_ITERATIONS', inject([AsyncTestCompleter], (async) => { it('should bind Options.MICRO_METRICS', inject([AsyncTestCompleter], (async) => {
createRunner().sample({id: 'someId', microIterations: 23}).then( (_) => { createRunner().sample({id: 'someId', microMetrics: {'a': 'b'}}).then( (_) => {
expect(injector.get(Options.MICRO_ITERATIONS)).toEqual(23); expect(injector.get(Options.MICRO_METRICS)).toEqual({'a': 'b'});
async.done(); async.done();
}); });
})); }));