diff options
Diffstat (limited to 'third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js')
-rw-r--r-- | third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js new file mode 100644 index 0000000000..a248182594 --- /dev/null +++ b/third_party/webkit/PerformanceTests/StyleBench/resources/benchmark-runner.js @@ -0,0 +1,299 @@ +// FIXME: Use the real promise if available. +// FIXME: Make sure this interface is compatible with the real Promise. +function SimplePromise() { + this._chainedPromise = null; + this._callback = null; +} + +SimplePromise.prototype.then = function (callback) { + if (this._callback) + throw "SimplePromise doesn't support multiple calls to then"; + this._callback = callback; + this._chainedPromise = new SimplePromise; + + if (this._resolved) + this.resolve(this._resolvedValue); + + return this._chainedPromise; +} + +SimplePromise.prototype.resolve = function (value) { + if (!this._callback) { + this._resolved = true; + this._resolvedValue = value; + return; + } + + var result = this._callback(value); + if (result instanceof SimplePromise) { + var chainedPromise = this._chainedPromise; + result.then(function (result) { chainedPromise.resolve(result); }); + } else + this._chainedPromise.resolve(result); +} + +function BenchmarkTestStep(testName, testFunction) { + this.name = testName; + this.run = testFunction; +} + +function BenchmarkRunner(suites, client) { + this._suites = suites; + this._prepareReturnValue = null; + this._client = client; +} + +BenchmarkRunner.prototype.waitForElement = function (selector) { + var promise = new SimplePromise; + var contentDocument = this._frame.contentDocument; + + function resolveIfReady() { + var element = contentDocument.querySelector(selector); + if (element) + return promise.resolve(element); + setTimeout(resolveIfReady, 50); + } + + resolveIfReady(); + return promise; +} + +BenchmarkRunner.prototype._removeFrame = function () { + if (this._frame) { + this._frame.parentNode.removeChild(this._frame); + this._frame = null; + } +} + +BenchmarkRunner.prototype._appendFrame = function (src) { + var frame = document.createElement('iframe'); + frame.style.width = '800px'; + frame.style.height = '600px'; + frame.style.border = '0px none'; + frame.style.position = 'absolute'; + frame.setAttribute('scrolling', 'no'); + + var marginLeft = parseInt(getComputedStyle(document.body).marginLeft); + var marginTop = parseInt(getComputedStyle(document.body).marginTop); + if (window.innerWidth > 800 + marginLeft && window.innerHeight > 600 + marginTop) { + frame.style.left = marginLeft + 'px'; + frame.style.top = marginTop + 'px'; + } else { + frame.style.left = '0px'; + frame.style.top = '0px'; + } + + if (this._client && this._client.willAddTestFrame) + this._client.willAddTestFrame(frame); + + document.body.insertBefore(frame, document.body.firstChild); + this._frame = frame; + return frame; +} + +BenchmarkRunner.prototype._waitAndWarmUp = function () { + var startTime = Date.now(); + + function Fibonacci(n) { + if (Date.now() - startTime > 100) + return; + if (n <= 0) + return 0; + else if (n == 1) + return 1; + return Fibonacci(n - 2) + Fibonacci(n - 1); + } + + var promise = new SimplePromise; + setTimeout(function () { + Fibonacci(100); + promise.resolve(); + }, 200); + return promise; +} + +BenchmarkRunner.prototype._writeMark = function(name) { + if (window.performance && window.performance.mark) + window.performance.mark(name); +} + +// This function ought be as simple as possible. Don't even use SimplePromise. +BenchmarkRunner.prototype._runTest = function(suite, test, prepareReturnValue, callback) +{ + var self = this; + var now = window.performance && window.performance.now ? function () { return window.performance.now(); } : Date.now; + + var contentWindow = self._frame.contentWindow; + var contentDocument = self._frame.contentDocument; + + self._writeMark(suite.name + '.' + test.name + '-start'); + var startTime = now(); + test.run(prepareReturnValue, contentWindow, contentDocument); + var endTime = now(); + self._writeMark(suite.name + '.' + test.name + '-sync-end'); + + var syncTime = endTime - startTime; + + var startTime = now(); + setTimeout(function () { + // Some browsers don't immediately update the layout for paint. + // Force the layout here to ensure we're measuring the layout time. + var height = self._frame.contentDocument.body.getBoundingClientRect().height; + var endTime = now(); + self._frame.contentWindow._unusedHeightValue = height; // Prevent dead code elimination. + self._writeMark(suite.name + '.' + test.name + '-async-end'); + callback(syncTime, endTime - startTime, height); + }, 0); +} + +function BenchmarkState(suites) { + this._suites = suites; + this._suiteIndex = -1; + this._testIndex = 0; + this.next(); +} + +BenchmarkState.prototype.currentSuite = function() { + return this._suites[this._suiteIndex]; +} + +BenchmarkState.prototype.currentTest = function () { + var suite = this.currentSuite(); + return suite ? suite.tests[this._testIndex] : null; +} + +BenchmarkState.prototype.next = function () { + this._testIndex++; + + var suite = this._suites[this._suiteIndex]; + if (suite && this._testIndex < suite.tests.length) + return this; + + this._testIndex = 0; + do { + this._suiteIndex++; + } while (this._suiteIndex < this._suites.length && this._suites[this._suiteIndex].disabled); + + return this; +} + +BenchmarkState.prototype.isFirstTest = function () { + return !this._testIndex; +} + +BenchmarkState.prototype.prepareCurrentSuite = function (runner, frame) { + var suite = this.currentSuite(); + var promise = new SimplePromise; + frame.onload = function () { + suite.prepare(runner, frame.contentWindow, frame.contentDocument).then(function (result) { promise.resolve(result); }); + } + frame.src = 'resources/' + suite.url; + return promise; +} + +BenchmarkRunner.prototype.step = function (state) { + if (!state) { + state = new BenchmarkState(this._suites); + this._measuredValues = {tests: {}, total: 0, mean: NaN, geomean: NaN, score: NaN}; + } + + var suite = state.currentSuite(); + if (!suite) { + this._finalize(); + var promise = new SimplePromise; + promise.resolve(); + return promise; + } + + if (state.isFirstTest()) { + this._removeFrame(); + var self = this; + return state.prepareCurrentSuite(this, this._appendFrame()).then(function (prepareReturnValue) { + self._prepareReturnValue = prepareReturnValue; + return self._runTestAndRecordResults(state); + }); + } + + return this._runTestAndRecordResults(state); +} + +BenchmarkRunner.prototype.runAllSteps = function (startingState) { + var nextCallee = this.runAllSteps.bind(this); + this.step(startingState).then(function (nextState) { + if (nextState) + nextCallee(nextState); + }); +} + +BenchmarkRunner.prototype.runMultipleIterations = function (iterationCount) { + var self = this; + var currentIteration = 0; + + this._runNextIteration = function () { + currentIteration++; + if (currentIteration < iterationCount) + self.runAllSteps(); + else if (this._client && this._client.didFinishLastIteration) + this._client.didFinishLastIteration(); + } + + if (this._client && this._client.willStartFirstIteration) + this._client.willStartFirstIteration(iterationCount); + + self.runAllSteps(); +} + +BenchmarkRunner.prototype._runTestAndRecordResults = function (state) { + var promise = new SimplePromise; + var suite = state.currentSuite(); + var test = state.currentTest(); + + if (this._client && this._client.willRunTest) + this._client.willRunTest(suite, test); + + var self = this; + setTimeout(function () { + self._runTest(suite, test, self._prepareReturnValue, function (syncTime, asyncTime) { + var suiteResults = self._measuredValues.tests[suite.name] || {tests:{}, total: 0}; + var total = syncTime + asyncTime; + self._measuredValues.tests[suite.name] = suiteResults; + suiteResults.tests[test.name] = {tests: {'Sync': syncTime, 'Async': asyncTime}, total: total}; + suiteResults.total += total; + + if (self._client && self._client.didRunTest) + self._client.didRunTest(suite, test); + + state.next(); + promise.resolve(state); + }); + }, 0); + return promise; +} + +BenchmarkRunner.prototype._finalize = function () { + this._removeFrame(); + + if (this._client && this._client.didRunSuites) { + var product = 1; + var values = []; + for (var suiteName in this._measuredValues.tests) { + var suiteTotal = this._measuredValues.tests[suiteName].total; + product *= suiteTotal; + values.push(suiteTotal); + } + + values.sort(function (a, b) { return a - b }); // Avoid the loss of significance for the sum. + var total = values.reduce(function (a, b) { return a + b }); + var geomean = Math.pow(product, 1 / values.length); + + var correctionFactor = 1.5; // This factor makes the test score look reasonably fit within 0 to 140. + this._measuredValues.total = total; + this._measuredValues.mean = total / values.length; + this._measuredValues.geomean = geomean; + this._measuredValues.score = 60 * 1000 / geomean / correctionFactor; + this._client.didRunSuites(this._measuredValues); + } + + if (this._runNextIteration) + this._runNextIteration(); +} |