feat(bench press): add microIterations option

This commit is contained in:
Tobias Bosch 2015-03-03 11:31:35 -08:00
parent 03793d0714
commit 043b8c6d2e
12 changed files with 107 additions and 38 deletions

View File

@ -23,7 +23,14 @@ 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 = [];
config.params.forEach(function(param) { var microIterations = config.microIterations || 0;
var params = config.params || [];
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);
@ -35,6 +42,7 @@ function runBenchmark(config) {
id: config.id, id: config.id,
execute: config.work, execute: config.work,
prepare: config.prepare, prepare: config.prepare,
microIterations: microIterations,
bindings: [ bindings: [
benchpress.bind(benchpress.Options.SAMPLE_DESCRIPTION).toValue(description) benchpress.bind(benchpress.Options.SAMPLE_DESCRIPTION).toValue(description)
] ]

View File

@ -12,8 +12,9 @@ describe('ng2 change detection benchmark', function () {
buttons: ['#ng2ChangeDetectionDynamic'], buttons: ['#ng2ChangeDetectionDynamic'],
id: 'ng2.changeDetection.dynamic', id: 'ng2.changeDetection.dynamic',
params: [{ params: [{
name: 'numberOfChecks', value: 900000, scale: 'linear' name: 'numberOfChecks', value: 900000
}] }],
microIterations: 20
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -23,8 +24,9 @@ describe('ng2 change detection benchmark', function () {
buttons: ['#ng2ChangeDetectionJit'], buttons: ['#ng2ChangeDetectionJit'],
id: 'ng2.changeDetection.jit', id: 'ng2.changeDetection.jit',
params: [{ params: [{
name: 'numberOfChecks', value: 900000, scale: 'linear' name: 'numberOfChecks', value: 900000
}] }],
microIterations: 20
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -34,8 +36,9 @@ describe('ng2 change detection benchmark', function () {
buttons: ['#baselineChangeDetection'], buttons: ['#baselineChangeDetection'],
id: 'baseline.changeDetection', id: 'baseline.changeDetection',
params: [{ params: [{
name: 'numberOfChecks', value: 900000, scale: 'linear' name: 'numberOfChecks', value: 900000
}] }],
microIterations: 20
}).then(done, done.fail); }).then(done, done.fail);
}); });

View File

@ -13,7 +13,8 @@ describe('ng2 di benchmark', function () {
id: 'ng2.di.getByToken', id: 'ng2.di.getByToken',
params: [{ params: [{
name: 'iterations', value: 20000, scale: 'linear' name: 'iterations', value: 20000, scale: 'linear'
}] }],
microIterations: 20000
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -22,9 +23,7 @@ describe('ng2 di benchmark', function () {
url: URL, url: URL,
buttons: ['#getByKey'], buttons: ['#getByKey'],
id: 'ng2.di.getByKey', id: 'ng2.di.getByKey',
params: [{ microIterations: 20000
name: 'iterations', value: 20000, scale: 'linear'
}]
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -33,9 +32,7 @@ describe('ng2 di benchmark', function () {
url: URL, url: URL,
buttons: ['#getChild'], buttons: ['#getChild'],
id: 'ng2.di.getChild', id: 'ng2.di.getChild',
params: [{ microIterations: 20000
name: 'iterations', value: 20000, scale: 'linear'
}]
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -44,9 +41,7 @@ describe('ng2 di benchmark', function () {
url: URL, url: URL,
buttons: ['#instantiate'], buttons: ['#instantiate'],
id: 'ng2.di.instantiate', id: 'ng2.di.instantiate',
params: [{ microIterations: 10000
name: 'iterations', value: 10000, scale: 'linear'
}]
}).then(done, done.fail); }).then(done, done.fail);
}); });

View File

@ -11,9 +11,7 @@ describe('ng2 element injector benchmark', function () {
url: URL, url: URL,
buttons: ['#instantiate'], buttons: ['#instantiate'],
id: 'ng2.elementInjector.instantiate', id: 'ng2.elementInjector.instantiate',
params: [{ microIterations: 20000
name: 'iterations', value: 20000, scale: 'linear'
}]
}).then(done, done.fail); }).then(done, done.fail);
}); });
@ -22,9 +20,7 @@ describe('ng2 element injector benchmark', function () {
url: URL, url: URL,
buttons: ['#instantiateDirectives'], buttons: ['#instantiateDirectives'],
id: 'ng2.elementInjector.instantiateDirectives', id: 'ng2.elementInjector.instantiateDirectives',
params: [{ microIterations: 20000
name: 'iterations', value: 20000, scale: 'linear'
}]
}).then(done, done.fail); }).then(done, done.fail);
}); });

View File

@ -5,6 +5,8 @@
<h2>Params</h2> <h2>Params</h2>
<form> <form>
Iterations: Iterations:
<input type="number" name="iterations" placeholder="iterations" value="20">
Number of checks:
<input type="number" name="numberOfChecks" placeholder="numberOfChecks" value="900000"> <input type="number" name="numberOfChecks" placeholder="numberOfChecks" value="900000">
<br> <br>
<button>Apply</button> <button>Apply</button>

View File

@ -138,9 +138,9 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) {
export function main () { export function main () {
BrowserDomAdapter.makeCurrent(); BrowserDomAdapter.makeCurrent();
var numberOfChecks = getIntParameter('numberOfChecks'); var numberOfChecks = getIntParameter('numberOfChecks');
var numberOfRuns = getIntParameter('iterations');
var numberOfChecksPerDetector = 10; var numberOfChecksPerDetector = 10;
var numberOfRuns = 20;
var numberOfDetectors = numberOfChecks / numberOfChecksPerDetector / numberOfRuns; var numberOfDetectors = numberOfChecks / numberOfChecksPerDetector / numberOfRuns;
setUpReflector(); setUpReflector();

View File

@ -5,6 +5,7 @@ import { bind, OpaqueToken } from 'angular2/di';
import { WebDriverExtension } from '../web_driver_extension'; import { WebDriverExtension } from '../web_driver_extension';
import { Metric } from '../metric'; import { Metric } from '../metric';
import { Options } from '../sample_options';
/** /**
* A metric that reads out the performance log * A metric that reads out the performance log
@ -19,17 +20,25 @@ export class PerflogMetric extends Metric {
_remainingEvents:List; _remainingEvents:List;
_measureCount:int; _measureCount:int;
_setTimeout:Function; _setTimeout:Function;
_microIterations:int;
constructor(driverExtension:WebDriverExtension, setTimeout:Function) { /**
* @param driverExtension
* @param setTimeout
* @param microIterations Number of iterations that run inside the browser by user code.
* Used for micro benchmarks.
**/
constructor(driverExtension:WebDriverExtension, setTimeout:Function, microIterations:int) {
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;
} }
describe():StringMap { describe():StringMap {
return { var res = {
'script': 'script execution time in ms', 'script': 'script execution time in ms',
'render': 'render time in ms', 'render': 'render time in ms',
'gcTime': 'gc time in ms', 'gcTime': 'gc time in ms',
@ -37,6 +46,10 @@ export class PerflogMetric extends Metric {
'gcTimeInScript': 'gc time during script execution in ms', 'gcTimeInScript': 'gc time during script execution in ms',
'gcAmountInScript': 'gc amount during script execution in kbytes' 'gcAmountInScript': 'gc amount during script execution in kbytes'
}; };
if (this._microIterations > 0) {
res['scriptMicroAvg'] = 'average script time for a micro iteration';
}
return res;
} }
beginMeasure():Promise { beginMeasure():Promise {
@ -148,6 +161,9 @@ export class PerflogMetric extends Metric {
} }
}); });
result['script'] -= result['gcTimeInScript']; result['script'] -= result['gcTimeInScript'];
if (this._microIterations > 0) {
result['scriptMicroAvg'] = result['script'] / this._microIterations;
}
return isPresent(markStartEvent) && isPresent(markEndEvent) ? result : null; return isPresent(markStartEvent) && isPresent(markEndEvent) ? result : null;
} }
@ -161,8 +177,9 @@ 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) => new PerflogMetric(driverExtension, setTimeout), (driverExtension, setTimeout, microIterations) => new PerflogMetric(driverExtension, setTimeout, microIterations),
[WebDriverExtension, _SET_TIMEOUT] [WebDriverExtension, _SET_TIMEOUT, Options.MICRO_ITERATIONS]
), ),
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, bindings}):Promise<SampleState> { sample({id, execute, prepare, microIterations, bindings}):Promise<SampleState> {
var sampleBindings = [ var sampleBindings = [
_DEFAULT_BINDINGS, _DEFAULT_BINDINGS,
this._defaultBindings, this._defaultBindings,
@ -44,6 +44,9 @@ 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)) {
ListWrapper.push(sampleBindings, bind(Options.MICRO_ITERATIONS).toValue(microIterations));
}
if (isPresent(bindings)) { if (isPresent(bindings)) {
ListWrapper.push(sampleBindings, bindings); ListWrapper.push(sampleBindings, bindings);
} }

View File

@ -17,6 +17,12 @@ export class Options {
static get CAPABILITIES() { return _CAPABILITIES; } static get CAPABILITIES() { return _CAPABILITIES; }
// 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
/**
* Number of iterations that run inside the browser by user code.
* Used for micro benchmarks.
**/
static get MICRO_ITERATIONS() { return _MICRO_ITERATIONS; }
} }
var _SAMPLE_ID = new OpaqueToken('Options.sampleId'); var _SAMPLE_ID = new OpaqueToken('Options.sampleId');
@ -27,3 +33,4 @@ 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');

View File

@ -136,7 +136,10 @@ var _BINDINGS = [
execute: execute, execute: execute,
time: time time: time
}), }),
[WebDriverAdapter, WebDriverExtension, Metric, Reporter, Validator, Options.FORCE_GC, Options.PREPARE, Options.EXECUTE, _TIME] [
WebDriverAdapter, WebDriverExtension, Metric, Reporter, Validator,
Options.FORCE_GC, Options.PREPARE, Options.EXECUTE, _TIME
]
), ),
bind(Options.FORCE_GC).toValue(false), bind(Options.FORCE_GC).toValue(false),
bind(Options.PREPARE).toValue(false), bind(Options.PREPARE).toValue(false),

View File

@ -2,8 +2,9 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from '
import { List, ListWrapper } from 'angular2/src/facade/collection'; import { List, ListWrapper } from 'angular2/src/facade/collection';
import { PromiseWrapper, Promise } from 'angular2/src/facade/async'; import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
import { isPresent } from 'angular2/src/facade/lang';
import { Metric, PerflogMetric, WebDriverExtension, bind, Injector } from 'benchpress/common'; import { Metric, PerflogMetric, WebDriverExtension, bind, Injector, Options } from 'benchpress/common';
import { TraceEventFactory } from '../trace_event_factory'; import { TraceEventFactory } from '../trace_event_factory';
@ -11,16 +12,18 @@ export function main() {
var commandLog; var commandLog;
var eventFactory = new TraceEventFactory('timeline', 'pid0'); var eventFactory = new TraceEventFactory('timeline', 'pid0');
function createMetric(perfLogs) { function createMetric(perfLogs, microIterations = 0) {
commandLog = []; commandLog = [];
return new Injector([ var bindings = [
PerflogMetric.BINDINGS, PerflogMetric.BINDINGS,
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)) bind(WebDriverExtension).toValue(new MockDriverExtension(perfLogs, commandLog)),
]).get(PerflogMetric); bind(Options.MICRO_ITERATIONS).toValue(microIterations)
];
return new Injector(bindings).get(PerflogMetric);
} }
describe('perflog metric', () => { describe('perflog metric', () => {
@ -151,10 +154,10 @@ export function main() {
describe('aggregation', () => { describe('aggregation', () => {
function aggregate(events) { function aggregate(events, microIterations = 0) {
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]); var metric = createMetric([events], microIterations);
return metric return metric
.beginMeasure().then( (_) => metric.endMeasure(false) ); .beginMeasure().then( (_) => metric.endMeasure(false) );
} }
@ -252,6 +255,31 @@ export function main() {
}); });
}); });
describe('microIterations', () => {
it('should not report scriptMicroAvg if microIterations = 0', (done) => {
aggregate([
eventFactory.start('script', 0),
eventFactory.end('script', 5)
], 0).then((data) => {
expect(isPresent(data['scriptMicroAvg'])).toBe(false);
done();
});
});
it('should report scriptMicroAvg', (done) => {
aggregate([
eventFactory.start('script', 0),
eventFactory.end('script', 5)
], 4).then((data) => {
expect(data['script']).toBe(5);
expect(data['scriptMicroAvg']).toBe(5/4);
done();
});
});
});
describe('gcTimeInScript / gcAmountInScript', () => { describe('gcTimeInScript / gcAmountInScript', () => {
it('should detect gc during script execution with begin/end events', (done) => { it('should detect gc during script execution with begin/end events', (done) => {

View File

@ -85,6 +85,13 @@ export function main() {
}); });
}); });
it('should bind Options.MICRO_ITERATIONS', (done) => {
createRunner().sample({id: 'someId', microIterations: 23}).then( (_) => {
expect(injector.get(Options.MICRO_ITERATIONS)).toEqual(23);
done();
});
});
it('should overwrite bindings per sample call', (done) => { it('should overwrite bindings per sample call', (done) => {
createRunner([ createRunner([
bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1}), bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1}),