Skip to content

Commit 37c6101

Browse files
committed
Ignore mutation frames when scoring
The RampController works by repeatedly changing the scene complexity, while measuring some number of animated frames at each complexity level. Currently, it tracks the duration of both the mutation frames, and the animation frames, and both contribute to the score. This has two issues: first, it increases noise in the results because there are two types of frames, and the mutation frames generally take longer. This noise can throw off the regression computation used to compute the score. Second, it's not the intent of this benchmark to measure DOM mutation performance. MotionMark is focused on animation, so it makes sense to only measure the animation frames. This change is implemented by having RampController keep track of which are the first frames after a complexity change, via `this._previousFrameComplexity`. Each frame is annotated as a "mutation" frame or an "animation" frame, which is stored as the first item in each sample. At the two locations where we compute Regressions, we filter out the mutation frames so that only animation frames contribute to the regression computations, and therefore the final score. All frames are present in the output JSON, with "m" or "a" annotations. Mutation frames are colored gray in the complexity graph.
1 parent c401061 commit 37c6101

File tree

5 files changed

+54
-20
lines changed

5 files changed

+54
-20
lines changed

MotionMark/resources/debug-runner/graph.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,14 @@ Utilities.extendObject(window.benchmarkController, {
250250
.attr("x1", function(d) { return xScale(d.complexity) - 3; })
251251
.attr("x2", function(d) { return xScale(d.complexity) + 3; })
252252
.attr("y1", function(d) { return yScale(d.frameLength) - 3; })
253-
.attr("y2", function(d) { return yScale(d.frameLength) + 3; });
254-
group.append("line")
253+
.attr("y2", function(d) { return yScale(d.frameLength) + 3; })
254+
.attr("class", function(d) { return d.frameType === "m" ? 'mutation' : 'animation'; });
255+
group.append("line")
255256
.attr("x1", function(d) { return xScale(d.complexity) - 3; })
256257
.attr("x2", function(d) { return xScale(d.complexity) + 3; })
257258
.attr("y1", function(d) { return yScale(d.frameLength) + 3; })
258-
.attr("y2", function(d) { return yScale(d.frameLength) - 3; });
259+
.attr("y2", function(d) { return yScale(d.frameLength) - 3; })
260+
.attr("class", function(d) { return d.frameType === "m" ? 'mutation' : 'animation'; });
259261

260262
// Cursor
261263
var cursorGroup = svg.append("g").attr("class", "cursor hidden");

MotionMark/resources/debug-runner/motionmark.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,10 @@ body.showing-test-graph header, body.showing-test-graph nav {
792792
stroke-width: 1px;
793793
}
794794

795+
#complexity-graph .raw.series line.mutation {
796+
stroke: rgba(255, 255, 255, .15);
797+
}
798+
795799
#complexity-graph .raw.regression line {
796800
stroke: rgba(30, 96%, 86%, .6);
797801
}

MotionMark/resources/runner/motionmark.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,17 @@
111111
maxComplexity = series.getFieldInDatum(maxIndex, Strings.json.complexity);
112112
}
113113

114+
var frameTypeIndex = series.fieldMap[Strings.json.frameType];
114115
var complexityIndex = series.fieldMap[Strings.json.complexity];
115116
var frameLengthIndex = series.fieldMap[Strings.json.frameLength];
116117
var regressionOptions = { desiredFrameLength: desiredFrameLength };
117118
if (profile)
118119
regressionOptions.preferredProfile = profile;
119120

120121
var regressionSamples = series.slice(minIndex, maxIndex + 1);
121-
var regressionData = regressionSamples.data.map((sample) => [ sample[complexityIndex], sample[frameLengthIndex] ]);
122+
var animationSamples = regressionSamples.data.filter((sample) => sample[frameTypeIndex] == Strings.json.animationFrameType);
123+
var regressionData = animationSamples.map((sample) => [ sample[complexityIndex], sample[frameLengthIndex] ]);
124+
122125
var regression = new Regression(regressionData, minIndex, maxIndex, regressionOptions);
123126
return {
124127
minComplexity: minComplexity,

MotionMark/resources/strings.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,15 @@ var Strings = {
4444
samples: "samples",
4545
dataFieldMap: "dataFieldMap",
4646
controller: "controller",
47+
frameType: "frameType",
4748
time: "time",
4849
complexity: "complexity",
4950
frameLength: "frameLength",
5051
smoothedFrameLength: "smoothedFrameLength",
5152

53+
mutationFrameType: "m",
54+
animationFrameType: "a",
55+
5256
result: "result",
5357
configuration: "configuration",
5458
score: "score",

MotionMark/tests/resources/main.js

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ Sampler = Utilities.createClass(
6060
}
6161
});
6262

63-
const sampleTimeIndex = 0;
64-
const sampleComplexityIndex = 1;
65-
const sampleFrameLengthEstimateIndex = 2;
63+
const sampleTypeIndex = 0;
64+
const sampleTimeIndex = 1;
65+
const sampleComplexityIndex = 2;
66+
const sampleFrameLengthEstimateIndex = 3;
6667

6768
Controller = Utilities.createClass(
6869
function(benchmark, options)
@@ -74,7 +75,7 @@ Controller = Utilities.createClass(
7475
this._targetFrameRate = options["frame-rate"];
7576
// Default data series: timestamp, complexity, estimatedFrameLength
7677
var sampleSize = options["sample-capacity"] || (this._targetFrameRate * options["test-interval"] / 1000);
77-
this._sampler = new Sampler(options["series-count"] || 3, sampleSize, this);
78+
this._sampler = new Sampler(options["series-count"] || 4, sampleSize, this);
7879
this._marks = {};
7980

8081
this._frameLengthEstimator = new SimpleKalmanEstimator(options["kalman-process-error"], options["kalman-measurement-error"]);
@@ -102,7 +103,7 @@ Controller = Utilities.createClass(
102103

103104
recordFirstSample: function(startTimestamp, stage)
104105
{
105-
this._sampler.record(startTimestamp, stage.complexity(), -1);
106+
this._sampler.record(Strings.json.mutationFrameType, startTimestamp, stage.complexity(), -1);
106107
this.mark(Strings.json.samplingStartTimeOffset, startTimestamp);
107108
},
108109

@@ -156,6 +157,7 @@ Controller = Utilities.createClass(
156157

157158
update: function(timestamp, stage)
158159
{
160+
const frameType = this._previousFrameComplexity(this._sampler.samples, this._sampler.sampleCount) != stage.complexity() ? Strings.json.mutationFrameType : Strings.json.animationFrameType
159161
var lastFrameLength = timestamp - this._previousTimestamp;
160162
this._previousTimestamp = timestamp;
161163

@@ -166,7 +168,7 @@ Controller = Utilities.createClass(
166168
this._frameLengthEstimator.sample(lastFrameLength);
167169
frameLengthEstimate = this._frameLengthEstimator.estimate;
168170
}
169-
this._sampler.record(timestamp, stage.complexity(), frameLengthEstimate);
171+
this._sampler.record(frameType, timestamp, stage.complexity(), frameLengthEstimate);
170172
} else {
171173
this.registerFrameTime(lastFrameLength);
172174
if (this.intervalHasConcluded(timestamp)) {
@@ -176,13 +178,13 @@ Controller = Utilities.createClass(
176178
this._frameLengthEstimator.sample(intervalAverageFrameLength);
177179
frameLengthEstimate = this._frameLengthEstimator.estimate;
178180
}
179-
this._sampler.record(timestamp, stage.complexity(), frameLengthEstimate);
181+
this._sampler.record(frameType, timestamp, stage.complexity(), frameLengthEstimate);
180182

181183
didFinishInterval = true;
182184
this.didFinishInterval(timestamp, stage, intervalAverageFrameLength);
183185
this._frameLengthEstimator.reset();
184186
} else
185-
this._sampler.record(timestamp, stage.complexity(), frameLengthEstimate);
187+
this._sampler.record(frameType, timestamp, stage.complexity(), frameLengthEstimate);
186188
}
187189

188190
this.tune(timestamp, stage, lastFrameLength, didFinishInterval, intervalAverageFrameLength);
@@ -231,12 +233,14 @@ Controller = Utilities.createClass(
231233

232234
_processControllerSamples: function()
233235
{
234-
const processedSampleTimeIndex = 0;
235-
const processedSampleComplexityIndex = 1;
236-
const processedSampleFrameLengthIndex = 2;
237-
const processedSampleSmoothedFrameLengthIndex = 3;
236+
const processedSampleTypeIndex = 0;
237+
const processedSampleTimeIndex = 1;
238+
const processedSampleComplexityIndex = 2;
239+
const processedSampleFrameLengthIndex = 3;
240+
const processedSampleSmoothedFrameLengthIndex = 4;
238241

239242
var controllerSamples = new SampleData;
243+
controllerSamples.addField(Strings.json.frameType, processedSampleTypeIndex);
240244
controllerSamples.addField(Strings.json.time, processedSampleTimeIndex);
241245
controllerSamples.addField(Strings.json.complexity, processedSampleComplexityIndex);
242246

@@ -249,6 +253,7 @@ Controller = Utilities.createClass(
249253
controllerSamples.push(sample);
250254

251255
// Represent time in milliseconds
256+
controllerSamples.setFieldInDatum(sample, Strings.json.frameType, samples[sampleTypeIndex][i]);
252257
controllerSamples.setFieldInDatum(sample, Strings.json.time, timestamp - this._startTimestamp);
253258
controllerSamples.setFieldInDatum(sample, Strings.json.complexity, samples[sampleComplexityIndex][i]);
254259

@@ -316,7 +321,7 @@ AdaptiveController = Utilities.createSubclass(Controller,
316321

317322
recordFirstSample: function(startTimestamp, stage)
318323
{
319-
this._sampler.record(startTimestamp, stage.complexity(), -1);
324+
this._sampler.record(Strings.json.mutationFrameType, startTimestamp, stage.complexity(), -1);
320325
},
321326

322327
update: function(timestamp, stage)
@@ -330,7 +335,7 @@ AdaptiveController = Utilities.createSubclass(Controller,
330335
++this._intervalFrameCount;
331336

332337
if (this._intervalFrameCount < this._numberOfFramesToMeasurePerInterval) {
333-
this._sampler.record(timestamp, stage.complexity(), -1);
338+
this._sampler.record(Strings.json.animationFrameType, timestamp, stage.complexity(), -1);
334339
return;
335340
}
336341

@@ -342,7 +347,7 @@ AdaptiveController = Utilities.createSubclass(Controller,
342347
tuneValue = tuneValue > 0 ? Math.floor(tuneValue) : Math.ceil(tuneValue);
343348
stage.tune(tuneValue);
344349

345-
this._sampler.record(timestamp, stage.complexity(), this._frameLengthEstimator.estimate);
350+
this._sampler.record(Strings.json.mutationFrameType, timestamp, stage.complexity(), this._frameLengthEstimator.estimate);
346351

347352
// Start the next interval.
348353
this._intervalFrameCount = 0;
@@ -551,8 +556,11 @@ RampController = Utilities.createSubclass(Controller,
551556
}
552557

553558
var regressionData = [];
554-
for (var i = this._rampStartIndex; i < this._sampler.sampleCount; ++i)
559+
for (var i = this._rampStartIndex; i < this._sampler.sampleCount; ++i) {
560+
if (this._getFrameType(this._sampler.samples, i) == Strings.json.mutationFrameType)
561+
continue;
555562
regressionData.push([ this._getComplexity(this._sampler.samples, i), this._getFrameLength(this._sampler.samples, i) ]);
563+
}
556564

557565
var regression = new Regression(regressionData, this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired });
558566
this._rampRegressions.push(regression);
@@ -590,6 +598,11 @@ RampController = Utilities.createSubclass(Controller,
590598
this._possibleMaximumComplexity = this._maximumComplexity;
591599
},
592600

601+
_getFrameType: function(samples, i)
602+
{
603+
return samples[sampleTypeIndex][i];
604+
},
605+
593606
_getComplexity: function(samples, i)
594607
{
595608
return samples[sampleComplexityIndex][i];
@@ -599,6 +612,14 @@ RampController = Utilities.createSubclass(Controller,
599612
{
600613
return samples[sampleTimeIndex][i] - samples[sampleTimeIndex][i - 1];
601614
},
615+
616+
_previousFrameComplexity: function(samples, currentSampleCount)
617+
{
618+
if (currentSampleCount > 0)
619+
return this._getComplexity(samples, currentSampleCount - 1);
620+
621+
return 0;
622+
},
602623

603624
processSamples: function(results)
604625
{

0 commit comments

Comments
 (0)