Skip to content

Commit c401061

Browse files
committed
Simplify regression computation
The Regression class took index and function arguments to extract complexity and frame length from the supplied data, which makes it hard to adjust in the face of frame skipping which will come in a future patch. Simplify by having the Regression class operate on a self-contained Array of x,y values (here, complexity and frame length). This also allows for removal of the complex logic that tracks if it's iterating forwards or backwards. The two callers, in `RampController` and `calculateScore()`, build the input Array by copying out of their respective data stores. There should be no behavior change.
1 parent da95272 commit c401061

File tree

3 files changed

+45
-34
lines changed

3 files changed

+45
-34
lines changed

MotionMark/resources/runner/motionmark.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,15 @@
116116
var regressionOptions = { desiredFrameLength: desiredFrameLength };
117117
if (profile)
118118
regressionOptions.preferredProfile = profile;
119+
120+
var regressionSamples = series.slice(minIndex, maxIndex + 1);
121+
var regressionData = regressionSamples.data.map((sample) => [ sample[complexityIndex], sample[frameLengthIndex] ]);
122+
var regression = new Regression(regressionData, minIndex, maxIndex, regressionOptions);
119123
return {
120124
minComplexity: minComplexity,
121125
maxComplexity: maxComplexity,
122-
samples: series.slice(minIndex, maxIndex + 1),
123-
regression: new Regression(
124-
series.data,
125-
function (data, i) { return data[i][complexityIndex]; },
126-
function (data, i) { return data[i][frameLengthIndex]; },
127-
minIndex, maxIndex, regressionOptions)
126+
samples: regressionSamples,
127+
regression: regression,
128128
};
129129
}
130130

MotionMark/resources/statistics.js

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -177,21 +177,22 @@ Experiment.defaults =
177177
};
178178

179179
Regression = Utilities.createClass(
180-
function(samples, getComplexity, getFrameLength, startIndex, endIndex, options)
180+
// `samples` is [ [ complexity, frameLength ], [ complexity, frameLength ], ... ]
181+
// All samples are analyzed. startIndex, endIndex are just stored for use by the caller.
182+
function(samples, startIndex, endIndex, options)
181183
{
182184
const desiredFrameLength = options.desiredFrameLength;
183185
var profile;
184186

185187
if (!options.preferredProfile || options.preferredProfile == Strings.json.profiles.slope) {
186-
profile = this._calculateRegression(samples, getComplexity, getFrameLength, startIndex, endIndex, {
188+
profile = this._calculateRegression(samples, {
187189
shouldClip: true,
188190
s1: desiredFrameLength,
189191
t1: 0
190192
});
191193
this.profile = Strings.json.profiles.slope;
192-
}
193-
else if (options.preferredProfile == Strings.json.profiles.flat) {
194-
profile = this._calculateRegression(samples, getComplexity, getFrameLength, startIndex, endIndex, {
194+
} else if (options.preferredProfile == Strings.json.profiles.flat) {
195+
profile = this._calculateRegression(samples, {
195196
shouldClip: true,
196197
s1: desiredFrameLength,
197198
t1: 0,
@@ -233,11 +234,14 @@ Regression = Utilities.createClass(
233234
//
234235
// x is assumed to be complexity, y is frame length. Can be used for pure complexity-FPS
235236
// analysis or for ramp controllers since complexity monotonically decreases with time.
236-
_calculateRegression: function(samples, getComplexity, getFrameLength, startIndex, endIndex, options)
237+
_calculateRegression: function(samples, options)
237238
{
238-
if (startIndex == endIndex) {
239+
const complexityIndex = 0;
240+
const frameLengthIndex = 1;
241+
242+
if (samples.length == 1) {
239243
// Only one sample point; we can't calculate any regression.
240-
var x = getComplexity(samples, startIndex);
244+
var x = samples[0][complexityIndex];
241245
return {
242246
complexity: x,
243247
s1: x,
@@ -249,17 +253,19 @@ Regression = Utilities.createClass(
249253
};
250254
}
251255

256+
// Sort by increasing complexity.
257+
var sortedSamples = samples.slice().sort((a, b) => a[complexityIndex] - b[complexityIndex]);
258+
252259
// x is expected to increase in complexity
253-
var iterationDirection = endIndex > startIndex ? 1 : -1;
254-
var lowComplexity = getComplexity(samples, startIndex);
255-
var highComplexity = getComplexity(samples, endIndex);
260+
var lowComplexity = sortedSamples[0][complexityIndex];
261+
var highComplexity = sortedSamples[samples.length - 1][complexityIndex];
262+
256263
var a1 = 0, b1 = 0, c1 = 0, d1 = 0, h1 = 0, k1 = 0;
257264
var a2 = 0, b2 = 0, c2 = 0, d2 = 0, h2 = 0, k2 = 0;
258265

259-
// Iterate from low to high complexity
260-
for (var i = startIndex; iterationDirection * (endIndex - i) > -1; i += iterationDirection) {
261-
var x = getComplexity(samples, i);
262-
var y = getFrameLength(samples, i);
266+
for (var i = 0; i < sortedSamples.length; ++i) {
267+
var x = sortedSamples[i][complexityIndex];
268+
var y = sortedSamples[i][frameLengthIndex];
263269
a2 += 1;
264270
b2 += x;
265271
c2 += x * x;
@@ -279,9 +285,9 @@ Regression = Utilities.createClass(
279285
t2_best = t2;
280286
error2_best = error2;
281287
// Number of samples included in the first segment, inclusive of splitIndex
282-
n1_best = iterationDirection * (splitIndex - startIndex) + 1;
288+
n1_best = splitIndex + 1;
283289
// Number of samples included in the second segment
284-
n2_best = iterationDirection * (endIndex - splitIndex);
290+
n2_best = samples.length - splitIndex - 1;
285291
if (!options.shouldClip || (x_prime >= lowComplexity && x_prime <= highComplexity))
286292
x_best = x_prime;
287293
else {
@@ -290,21 +296,21 @@ Regression = Utilities.createClass(
290296
}
291297
}
292298

293-
// Iterate from startIndex to endIndex - 1, inclusive
294-
for (var i = startIndex; iterationDirection * (endIndex - i) > 0; i += iterationDirection) {
295-
var x = getComplexity(samples, i);
296-
var y = getFrameLength(samples, i);
299+
// Iterate from 0 to n - 2, inclusive
300+
for (var i = 0; i < sortedSamples.length - 1; ++i) {
301+
var x = sortedSamples[i][complexityIndex];
302+
var y = sortedSamples[i][frameLengthIndex];
297303
var xx = x * x;
298304
var yx = y * x;
299305
var yy = y * y;
300-
// a1, b1, etc. is sum from startIndex to i, inclusive
306+
// a1, b1, etc. is sum from 0 to i, inclusive
301307
a1 += 1;
302308
b1 += x;
303309
c1 += xx;
304310
d1 += y;
305311
h1 += yx;
306312
k1 += yy;
307-
// a2, b2, etc. is sum from i+1 to endIndex, inclusive
313+
// a2, b2, etc. is sum from i+1 to sortedSamples.length - 1, inclusive
308314
a2 -= 1;
309315
b2 -= x;
310316
c2 -= xx;
@@ -328,7 +334,7 @@ Regression = Utilities.createClass(
328334
var error1 = (k1 + a1*s1*s1 + c1*t1*t1 - 2*d1*s1 - 2*h1*t1 + 2*b1*s1*t1) || Number.MAX_VALUE;
329335
var error2 = (k2 + a2*s2*s2 + c2*t2*t2 - 2*d2*s2 - 2*h2*t2 + 2*b2*s2*t2) || Number.MAX_VALUE;
330336

331-
if (i == startIndex) {
337+
if (i == 0) {
332338
setBest(s1, t1, error1, s2, t2, error2, i, x_prime, x);
333339
continue;
334340
}
@@ -337,7 +343,8 @@ Regression = Utilities.createClass(
337343
continue;
338344

339345
// Projected point is not between this and the next sample
340-
if (x_prime > getComplexity(samples, i + iterationDirection) || x_prime < x) {
346+
var nextSampleComplexity = sortedSamples[i + 1][complexityIndex];
347+
if (x_prime > nextSampleComplexity || x_prime < x) {
341348
// Calculate lambda, which divides the weight of this sample between the two lines
342349

343350
// These values remove the influence of this sample

MotionMark/tests/resources/main.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ Sampler = Utilities.createClass(
3636
this.sampleCount = 0;
3737
}, {
3838

39-
record: function() {
39+
record: function()
40+
{
4041
// Assume that arguments.length == this.samples.length
4142
for (var i = 0; i < arguments.length; i++) {
4243
this.samples[i][this.sampleCount] = arguments[i];
@@ -549,8 +550,11 @@ RampController = Utilities.createSubclass(Controller,
549550
return;
550551
}
551552

552-
var regression = new Regression(this._sampler.samples, this._getComplexity, this._getFrameLength,
553-
this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired });
553+
var regressionData = [];
554+
for (var i = this._rampStartIndex; i < this._sampler.sampleCount; ++i)
555+
regressionData.push([ this._getComplexity(this._sampler.samples, i), this._getFrameLength(this._sampler.samples, i) ]);
556+
557+
var regression = new Regression(regressionData, this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired });
554558
this._rampRegressions.push(regression);
555559

556560
var frameLengthAtMaxComplexity = regression.valueAt(this._maximumComplexity);

0 commit comments

Comments
 (0)