Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / PerformanceTests / resources / runner.js
1 // There are tests for computeStatistics() located in LayoutTests/fast/harness/perftests
2
3 if (window.testRunner) {
4     testRunner.waitUntilDone();
5     testRunner.dumpAsText();
6 }
7
8 (function () {
9     var logLines = null;
10     var completedIterations = -1;
11     var callsPerIteration = 1;
12     var currentTest = null;
13     var results = [];
14     var jsHeapResults = [];
15     var mallocHeapResults = [];
16     var iterationCount = undefined;
17
18     var PerfTestRunner = {};
19
20     // To make the benchmark results predictable, we replace Math.random with a
21     // 100% deterministic alternative.
22     PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed = 49734321;
23
24     PerfTestRunner.resetRandomSeed = function() {
25         PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed
26     }
27
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;
39     };
40
41     PerfTestRunner.now = window.performance && window.performance.now ? function () { return window.performance.now(); } : Date.now;
42
43     PerfTestRunner.logInfo = function (text) {
44         if (!window.testRunner)
45             this.log(text);
46     }
47
48     PerfTestRunner.loadFile = function (path) {
49         var xhr = new XMLHttpRequest();
50         xhr.open("GET", path, false);
51         xhr.send(null);
52         return xhr.responseText;
53     }
54
55     PerfTestRunner.computeStatistics = function (times, unit) {
56         var data = times.slice();
57
58         // Add values from the smallest to the largest to avoid the loss of significance
59         data.sort(function(a,b){return a-b;});
60
61         var middle = Math.floor(data.length / 2);
62         var result = {
63             min: data[0],
64             max: data[data.length - 1],
65             median: data.length % 2 ? data[middle] : (data[middle - 1] + data[middle]) / 2,
66         };
67
68         // Compute the mean and variance using Knuth's online algorithm (has good numerical stability).
69         var squareSum = 0;
70         result.values = times;
71         result.mean = 0;
72         for (var i = 0; i < data.length; ++i) {
73             var x = data[i];
74             var delta = x - result.mean;
75             var sweep = i + 1.0;
76             result.mean += delta / sweep;
77             squareSum += delta * (x - result.mean);
78         }
79         result.variance = data.length <= 1 ? 0 : squareSum / (data.length - 1);
80         result.stdev = Math.sqrt(result.variance);
81         result.unit = unit || "ms";
82
83         return result;
84     }
85
86     PerfTestRunner.logStatistics = function (values, unit, title) {
87         var statistics = this.computeStatistics(values, unit);
88         this.log("");
89         this.log(title);
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);
97     }
98
99     function getUsedMallocHeap() {
100         var stats = window.internals.mallocStatistics();
101         return stats.committedVMBytes - stats.freeListBytes;
102     }
103
104     function getUsedJSHeap() {
105         return console.memory.usedJSHeapSize;
106     }
107
108     PerfTestRunner.gc = function () {
109         if (window.GCController)
110             window.GCController.collect();
111         else {
112             function gcRec(n) {
113                 if (n < 1)
114                     return {};
115                 var temp = {i: "ab" + i + (i / 100000)};
116                 temp += "foo";
117                 gcRec(n-1);
118             }
119             for (var i = 0; i < 1000; i++)
120                 gcRec(10);
121         }
122     };
123
124     function logInDocument(text) {
125         if (!document.getElementById("log")) {
126             var pre = document.createElement("pre");
127             pre.id = "log";
128             document.body.appendChild(pre);
129         }
130         document.getElementById("log").innerHTML += text + "\n";
131         window.scrollTo(0, document.body.height);
132     }
133
134     PerfTestRunner.log = function (text) {
135         if (logLines)
136             logLines.push(text);
137         else
138             logInDocument(text);
139     }
140
141     PerfTestRunner.logFatalError = function (text) {
142         PerfTestRunner.log(text);
143         finish();
144     }
145
146     function start(test, runner) {
147         if (!test) {
148             PerfTestRunner.logFatalError("Got a bad test object.");
149             return;
150         }
151         currentTest = test;
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++;
159         if (runner)
160             scheduleNextRun(runner);
161     }
162
163     function scheduleNextRun(runner) {
164         PerfTestRunner.gc();
165         window.setTimeout(function () {
166             try {
167                 if (currentTest.setup)
168                     currentTest.setup();
169
170                 var measuredValue = runner();
171             } catch (exception) {
172                 PerfTestRunner.logFatalError("Got an exception while running test.run with name=" + exception.name + ", message=" + exception.message);
173                 return;
174             }
175
176             completedIterations++;
177
178             try {
179                 ignoreWarmUpAndLog(measuredValue);
180             } catch (exception) {
181                 PerfTestRunner.logFatalError("Got an exception while logging the result with name=" + exception.name + ", message=" + exception.message);
182                 return;
183             }
184
185             if (completedIterations < iterationCount)
186                 scheduleNextRun(runner);
187             else
188                 finish();
189         }, 0);
190     }
191
192     function ignoreWarmUpAndLog(measuredValue) {
193         var labeledResult = measuredValue + " " + PerfTestRunner.unit;
194         if (completedIterations <= 0)
195             PerfTestRunner.log("Ignoring warm-up run (" + labeledResult + ")");
196         else {
197             results.push(measuredValue);
198             if (window.internals && !currentTest.doNotMeasureMemoryUsage) {
199                 jsHeapResults.push(getUsedJSHeap());
200                 mallocHeapResults.push(getUsedMallocHeap());
201             }
202             PerfTestRunner.log(labeledResult);
203         }
204     }
205
206     function finish() {
207         try {
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:");
214             }
215             if (logLines)
216                 logLines.forEach(logInDocument);
217             if (currentTest.done)
218                 currentTest.done();
219         } catch (exception) {
220             logInDocument("Got an exception while finalizing the test with name=" + exception.name + ", message=" + exception.message);
221         }
222
223         if (window.testRunner)
224             testRunner.notifyDone();
225     }
226
227     PerfTestRunner.prepareToMeasureValuesAsync = function (test) {
228         PerfTestRunner.unit = test.unit;
229         start(test);
230     }
231
232     PerfTestRunner.measureValueAsync = function (measuredValue) {
233         completedIterations++;
234
235         try {
236             ignoreWarmUpAndLog(measuredValue);
237         } catch (exception) {
238             PerfTestRunner.logFatalError("Got an exception while logging the result with name=" + exception.name + ", message=" + exception.message);
239             return;
240         }
241
242         if (completedIterations >= iterationCount)
243             finish();
244     }
245
246     PerfTestRunner.measureTime = function (test) {
247         PerfTestRunner.unit = "ms";
248         start(test, measureTimeOnce);
249     }
250
251     function measureTimeOnce() {
252         var start = PerfTestRunner.now();
253         var returnValue = currentTest.run();
254         var end = PerfTestRunner.now();
255
256         if (returnValue - 0 === returnValue) {
257             if (returnValue < 0)
258                 PerfTestRunner.log("runFunction returned a negative value: " + returnValue);
259             return returnValue;
260         }
261
262         return end - start;
263     }
264
265     PerfTestRunner.measureRunsPerSecond = function (test) {
266         PerfTestRunner.unit = "runs/s";
267         start(test, measureRunsPerSecondOnce);
268     }
269
270     function measureRunsPerSecondOnce() {
271         var timeToRun = 750;
272         var totalTime = 0;
273         var numberOfRuns = 0;
274
275         while (totalTime < timeToRun) {
276             totalTime += callRunAndMeasureTime(callsPerIteration);
277             numberOfRuns += callsPerIteration;
278             if (completedIterations < 0 && totalTime < 100)
279                 callsPerIteration = Math.max(10, 2 * callsPerIteration);
280         }
281
282         return numberOfRuns * 1000 / totalTime;
283     }
284
285     function callRunAndMeasureTime(callsPerIteration) {
286         var startTime = PerfTestRunner.now();
287         for (var i = 0; i < callsPerIteration; i++)
288             currentTest.run();
289         return PerfTestRunner.now() - startTime;
290     }
291
292
293     PerfTestRunner.measurePageLoadTime = function(test) {
294         test.run = function() {
295             var file = PerfTestRunner.loadFile(test.path);
296             if (!test.chunkSize)
297                 this.chunkSize = 50000;
298
299             var chunks = [];
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);
307                 chunks.push(chunk);
308             }
309
310             PerfTestRunner.logInfo("Testing " + file.length + " byte document in " + chunkCount + " " + this.chunkSize + " byte chunks.");
311
312             var iframe = document.createElement("iframe");
313             document.body.appendChild(iframe);
314
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();
319
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.
328             }
329
330             iframe.contentDocument.close();
331             document.body.removeChild(iframe);
332         };
333
334         PerfTestRunner.measureTime(test);
335     }
336
337     window.PerfTestRunner = PerfTestRunner;
338 })();