angular/modules/benchpress/src/sampler.js
Tobias Bosch 21a293b017 refactor(bench press): rename metrics and adapt them to the features of the browser
* Rename metrics, add `Time` suffix to all so that they are
  more consistent
* iOS does not give us `gc` metrics, so they should not be reported
* Rename `scriptMicroAvg` into `microScriptTimeAvg`
* Rename previous `script` metric into `pureScriptTime` metric,
  and keep `scriptTime` metric as the overall time, so that we still
  have a shared metric across devices independent of the supported
  browser features
* `microScriptTimeAvg` is now based on overall `scriptTime`, including
  gc and render time.
* Move more shared DI tokens into `common_options` (previously
  `sample_options`).

Closes #930
2015-03-13 20:46:37 -07:00

140 lines
4.6 KiB
JavaScript

import { isPresent, isBlank, Date, DateWrapper } from 'angular2/src/facade/lang';
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
import { StringMapWrapper, StringMap, List, ListWrapper } from 'angular2/src/facade/collection';
import { bind, OpaqueToken } from 'angular2/di';
import { Metric } from './metric';
import { Validator } from './validator';
import { Reporter } from './reporter';
import { WebDriverExtension } from './web_driver_extension';
import { WebDriverAdapter } from './web_driver_adapter';
import { Options } from './common_options';
import { MeasureValues} from './measure_values';
/**
* The Sampler owns the sample loop:
* 1. calls the prepare/execute callbacks,
* 2. gets data from the metric
* 3. asks the validator for a valid sample
* 4. reports the new data to the reporter
* 5. loop until there is a valid sample
*/
export class Sampler {
// TODO(tbosch): use static values when our transpiler supports them
static get BINDINGS() { return _BINDINGS; }
_driver:WebDriverAdapter;
_driverExtension:WebDriverExtension;
_metric:Metric;
_reporter:Reporter;
_validator:Validator;
_forceGc:boolean;
_prepare:Function;
_execute:Function;
_now:Function;
constructor({
driver, driverExtension, metric, reporter, validator, forceGc, prepare, execute, now
}:{
driver: WebDriverAdapter,
driverExtension: WebDriverExtension, metric: Metric, reporter: Reporter,
validator: Validator, prepare: Function, execute: Function, now: Function
}={}) {
this._driver = driver;
this._driverExtension = driverExtension;
this._metric = metric;
this._reporter = reporter;
this._validator = validator;
this._forceGc = forceGc;
this._prepare = prepare;
this._execute = execute;
this._now = now;
}
sample():Promise<SampleState> {
var loop;
loop = (lastState) => {
return this._iterate(lastState)
.then( (newState) => {
if (isPresent(newState.validSample)) {
return newState;
} else {
return loop(newState);
}
});
}
return this._gcIfNeeded().then( (_) => loop(new SampleState([], null)) );
}
_gcIfNeeded() {
if (this._forceGc) {
return this._driverExtension.gc();
} else {
return PromiseWrapper.resolve(null);
}
}
_iterate(lastState) {
var resultPromise;
if (isPresent(this._prepare)) {
resultPromise = this._driver.waitFor(this._prepare)
.then( (_) => this._gcIfNeeded() );
} else {
resultPromise = PromiseWrapper.resolve(null);
}
if (isPresent(this._prepare) || lastState.completeSample.length === 0) {
resultPromise = resultPromise.then( (_) => this._metric.beginMeasure() );
}
return resultPromise
.then( (_) => this._driver.waitFor(this._execute) )
.then( (_) => this._gcIfNeeded() )
.then( (_) => this._metric.endMeasure(isBlank(this._prepare)) )
.then( (measureValues) => this._report(lastState, measureValues) );
}
_report(state:SampleState, metricValues:StringMap):Promise<SampleState> {
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
var completeSample = ListWrapper.concat(state.completeSample, [measureValues]);
var validSample = this._validator.validate(completeSample);
var resultPromise = this._reporter.reportMeasureValues(measureValues);
if (isPresent(validSample)) {
resultPromise = resultPromise.then( (_) => this._reporter.reportSample(completeSample, validSample) )
}
return resultPromise.then( (_) => new SampleState(completeSample, validSample) );
}
}
export class SampleState {
completeSample:List;
validSample:List;
constructor(completeSample: List, validSample: List) {
this.completeSample = completeSample;
this.validSample = validSample;
}
}
var _BINDINGS = [
bind(Sampler).toFactory(
(driver, driverExtension, metric, reporter, validator, forceGc, prepare, execute, now) => new Sampler({
driver: driver,
driverExtension: driverExtension,
reporter: reporter,
validator: validator,
metric: metric,
forceGc: forceGc,
// TODO(tbosch): DI right now does not support null/undefined objects
// Mostly because the cache would have to be initialized with a
// special null object, which is expensive.
prepare: prepare !== false ? prepare : null,
execute: execute,
now: now
}),
[
WebDriverAdapter, WebDriverExtension, Metric, Reporter, Validator,
Options.FORCE_GC, Options.PREPARE, Options.EXECUTE, Options.NOW
]
)
];