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, options)
64 this._benchmark = benchmark;
65 this._options = options;
68 this._dropFrameCount = 1;
69 this._measureFrameCount = 3;
70 this._referenceTime = 0;
71 this._currentTimeOffset = 0;
72 this._estimator = new KalmanEstimator(60);
79 return this._currentTimeOffset - this._startTimeOffset;
84 var currentTime = performance.now();
86 if (!this._referenceTime)
87 this._referenceTime = currentTime;
89 this._currentTimeOffset = currentTime - this._referenceTime;
91 if (!this._frameCount)
92 this._startTimeOffset = this._currentTimeOffset;
96 // Start measuring after dropping _dropFrameCount frames.
97 if (this._frameCount == this._dropFrameCount)
98 this._measureTimeOffset = this._currentTimeOffset;
100 // Drop _dropFrameCount frames and measure the average of _measureFrameCount frames.
101 if (this._frameCount < this._dropFrameCount + this._measureFrameCount)
104 // Get the average FPS of _measureFrameCount frames over measureTimeDelta.
105 var measureTimeDelta = this._currentTimeOffset - this._measureTimeOffset;
106 var currentFrameRate = Math.floor(1000 / (measureTimeDelta / this._measureFrameCount));
108 // Use Kalman filter to get a more non-fluctuating frame rate.
109 if (this._options["estimated-frame-rate"])
110 currentFrameRate = this._estimator.estimate(currentFrameRate);
112 // Adjust the test to reach the desired FPS.
113 var result = this._benchmark.update(this._currentTimeOffset, this.timeDelta(), currentFrameRate);
115 // Start the next drop/measure cycle.
116 this._frameCount = 0;
118 // If result == 0, no more requestAnimationFrame() will be invoked.
122 animateLoop: function(timestamp)
125 requestAnimationFrame(this.animateLoop.bind(this));
129 function Benchmark(options)
131 this._options = options;
132 this._recordInterval = 200;
133 this._isSampling = false;
135 var gain = parseInt(this._options["gain"]) || 1;
136 var lowValue = -parseInt(this._options["addLimit"]) || 1;
137 var highValue = parseInt(this._options["removeLimit"]) || 1;
139 this._controller = new PIDController(gain, this._options["frame-rate"], lowValue, highValue);
140 this._sampler = new Sampler(2);
141 this._state = new BenchmarkState(this._options["test-interval"] * 1000);
144 Benchmark.prototype =
146 // Called from the load event listener or from this.run().
149 this._animator.animateLoop();
152 // Called from the animator to adjust the complexity of the test.
153 update: function(currentTimeOffset, timeDelta, currentFrameRate)
155 this._state.update(currentTimeOffset);
157 var stage = this._state.currentStage();
158 if (stage == BenchmarkState.stages.FINISHED) {
163 if (stage == BenchmarkState.stages.SAMPLING && !this._isSampling) {
164 this._sampler.startSampling(this._state.samplingTimeOffset());
165 this._isSampling = true;
169 if (this._options["complexity"] && !this._options["adaptive-test"]) {
170 // this.tune(0) returns the current complexity of the test.
171 tuneValue = this._options["complexity"] - this.tune(0);
173 else if (!(this._isSampling && this._options["fix-test-complexity"])) {
174 // The relationship between frameRate and test complexity is inverse-proportional so we
175 // need to use the negative of PIDController.tune() to change the complexity of the test.
176 tuneValue = -this._controller.tune(currentFrameRate, timeDelta / 1000);
177 tuneValue = tuneValue > 0 ? Math.floor(tuneValue) : Math.ceil(tuneValue);
180 var currentComplexity = this.tune(tuneValue);
181 this.record(currentTimeOffset, currentComplexity, currentFrameRate);
185 record: function(currentTimeOffset, currentComplexity, currentFrameRate)
187 this._sampler.sample(currentTimeOffset, [currentComplexity, currentFrameRate]);
189 if (typeof this._recordTimeOffset == "undefined")
190 this._recordTimeOffset = currentTimeOffset;
192 var stage = this._state.currentStage();
193 if (stage != BenchmarkState.stages.FINISHED && currentTimeOffset < this._recordTimeOffset + this._recordInterval)
196 this.showResults(this._state.currentMessage(), this._state.currentProgress());
197 this._recordTimeOffset = currentTimeOffset;
204 var promise = new SimplePromise;
206 function resolveWhenFinished() {
207 if (typeof self._state != "undefined" && (self._state.currentStage() == BenchmarkState.stages.FINISHED))
208 return promise.resolve(self._sampler);
209 setTimeout(resolveWhenFinished.bind(self), 50);
212 resolveWhenFinished();
217 window.benchmarkClient = {};
219 // This event listener runs the test if it is loaded outside the benchmark runner.
220 window.addEventListener("load", function()
222 if (window.self !== window.top)
224 window.benchmark = window.benchmarkClient.create(null, null, 30000, 50, null, null);
225 window.benchmark.start();
228 // This function is called from the suite controller run-callback when running the benchmark runner.
229 window.runBenchmark = function(suite, test, options, recordTable, progressBar)
231 var benchmarkOptions = { complexity: test.complexity };
232 benchmarkOptions = Utilities.mergeObjects(benchmarkOptions, options);
233 benchmarkOptions = Utilities.mergeObjects(benchmarkOptions, Utilities.parseParameters());
234 window.benchmark = window.benchmarkClient.create(suite, test, benchmarkOptions, recordTable, progressBar);
235 return window.benchmark.run();