Skip to content

Commit f7bf9c8

Browse files
vmiurasunnyps
authored andcommitted
Add new experimental 'window' regression function.
1 parent 6d31cee commit f7bf9c8

5 files changed

Lines changed: 138 additions & 6 deletions

File tree

MotionMark/developer.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ <h3>Adjusting the test complexity:</h3>
113113
<select name="score-profile" id="score-profile">
114114
<option value="slope">Slope</option>
115115
<option value="flat">Flat</option>
116+
<option value="window">Windowed</option>
117+
<option value="window-strict">Windowed Strict</option>
116118
</select>
117119
</label>
118120
</li>
@@ -165,6 +167,8 @@ <h1>MotionMark score</h1>
165167
<select id="results-score-profile" onchange="benchmarkController.changeScoreProfile(this.value)">
166168
<option value="slope">Slope</option>
167169
<option value="flat">Flat</option>
170+
<option value="window">Windowed</option>
171+
<option value="window-strict">Windowed Strict</option>
168172
</select>
169173
</label>
170174
</div>
@@ -195,6 +199,8 @@ <h1>Graph:</h1>
195199
<select id="graph-score-profile" onchange="benchmarkController.changeScoreProfile(this.value)">
196200
<option value="slope">Slope</option>
197201
<option value="flat">Flat</option>
202+
<option value="window">Windowed</option>
203+
<option value="window-strict">Windowed Strict</option>
198204
</select>
199205
</label>
200206
</div>

MotionMark/resources/runner/results.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,15 @@ class ScoreCalculator {
187187

188188
const frameTypeIndex = series.fieldMap[Strings.json.frameType];
189189
const complexityIndex = series.fieldMap[complexityKey];
190+
const frameTimeIndex = series.fieldMap[Strings.json.time];
190191
const frameLengthIndex = series.fieldMap[Strings.json.frameLength];
191192
const regressionOptions = { desiredFrameLength: desiredFrameLength };
192193
if (profile)
193194
regressionOptions.preferredProfile = profile;
194195

195196
const regressionSamples = series.slice(minIndex, maxIndex + 1);
196197
const animationSamples = regressionSamples.data.filter((sample) => sample[frameTypeIndex] == Strings.json.animationFrameType);
197-
const regressionData = animationSamples.map((sample) => [ sample[complexityIndex], sample[frameLengthIndex] ]);
198+
const regressionData = animationSamples.map((sample) => [ sample[complexityIndex], sample[frameLengthIndex], sample[frameTimeIndex] ]);
198199

199200
const regression = new Regression(regressionData, minIndex, maxIndex, regressionOptions);
200201
return {

MotionMark/resources/statistics.js

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,10 @@ class Regression {
178178
constructor(samples, startIndex, endIndex, options)
179179
{
180180
const desiredFrameLength = options.desiredFrameLength;
181-
var profile;
181+
const kWindowSizeMultiple = 0.1;
182182

183+
var profile;
184+
183185
if (!options.preferredProfile || options.preferredProfile == Strings.json.profiles.slope) {
184186
profile = this._calculateRegression(samples, {
185187
shouldClip: true,
@@ -195,6 +197,11 @@ class Regression {
195197
t2: 0
196198
});
197199
this.profile = Strings.json.profiles.flat;
200+
} else if (options.preferredProfile == Strings.json.profiles.window || options.preferredProfile == Strings.json.profiles.windowStrict) {
201+
const window_size = Math.max(1, Math.floor(samples.length * kWindowSizeMultiple));
202+
const strict = options.preferredProfile == Strings.json.profiles.windowStrict;
203+
profile = this._windowedFit(samples, desiredFrameLength, window_size, strict);
204+
this.profile = options.preferredProfile;
198205
}
199206

200207
this.startIndex = Math.min(startIndex, endIndex);
@@ -219,6 +226,111 @@ class Regression {
219226
return this.s1 + this.t1 * complexity;
220227
}
221228

229+
_windowedFit(samples, desiredFrameLength, windowSize, strict)
230+
{
231+
const kAllowedErrorFactor = 0.9;
232+
233+
const complexityIndex = 0;
234+
const frameLengthIndex = 1;
235+
const frameTimeIndex = 2;
236+
237+
const average = array => array.reduce((a, b) => a + b) / array.length;
238+
239+
var sortedSamples = samples.slice().sort((a, b) => {
240+
if (a[complexityIndex] == b[complexityIndex])
241+
return b[frameTimeIndex] - a[frameTimeIndex];
242+
return a[complexityIndex] - b[complexityIndex];
243+
});
244+
245+
var cumFrameLength = 0.0;
246+
var bestIndex = 0;
247+
var bestComplexity = 0;
248+
var runningFrameLengths = [];
249+
var runningComplexities = [];
250+
251+
for (var i = 0; i < sortedSamples.length; ++i) {
252+
runningFrameLengths.push(sortedSamples[i][frameLengthIndex]);
253+
runningComplexities.push(sortedSamples[i][complexityIndex]);
254+
255+
if (runningFrameLengths.length < windowSize) {
256+
continue
257+
} else if (runningFrameLengths.length > windowSize) {
258+
runningFrameLengths.shift();
259+
runningComplexities.shift();
260+
}
261+
262+
let averageFrameLength = average(runningFrameLengths);
263+
let averageComplexity = average(runningComplexities);
264+
let error = desiredFrameLength / averageFrameLength;
265+
let adjustedComplexity = averageComplexity * Math.min(1.0, error);
266+
267+
if (error >= kAllowedErrorFactor) {
268+
if (adjustedComplexity > bestComplexity) {
269+
bestComplexity = adjustedComplexity;
270+
}
271+
} else if (strict) {
272+
break;
273+
}
274+
}
275+
276+
for (var i = 0; i < sortedSamples.length; ++i) {
277+
if (sortedSamples[i][complexityIndex] <= bestComplexity)
278+
bestIndex = i;
279+
}
280+
281+
let complexity = bestComplexity;
282+
283+
// Calculate slope for remaining points
284+
let t_nom = 0.0;
285+
let t_denom = 0.0;
286+
for (var i = bestIndex + 1; i < sortedSamples.length; i++) {
287+
const tx = sortedSamples[i][complexityIndex] - complexity;
288+
const ty = sortedSamples[i][frameLengthIndex] - desiredFrameLength;
289+
t_nom += tx * ty;
290+
t_denom += tx * tx;
291+
}
292+
293+
var s1 = desiredFrameLength;
294+
var t1 = 0;
295+
var t2 = (t_nom / t_denom) || 0.0;
296+
var s2 = desiredFrameLength - t2 * complexity;
297+
var n1 = bestIndex + 1;
298+
var n2 = sortedSamples.length - bestIndex - 1;
299+
300+
function getValueAt(at_complexity)
301+
{
302+
if (at_complexity > complexity)
303+
return s2 + t2 * complexity;
304+
return s1 + t1 * complexity;
305+
}
306+
307+
let error1 = 0.0;
308+
let error2 = 0.0;
309+
for (var i = 0; i < n1; ++i) {
310+
const frameLengthErr = sortedSamples[i][frameLengthIndex] - getValueAt(sortedSamples[i][complexityIndex]);
311+
error1 += frameLengthErr * frameLengthErr;
312+
}
313+
for (var i = n1; i < sortedSamples.length; ++i) {
314+
const frameLengthErr = sortedSamples[i][frameLengthIndex] - getValueAt(sortedSamples[i][complexityIndex]);
315+
error2 += frameLengthErr * frameLengthErr;
316+
}
317+
318+
return {
319+
s1: s1,
320+
t1: t1,
321+
s2: s2,
322+
t2: t2,
323+
complexity: complexity,
324+
// Number of samples included in the first segment, inclusive of bestIndex
325+
n1: n1,
326+
// Number of samples included in the second segment
327+
n2: n2,
328+
stdev1: Math.sqrt(error1 / n1),
329+
stdev2: Math.sqrt(error2 / n2),
330+
error: error1 + error2,
331+
};
332+
}
333+
222334
// A generic two-segment piecewise regression calculator. Based on Kundu/Ubhaya
223335
//
224336
// Minimize sum of (y - y')^2

MotionMark/resources/strings.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ var Strings = {
7878

7979
profiles: {
8080
slope: "slope",
81-
flat: "flat"
81+
flat: "flat",
82+
window: "window",
83+
windowStrict: "window-strict",
8284
},
8385

8486
results: {

MotionMark/tests/resources/controllers.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,15 @@ class Controller {
170170
{
171171
return samples[sampleTimeIndex][i] - samples[sampleTimeIndex][i - 1];
172172
}
173+
174+
_getFrameTime(samples, i)
175+
{
176+
return samples[sampleTimeIndex][i];
177+
}
173178

174179
_previousFrameComplexity(samples, i)
175180
{
176-
if (i > 0)
181+
if (i > 1)
177182
return this._getComplexity(samples, i - 1);
178183

179184
return 0;
@@ -415,6 +420,7 @@ class RampController extends Controller {
415420
super(benchmark, options);
416421

417422
this.targetFPS = targetFPS;
423+
this.preferredProfile = options["score-profile"];
418424

419425
// Initially start with a tier test to find the bounds
420426
// The number of objects in a tier test is 10^|_tier|
@@ -613,10 +619,15 @@ class RampController extends Controller {
613619
for (var i = this._rampStartIndex; i < this._sampler.sampleCount; ++i) {
614620
if (this._getFrameType(this._sampler.samples, i) == Strings.json.mutationFrameType)
615621
continue;
616-
regressionData.push([ this._getComplexity(this._sampler.samples, i), this._getFrameLength(this._sampler.samples, i) ]);
622+
regressionData.push(
623+
[
624+
this._getComplexity(this._sampler.samples, i),
625+
this._getFrameLength(this._sampler.samples, i),
626+
this._getFrameTime(this._sampler.samples, i)
627+
]);
617628
}
618629

619-
var regression = new Regression(regressionData, this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired });
630+
var regression = new Regression(regressionData, this._sampler.sampleCount - 1, this._rampStartIndex, { desiredFrameLength: this.frameLengthDesired, preferredProfile: this.preferredProfile });
620631
this._rampRegressions.push(regression);
621632

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

0 commit comments

Comments
 (0)