1 // There are tests for computeStatistics() located in LayoutTests/fast/harness/perftests
3 if (window.testRunner) {
4 testRunner.waitUntilDone();
5 testRunner.dumpAsText();
10 var completedIterations = -1;
11 var callsPerIteration = 1;
12 var currentTest = null;
14 var jsHeapResults = [];
15 var mallocHeapResults = [];
16 var iterationCount = undefined;
18 var PerfTestRunner = {};
20 // To make the benchmark results predictable, we replace Math.random with a
21 // 100% deterministic alternative.
22 PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed = 49734321;
24 PerfTestRunner.resetRandomSeed = function() {
25 PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed
28 PerfTestRunner.random = Math.random = function() {
29 // Robert Jenkins' 32 bit integer hash function.
30 var randomSeed = PerfTestRunner.randomSeed;
31 randomSeed = ((randomSeed + 0x7ed55d16) + (randomSeed << 12)) & 0xffffffff;
32 randomSeed = ((randomSeed ^ 0xc761c23c) ^ (randomSeed >>> 19)) & 0xffffffff;
33 randomSeed = ((randomSeed + 0x165667b1) + (randomSeed << 5)) & 0xffffffff;
34 randomSeed = ((randomSeed + 0xd3a2646c) ^ (randomSeed << 9)) & 0xffffffff;
35 randomSeed = ((randomSeed + 0xfd7046c5) + (randomSeed << 3)) & 0xffffffff;
36 randomSeed = ((randomSeed ^ 0xb55a4f09) ^ (randomSeed >>> 16)) & 0xffffffff;
37 PerfTestRunner.randomSeed = randomSeed;
38 return (randomSeed & 0xfffffff) / 0x10000000;
41 PerfTestRunner.now = window.performance && window.performance.now ? function () { return window.performance.now(); } : Date.now;
43 PerfTestRunner.logInfo = function (text) {
44 if (!window.testRunner)
48 PerfTestRunner.loadFile = function (path) {
49 var xhr = new XMLHttpRequest();
50 xhr.open("GET", path, false);
52 return xhr.responseText;
55 PerfTestRunner.computeStatistics = function (times, unit) {
56 var data = times.slice();
58 // Add values from the smallest to the largest to avoid the loss of significance
59 data.sort(function(a,b){return a-b;});
61 var middle = Math.floor(data.length / 2);
64 max: data[data.length - 1],
65 median: data.length % 2 ? data[middle] : (data[middle - 1] + data[middle]) / 2,
68 // Compute the mean and variance using Knuth's online algorithm (has good numerical stability).
70 result.values = times;
72 for (var i = 0; i < data.length; ++i) {
74 var delta = x - result.mean;
76 result.mean += delta / sweep;
77 squareSum += delta * (x - result.mean);
79 result.variance = data.length <= 1 ? 0 : squareSum / (data.length - 1);
80 result.stdev = Math.sqrt(result.variance);
81 result.unit = unit || "ms";
86 PerfTestRunner.logStatistics = function (values, unit, title) {
87 var statistics = this.computeStatistics(values, unit);
90 if (statistics.values)
91 this.log("values " + statistics.values.join(", ") + " " + statistics.unit);
92 this.log("avg " + statistics.mean + " " + statistics.unit);
93 this.log("median " + statistics.median + " " + statistics.unit);
94 this.log("stdev " + statistics.stdev + " " + statistics.unit);
95 this.log("min " + statistics.min + " " + statistics.unit);
96 this.log("max " + statistics.max + " " + statistics.unit);
99 function getUsedMallocHeap() {
100 var stats = window.internals.mallocStatistics();
101 return stats.committedVMBytes - stats.freeListBytes;
104 function getUsedJSHeap() {
105 return console.memory.usedJSHeapSize;
108 PerfTestRunner.gc = function () {
109 if (window.GCController)
110 window.GCController.collect();
115 var temp = {i: "ab" + i + (i / 100000)};
119 for (var i = 0; i < 1000; i++)
124 function logInDocument(text) {
125 if (!document.getElementById("log")) {
126 var pre = document.createElement("pre");
128 document.body.appendChild(pre);
130 document.getElementById("log").innerHTML += text + "\n";
131 window.scrollTo(0, document.body.height);
134 PerfTestRunner.log = function (text) {
141 PerfTestRunner.logFatalError = function (text) {
142 PerfTestRunner.log(text);
146 function start(test, runner) {
148 PerfTestRunner.logFatalError("Got a bad test object.");
152 // FIXME: We should be using multiple instances of test runner on Dromaeo as well but it's too slow now.
153 // FIXME: Don't hard code the number of in-process iterations to use inside a test runner.
154 iterationCount = test.dromaeoIterationCount || (window.testRunner ? 5 : 20);
155 logLines = window.testRunner ? [] : null;
156 PerfTestRunner.log("Running " + iterationCount + " times");
157 if (test.doNotIgnoreInitialRun)
158 completedIterations++;
160 scheduleNextRun(runner);
163 function scheduleNextRun(runner) {
165 window.setTimeout(function () {
167 if (currentTest.setup)
170 var measuredValue = runner();
171 } catch (exception) {
172 PerfTestRunner.logFatalError("Got an exception while running test.run with name=" + exception.name + ", message=" + exception.message);
176 completedIterations++;
179 ignoreWarmUpAndLog(measuredValue);
180 } catch (exception) {
181 PerfTestRunner.logFatalError("Got an exception while logging the result with name=" + exception.name + ", message=" + exception.message);
185 if (completedIterations < iterationCount)
186 scheduleNextRun(runner);
192 function ignoreWarmUpAndLog(measuredValue) {
193 var labeledResult = measuredValue + " " + PerfTestRunner.unit;
194 if (completedIterations <= 0)
195 PerfTestRunner.log("Ignoring warm-up run (" + labeledResult + ")");
197 results.push(measuredValue);
198 if (window.internals && !currentTest.doNotMeasureMemoryUsage) {
199 jsHeapResults.push(getUsedJSHeap());
200 mallocHeapResults.push(getUsedMallocHeap());
202 PerfTestRunner.log(labeledResult);
208 if (currentTest.description)
209 PerfTestRunner.log("Description: " + currentTest.description);
210 PerfTestRunner.logStatistics(results, PerfTestRunner.unit, "Time:");
211 if (jsHeapResults.length) {
212 PerfTestRunner.logStatistics(jsHeapResults, "bytes", "JS Heap:");
213 PerfTestRunner.logStatistics(mallocHeapResults, "bytes", "Malloc:");
216 logLines.forEach(logInDocument);
217 if (currentTest.done)
219 } catch (exception) {
220 logInDocument("Got an exception while finalizing the test with name=" + exception.name + ", message=" + exception.message);
223 if (window.testRunner)
224 testRunner.notifyDone();
227 PerfTestRunner.prepareToMeasureValuesAsync = function (test) {
228 PerfTestRunner.unit = test.unit;
232 PerfTestRunner.measureValueAsync = function (measuredValue) {
233 completedIterations++;
236 ignoreWarmUpAndLog(measuredValue);
237 } catch (exception) {
238 PerfTestRunner.logFatalError("Got an exception while logging the result with name=" + exception.name + ", message=" + exception.message);
242 if (completedIterations >= iterationCount)
246 PerfTestRunner.measureTime = function (test) {
247 PerfTestRunner.unit = "ms";
248 start(test, measureTimeOnce);
251 function measureTimeOnce() {
252 var start = PerfTestRunner.now();
253 var returnValue = currentTest.run();
254 var end = PerfTestRunner.now();
256 if (returnValue - 0 === returnValue) {
258 PerfTestRunner.log("runFunction returned a negative value: " + returnValue);
265 PerfTestRunner.measureRunsPerSecond = function (test) {
266 PerfTestRunner.unit = "runs/s";
267 start(test, measureRunsPerSecondOnce);
270 function measureRunsPerSecondOnce() {
273 var numberOfRuns = 0;
275 while (totalTime < timeToRun) {
276 totalTime += callRunAndMeasureTime(callsPerIteration);
277 numberOfRuns += callsPerIteration;
278 if (completedIterations < 0 && totalTime < 100)
279 callsPerIteration = Math.max(10, 2 * callsPerIteration);
282 return numberOfRuns * 1000 / totalTime;
285 function callRunAndMeasureTime(callsPerIteration) {
286 var startTime = PerfTestRunner.now();
287 for (var i = 0; i < callsPerIteration; i++)
289 return PerfTestRunner.now() - startTime;
293 PerfTestRunner.measurePageLoadTime = function(test) {
294 test.run = function() {
295 var file = PerfTestRunner.loadFile(test.path);
297 this.chunkSize = 50000;
300 // The smaller the chunks the more style resolves we do.
301 // Smaller chunk sizes will show more samples in style resolution.
302 // Larger chunk sizes will show more samples in line layout.
303 // Smaller chunk sizes run slower overall, as the per-chunk overhead is high.
304 var chunkCount = Math.ceil(file.length / this.chunkSize);
305 for (var chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++) {
306 var chunk = file.substr(chunkIndex * this.chunkSize, this.chunkSize);
310 PerfTestRunner.logInfo("Testing " + file.length + " byte document in " + chunkCount + " " + this.chunkSize + " byte chunks.");
312 var iframe = document.createElement("iframe");
313 document.body.appendChild(iframe);
315 iframe.sandbox = ''; // Prevent external loads which could cause write() to return before completing the parse.
316 iframe.style.width = "600px"; // Have a reasonable size so we're not line-breaking on every character.
317 iframe.style.height = "800px";
318 iframe.contentDocument.open();
320 for (var chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
321 iframe.contentDocument.write(chunks[chunkIndex]);
322 // Note that we won't cause a style resolve until we've encountered the <body> element.
323 // Thus the number of chunks counted above is not exactly equal to the number of style resolves.
324 if (iframe.contentDocument.body)
325 iframe.contentDocument.body.clientHeight; // Force a full layout/style-resolve.
326 else if (iframe.documentElement.localName == 'html')
327 iframe.contentDocument.documentElement.offsetWidth; // Force the painting.
330 iframe.contentDocument.close();
331 document.body.removeChild(iframe);
334 PerfTestRunner.measureTime(test);
337 window.PerfTestRunner = PerfTestRunner;