1 function BenchmarkState(testInterval)
3 this._currentTimeOffset = 0;
4 this._stageInterval = testInterval / BenchmarkState.stages.FINISHED;
7 // The enum values and the messages should be in the same order
8 BenchmarkState.stages = {
12 messages: [ "Warming up", "Sampling", "Finished" ]
15 BenchmarkState.prototype =
17 _timeOffset: function(stage)
19 return stage * this._stageInterval;
22 _message: function(stage, timeOffset)
24 if (stage == BenchmarkState.stages.FINISHED)
25 return BenchmarkState.stages.messages[stage];
27 return BenchmarkState.stages.messages[stage] + "... ("
28 + Math.floor((timeOffset - this._timeOffset(stage)) / 1000) + "/"
29 + Math.floor((this._timeOffset(stage + 1) - this._timeOffset(stage)) / 1000) + ")";
32 update: function(currentTimeOffset)
34 this._currentTimeOffset = currentTimeOffset;
37 samplingTimeOffset: function()
39 return this._timeOffset(BenchmarkState.stages.SAMPLING);
42 currentStage: function()
44 for (var stage = BenchmarkState.stages.WARMING_UP; stage < BenchmarkState.stages.FINISHED; ++stage) {
45 if (this._currentTimeOffset < this._timeOffset(stage + 1))
48 return BenchmarkState.stages.FINISHED;
51 currentMessage: function()
53 return this._message(this.currentStage(), this._currentTimeOffset);
56 currentProgress: function()
58 return this._currentTimeOffset / this._timeOffset(BenchmarkState.stages.FINISHED);
62 function Animator(benchmark)
64 this._benchmark = benchmark;
66 this._dropFrameCount = 1;
67 this._measureFrameCount = 3;
68 this._referenceTime = 0;
69 this._currentTimeOffset = 0;
70 this._estimator = new KalmanEstimator();
77 this._intervalId = setInterval(this.animate.bind(this), 1);
82 return this._currentTimeOffset - this._startTimeOffset;
87 var currentTime = performance.now();
89 if (!this._referenceTime)
90 this._referenceTime = currentTime;
92 this._currentTimeOffset = currentTime - this._referenceTime;
94 if (!this._frameCount)
95 this._startTimeOffset = this._currentTimeOffset;
99 // Start measuring after dropping _dropFrameCount frames.
100 if (this._frameCount == this._dropFrameCount)
101 this._measureTimeOffset = this._currentTimeOffset;
103 // Drop _dropFrameCount frames and measure the average of _measureFrameCount frames.
104 if (this._frameCount < this._dropFrameCount + this._measureFrameCount)
107 // Get the average FPS of _measureFrameCount frames over measureTimeDelta.
108 var measureTimeDelta = this._currentTimeOffset - this._measureTimeOffset;
109 var currentFrameRate = Math.floor(1000 / (measureTimeDelta / this._measureFrameCount));
111 // Use Kalman filter to get a more non-fluctuating frame rate.
112 if (this._benchmark.options["estimated-frame-rate"])
113 currentFrameRate = this._estimator.estimate(measureTimeDelta, currentFrameRate);
115 // Adjust the test to reach the desired FPS.
116 var result = this._benchmark.update(this._currentTimeOffset, this.timeDelta(), currentFrameRate);
118 // Stop the animator if the benchmark has finished.
119 if (!result && typeof this._intervalId != "undefined")
120 clearInterval(this._intervalId);
122 // Start the next drop/measure cycle.
123 this._frameCount = 0;
125 // result may stop the animator if requestAnimationFrame() has been used.
129 animateLoop: function(timestamp)
132 requestAnimationFrame(this.animateLoop.bind(this));
136 function Benchmark(options)
138 this._options = options;
139 this._method = this._options["method"] || "requestAnimationFrame";
141 this.options = options;
142 this._recordInterval = 200;
143 this._isSampling = false;
145 var gain = parseInt(this._options["gain"]) || 1;
146 var lowValue = -parseInt(this._options["addLimit"]) || 1;
147 var highValue = parseInt(this._options["removeLimit"]) || 1;
149 this._controller = new PIDController(gain, options["frame-rate"], lowValue, highValue);
150 this._sampler = new Sampler(2);
151 this._state = new BenchmarkState(this.options["test-interval"] * 1000);
154 Benchmark.prototype =
156 // Called from the load event listener or from this.run().
159 if (this._method == "setInterval")
160 this._animator.start();
162 this._animator.animateLoop();
165 // Called from the animator to adjust the complexity of the test.
166 update: function(currentTimeOffset, timeDelta, currentFrameRate)
168 this._state.update(currentTimeOffset);
170 var stage = this._state.currentStage();
171 if (stage == BenchmarkState.stages.FINISHED) {
176 if (stage == BenchmarkState.stages.SAMPLING && !this._isSampling) {
177 this._sampler.startSampling(this._state.samplingTimeOffset());
178 this._isSampling = true;
182 if (!(this._isSampling && this.options["fix-test-complexity"])) {
183 // The relationship between frameRate and test complexity is inverse-proportional so we
184 // need to use the negative of PIDController.tune() to change the complexity of the test.
185 tuneValue = -this._controller.tune(currentFrameRate, timeDelta / 1000);
186 tuneValue = tuneValue > 0 ? Math.floor(tuneValue) : Math.ceil(tuneValue);
189 var currentComplexity = this.tune(tuneValue);
190 this.record(currentTimeOffset, currentComplexity, currentFrameRate);
194 record: function(currentTimeOffset, currentComplexity, currentFrameRate)
196 this._sampler.sample(currentTimeOffset, [currentComplexity, currentFrameRate]);
198 if (typeof this._recordTimeOffset == "undefined")
199 this._recordTimeOffset = currentTimeOffset;
201 var stage = this._state.currentStage();
202 if (stage != BenchmarkState.stages.FINISHED && currentTimeOffset < this._recordTimeOffset + this._recordInterval)
205 this.showResults(this._state.currentMessage(), this._state.currentProgress());
206 this._recordTimeOffset = currentTimeOffset;
213 var promise = new SimplePromise;
215 function resolveWhenFinished() {
216 if (typeof self._state != "undefined" && (self._state.currentStage() == BenchmarkState.stages.FINISHED))
217 return promise.resolve(self._sampler);
218 setTimeout(resolveWhenFinished.bind(self), 50);
221 resolveWhenFinished();
226 window.benchmarkClient = {};
228 // This event listener runs the test if it is loaded outside the benchmark runner.
229 window.addEventListener("load", function()
231 if (window.self !== window.top)
233 window.benchmark = window.benchmarkClient.create(null, null, 30000, 50, null, null);
234 window.benchmark.start();
237 // This function is called from the suite controller run-callback when running the benchmark runner.
238 window.runBenchmark = function(suite, test, options, recordTable, progressBar)
240 var mergedOptions = Utilities.mergeObjects(options, Utilities.parseParameters());
241 window.benchmark = window.benchmarkClient.create(suite, test, mergedOptions, recordTable, progressBar);
242 return window.benchmark.run();