Skip to content

Commit c671706

Browse files
committed
refactor(benchpress): report forced gc metric separately
1 parent ead21c9 commit c671706

File tree

4 files changed

+150
-105
lines changed

4 files changed

+150
-105
lines changed

modules/benchpress/src/metric/perflog_metric.js

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,22 @@ export class PerflogMetric extends Metric {
2424
_setTimeout:Function;
2525
_microMetrics:StringMap<string, string>;
2626
_perfLogFeatures:PerfLogFeatures;
27+
_forceGc:boolean;
2728

2829
/**
2930
* @param driverExtension
3031
* @param setTimeout
3132
* @param microMetrics Name and description of metrics provided via console.time / console.timeEnd
3233
**/
33-
constructor(driverExtension:WebDriverExtension, setTimeout:Function, microMetrics:StringMap<string, string>) {
34+
constructor(driverExtension:WebDriverExtension, setTimeout:Function, microMetrics:StringMap<string, string>, forceGc) {
3435
super();
3536
this._driverExtension = driverExtension;
3637
this._remainingEvents = [];
3738
this._measureCount = 0;
3839
this._setTimeout = setTimeout;
3940
this._microMetrics = microMetrics;
4041
this._perfLogFeatures = driverExtension.perfLogFeatures();
42+
this._forceGc = forceGc;
4143
}
4244

4345
describe():StringMap {
@@ -46,12 +48,16 @@ export class PerflogMetric extends Metric {
4648
'pureScriptTime': 'script execution time in ms, without gc nor render'
4749
};
4850
if (this._perfLogFeatures.render) {
49-
res['renderTime'] = 'render time in and ouside of script in ms';
51+
res['renderTime'] = 'render time in ms';
5052
}
5153
if (this._perfLogFeatures.gc) {
52-
res['gcTime'] = 'gc time in and ouside of script in ms';
54+
res['gcTime'] = 'gc time in ms';
5355
res['gcAmount'] = 'gc amount in kbytes';
5456
res['majorGcTime'] = 'time of major gcs in ms';
57+
if (this._forceGc) {
58+
res['forcedGcTime'] = 'forced gc time in ms';
59+
res['forcedGcAmount'] = 'forced gc amount in kbytes';
60+
}
5561
}
5662
StringMapWrapper.forEach(this._microMetrics, (desc, name) => {
5763
StringMapWrapper.set(res, name, desc);
@@ -60,10 +66,38 @@ export class PerflogMetric extends Metric {
6066
}
6167

6268
beginMeasure():Promise {
69+
var resultPromise = PromiseWrapper.resolve(null);
70+
if (this._forceGc) {
71+
resultPromise = resultPromise.then( (_) => this._driverExtension.gc() );
72+
}
73+
return resultPromise.then( (_) => this._beginMeasure() );
74+
}
75+
76+
endMeasure(restart:boolean):Promise<StringMap> {
77+
if (this._forceGc) {
78+
return this._endPlainMeasureAndMeasureForceGc(restart);
79+
} else {
80+
return this._endMeasure(restart);
81+
}
82+
}
83+
84+
_endPlainMeasureAndMeasureForceGc(restartMeasure:boolean) {
85+
return this._endMeasure(true).then( (measurValues) => {
86+
return this._driverExtension.gc()
87+
.then( (_) => this._endMeasure(restartMeasure) )
88+
.then( (forceGcMeasureValues) => {
89+
StringMapWrapper.set(measurValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
90+
StringMapWrapper.set(measurValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
91+
return measurValues;
92+
});
93+
});
94+
}
95+
96+
_beginMeasure():Promise {
6397
return this._driverExtension.timeBegin(this._markName(this._measureCount++));
6498
}
6599

66-
endMeasure(restart:boolean):Promise<Object> {
100+
_endMeasure(restart:boolean):Promise<StringMap> {
67101
var markName = this._markName(this._measureCount-1);
68102
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
69103
return this._driverExtension.timeEnd(markName, nextMarkName)
@@ -210,9 +244,9 @@ var _MARK_NAME_PREFIX = 'benchpress';
210244
var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
211245
var _BINDINGS = [
212246
bind(PerflogMetric).toFactory(
213-
(driverExtension, setTimeout, microMetrics) =>
214-
new PerflogMetric(driverExtension, setTimeout, microMetrics),
215-
[WebDriverExtension, _SET_TIMEOUT, Options.MICRO_METRICS]
247+
(driverExtension, setTimeout, microMetrics, forceGc) =>
248+
new PerflogMetric(driverExtension, setTimeout, microMetrics, forceGc),
249+
[WebDriverExtension, _SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC]
216250
),
217251
bind(_SET_TIMEOUT).toValue( (fn, millis) => PromiseWrapper.setTimeout(fn, millis) )
218252
];

modules/benchpress/src/sampler.js

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { bind, OpaqueToken } from 'angular2/di';
66
import { Metric } from './metric';
77
import { Validator } from './validator';
88
import { Reporter } from './reporter';
9-
import { WebDriverExtension } from './web_driver_extension';
109
import { WebDriverAdapter } from './web_driver_adapter';
1110

1211
import { Options } from './common_options';
@@ -25,28 +24,24 @@ export class Sampler {
2524
static get BINDINGS() { return _BINDINGS; }
2625

2726
_driver:WebDriverAdapter;
28-
_driverExtension:WebDriverExtension;
2927
_metric:Metric;
3028
_reporter:Reporter;
3129
_validator:Validator;
32-
_forceGc:boolean;
3330
_prepare:Function;
3431
_execute:Function;
3532
_now:Function;
3633

3734
constructor({
38-
driver, driverExtension, metric, reporter, validator, forceGc, prepare, execute, now
35+
driver, metric, reporter, validator, prepare, execute, now
3936
}:{
4037
driver: WebDriverAdapter,
41-
driverExtension: WebDriverExtension, metric: Metric, reporter: Reporter,
38+
metric: Metric, reporter: Reporter,
4239
validator: Validator, prepare: Function, execute: Function, now: Function
4340
}={}) {
4441
this._driver = driver;
45-
this._driverExtension = driverExtension;
4642
this._metric = metric;
4743
this._reporter = reporter;
4844
this._validator = validator;
49-
this._forceGc = forceGc;
5045
this._prepare = prepare;
5146
this._execute = execute;
5247
this._now = now;
@@ -64,22 +59,13 @@ export class Sampler {
6459
}
6560
});
6661
}
67-
return this._gcIfNeeded().then( (_) => loop(new SampleState([], null)) );
68-
}
69-
70-
_gcIfNeeded() {
71-
if (this._forceGc) {
72-
return this._driverExtension.gc();
73-
} else {
74-
return PromiseWrapper.resolve(null);
75-
}
62+
return loop(new SampleState([], null));
7663
}
7764

7865
_iterate(lastState) {
7966
var resultPromise;
8067
if (isPresent(this._prepare)) {
81-
resultPromise = this._driver.waitFor(this._prepare)
82-
.then( (_) => this._gcIfNeeded() );
68+
resultPromise = this._driver.waitFor(this._prepare);
8369
} else {
8470
resultPromise = PromiseWrapper.resolve(null);
8571
}
@@ -88,7 +74,6 @@ export class Sampler {
8874
}
8975
return resultPromise
9076
.then( (_) => this._driver.waitFor(this._execute) )
91-
.then( (_) => this._gcIfNeeded() )
9277
.then( (_) => this._metric.endMeasure(isBlank(this._prepare)) )
9378
.then( (measureValues) => this._report(lastState, measureValues) );
9479
}
@@ -118,13 +103,11 @@ export class SampleState {
118103

119104
var _BINDINGS = [
120105
bind(Sampler).toFactory(
121-
(driver, driverExtension, metric, reporter, validator, forceGc, prepare, execute, now) => new Sampler({
106+
(driver, metric, reporter, validator, prepare, execute, now) => new Sampler({
122107
driver: driver,
123-
driverExtension: driverExtension,
124108
reporter: reporter,
125109
validator: validator,
126110
metric: metric,
127-
forceGc: forceGc,
128111
// TODO(tbosch): DI right now does not support null/undefined objects
129112
// Mostly because the cache would have to be initialized with a
130113
// special null object, which is expensive.
@@ -133,8 +116,8 @@ var _BINDINGS = [
133116
now: now
134117
}),
135118
[
136-
WebDriverAdapter, WebDriverExtension, Metric, Reporter, Validator,
137-
Options.FORCE_GC, Options.PREPARE, Options.EXECUTE, Options.NOW
119+
WebDriverAdapter, Metric, Reporter, Validator,
120+
Options.PREPARE, Options.EXECUTE, Options.NOW
138121
]
139122
)
140123
];

modules/benchpress/test/metric/perflog_metric_spec.js

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function main() {
2727
var commandLog;
2828
var eventFactory = new TraceEventFactory('timeline', 'pid0');
2929

30-
function createMetric(perfLogs, microMetrics = null, perfLogFeatures = null) {
30+
function createMetric(perfLogs, microMetrics = null, perfLogFeatures = null, forceGc = null) {
3131
commandLog = [];
3232
if (isBlank(perfLogFeatures)) {
3333
perfLogFeatures = new PerfLogFeatures({render: true, gc: true});
@@ -45,34 +45,48 @@ export function main() {
4545
}),
4646
bind(WebDriverExtension).toValue(new MockDriverExtension(perfLogs, commandLog, perfLogFeatures))
4747
];
48+
if (isPresent(forceGc)) {
49+
ListWrapper.push(bindings, bind(Options.FORCE_GC).toValue(forceGc));
50+
}
4851
return Injector.resolveAndCreate(bindings).get(PerflogMetric);
4952
}
5053

5154
describe('perflog metric', () => {
5255

53-
it('should describe itself based on the perfLogFeatrues', () => {
54-
expect(createMetric([[]], null, new PerfLogFeatures()).describe()).toEqual({
55-
'scriptTime': 'script execution time in ms, including gc and render',
56-
'pureScriptTime': 'script execution time in ms, without gc nor render'
56+
function sortedKeys(stringMap) {
57+
var res = [];
58+
StringMapWrapper.forEach(stringMap, (_, key) => {
59+
ListWrapper.push(res, key);
5760
});
61+
res.sort();
62+
return res;
63+
}
64+
65+
it('should describe itself based on the perfLogFeatrues', () => {
66+
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures()).describe())).toEqual([
67+
'pureScriptTime', 'scriptTime'
68+
]);
5869

59-
expect(createMetric([[]], null, new PerfLogFeatures({
70+
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures({
6071
render: true,
6172
gc: false
62-
})).describe()).toEqual({
63-
'scriptTime': 'script execution time in ms, including gc and render',
64-
'pureScriptTime': 'script execution time in ms, without gc nor render',
65-
'renderTime': 'render time in and ouside of script in ms',
66-
});
73+
})).describe())).toEqual([
74+
'pureScriptTime', 'renderTime', 'scriptTime'
75+
]);
6776

68-
expect(createMetric([[]]).describe()).toEqual({
69-
'scriptTime': 'script execution time in ms, including gc and render',
70-
'pureScriptTime': 'script execution time in ms, without gc nor render',
71-
'renderTime': 'render time in and ouside of script in ms',
72-
'gcTime': 'gc time in and ouside of script in ms',
73-
'gcAmount': 'gc amount in kbytes',
74-
'majorGcTime': 'time of major gcs in ms'
75-
});
77+
expect(sortedKeys(createMetric([[]]).describe())).toEqual([
78+
'gcAmount', 'gcTime', 'majorGcTime',
79+
'pureScriptTime', 'renderTime', 'scriptTime'
80+
]);
81+
82+
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures({
83+
render: true,
84+
gc: true
85+
}), true).describe())).toEqual([
86+
'forcedGcAmount', 'forcedGcTime',
87+
'gcAmount', 'gcTime', 'majorGcTime',
88+
'pureScriptTime', 'renderTime', 'scriptTime'
89+
]);
7690
});
7791

7892
it('should describe itself based on micro metrics', () => {
@@ -84,7 +98,7 @@ export function main() {
8498

8599
describe('beginMeasure', () => {
86100

87-
it('should mark the timeline', inject([AsyncTestCompleter], (async) => {
101+
it('should not force gc and mark the timeline', inject([AsyncTestCompleter], (async) => {
88102
var metric = createMetric([[]]);
89103
metric.beginMeasure().then((_) => {
90104
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
@@ -93,6 +107,15 @@ export function main() {
93107
});
94108
}));
95109

110+
it('should force gc and mark the timeline', inject([AsyncTestCompleter], (async) => {
111+
var metric = createMetric([[]], null, null, true);
112+
metric.beginMeasure().then((_) => {
113+
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
114+
115+
async.done();
116+
});
117+
}));
118+
96119
});
97120

98121
describe('endMeasure', () => {
@@ -200,6 +223,57 @@ export function main() {
200223
});
201224
}));
202225

226+
describe('with forced gc', () => {
227+
var events;
228+
beforeEach( () => {
229+
events = [
230+
[
231+
eventFactory.markStart('benchpress0', 0),
232+
eventFactory.start('script', 4),
233+
eventFactory.end('script', 6),
234+
eventFactory.markEnd('benchpress0', 10),
235+
eventFactory.markStart('benchpress1', 11),
236+
eventFactory.start('gc', 12, {'usedHeapSize': 2500}),
237+
eventFactory.end('gc', 15, {'usedHeapSize': 1000}),
238+
eventFactory.markEnd('benchpress1', 20)
239+
]
240+
];
241+
});
242+
243+
it('should measure forced gc', inject([AsyncTestCompleter], (async) => {
244+
var metric = createMetric(events, null, null, true);
245+
metric.beginMeasure()
246+
.then( (_) => metric.endMeasure(false) )
247+
.then( (data) => {
248+
expect(commandLog).toEqual([
249+
['gc'],
250+
['timeBegin', 'benchpress0'],
251+
['timeEnd', 'benchpress0', 'benchpress1'],
252+
'readPerfLog',
253+
['gc'],
254+
['timeEnd', 'benchpress1', null],
255+
'readPerfLog'
256+
]);
257+
expect(data['forcedGcTime']).toBe(3);
258+
expect(data['forcedGcAmount']).toBe(1.5);
259+
260+
async.done();
261+
});
262+
}));
263+
264+
it('should restart after the forced gc if needed', inject([AsyncTestCompleter], (async) => {
265+
var metric = createMetric(events, null, null, true);
266+
metric.beginMeasure()
267+
.then( (_) => metric.endMeasure(true) )
268+
.then( (data) => {
269+
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
270+
271+
async.done();
272+
});
273+
}));
274+
275+
});
276+
203277
});
204278

205279
describe('aggregation', () => {
@@ -403,4 +477,9 @@ class MockDriverExtension extends WebDriverExtension {
403477
return PromiseWrapper.resolve([]);
404478
}
405479
}
480+
481+
gc():Promise {
482+
ListWrapper.push(this._commandLog, ['gc']);
483+
return PromiseWrapper.resolve(null);
484+
}
406485
}

0 commit comments

Comments
 (0)