94242e8d8afb355061af859beaf27349c63cb4c7
[WebKit-https.git] / PerformanceTests / resources / runner.js
1 // There are tests for computeStatistics() located in LayoutTests/fast/harness/perftests
2
3 // We need access to console.memory for the memory measurements
4 if (window.internals)
5      internals.settings.setMemoryInfoEnabled(true);
6
7 var PerfTestRunner = {};
8
9 // To make the benchmark results predictable, we replace Math.random with a
10 // 100% deterministic alternative.
11 PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed = 49734321;
12
13 PerfTestRunner.resetRandomSeed = function() {
14     PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed
15 }
16
17 PerfTestRunner.random = Math.random = function() {
18     // Robert Jenkins' 32 bit integer hash function.
19     var randomSeed = PerfTestRunner.randomSeed;
20     randomSeed = ((randomSeed + 0x7ed55d16) + (randomSeed << 12))  & 0xffffffff;
21     randomSeed = ((randomSeed ^ 0xc761c23c) ^ (randomSeed >>> 19)) & 0xffffffff;
22     randomSeed = ((randomSeed + 0x165667b1) + (randomSeed << 5))   & 0xffffffff;
23     randomSeed = ((randomSeed + 0xd3a2646c) ^ (randomSeed << 9))   & 0xffffffff;
24     randomSeed = ((randomSeed + 0xfd7046c5) + (randomSeed << 3))   & 0xffffffff;
25     randomSeed = ((randomSeed ^ 0xb55a4f09) ^ (randomSeed >>> 16)) & 0xffffffff;
26     PerfTestRunner.randomSeed = randomSeed;
27     return (randomSeed & 0xfffffff) / 0x10000000;
28 };
29
30 PerfTestRunner.log = function (text) {
31     if (this._logLines) {
32         this._logLines.push(text);
33         return;
34     }
35     if (!document.getElementById("log")) {
36         var pre = document.createElement('pre');
37         pre.id = 'log';
38         document.body.appendChild(pre);
39     }
40     document.getElementById("log").innerHTML += text + "\n";
41     window.scrollTo(0, document.body.height);
42 }
43
44 PerfTestRunner.info = function (text) {
45     this.log("Info: " + text);
46 }
47
48 PerfTestRunner.logInfo = function (text) {
49     if (!window.testRunner)
50         this.log(text);
51 }
52
53 PerfTestRunner.loadFile = function (path) {
54     var xhr = new XMLHttpRequest();
55     xhr.open("GET", path, false);
56     xhr.send(null);
57     return xhr.responseText;
58 }
59
60 PerfTestRunner.computeStatistics = function (times, unit) {
61     var data = times.slice();
62
63     // Add values from the smallest to the largest to avoid the loss of significance
64     data.sort(function(a,b){return a-b;});
65
66     var middle = Math.floor(data.length / 2);
67     var result = {
68         min: data[0],
69         max: data[data.length - 1],
70         median: data.length % 2 ? data[middle] : (data[middle - 1] + data[middle]) / 2,
71     };
72
73     // Compute the mean and variance using a numerically stable algorithm.
74     var squareSum = 0;
75     result.mean = data[0];
76     result.sum = data[0];
77     for (var i = 1; i < data.length; ++i) {
78         var x = data[i];
79         var delta = x - result.mean;
80         var sweep = i + 1.0;
81         result.mean += delta / sweep;
82         result.sum += x;
83         squareSum += delta * delta * (i / sweep);
84     }
85     result.variance = squareSum / data.length;
86     result.stdev = Math.sqrt(result.variance);
87     result.unit = unit || "ms";
88
89     return result;
90 }
91
92 PerfTestRunner.logStatistics = function (values, unit, title) {
93     var statistics = this.computeStatistics(values, unit);
94     this.printStatistics(statistics, title);
95 }
96
97 PerfTestRunner.printStatistics = function (statistics, title) {
98     this.log("");
99     this.log(title);
100     this.log("avg " + statistics.mean + " " + statistics.unit);
101     this.log("median " + statistics.median + " " + statistics.unit);
102     this.log("stdev " + statistics.stdev + " " + statistics.unit);
103     this.log("min " + statistics.min + " " + statistics.unit);
104     this.log("max " + statistics.max + " " + statistics.unit);
105 }
106
107 PerfTestRunner.gc = function () {
108     if (window.GCController)
109         window.GCController.collect();
110     else {
111         function gcRec(n) {
112             if (n < 1)
113                 return {};
114             var temp = {i: "ab" + i + (i / 100000)};
115             temp += "foo";
116             gcRec(n-1);
117         }
118         for (var i = 0; i < 1000; i++)
119             gcRec(10);
120     }
121 }
122
123 PerfTestRunner._runLoop = function () {
124     if (this._completedRuns < this._runCount) {
125         this.gc();
126         window.setTimeout(function () { PerfTestRunner._runner(); }, 0);
127     } else {
128         if (this._description)
129             this.log("Description: " + this._description);
130         this.logStatistics(this._results, this.unit, "Time:");
131         if (window.internals) {
132             this.logStatistics(this._jsHeapResults, "bytes", "JS Heap:");
133             this.logStatistics(this._fastMallocHeapResults, "bytes", "FastMalloc:");
134         }
135         if (this._logLines) {
136             var logLines = this._logLines;
137             this._logLines = null;
138             var self = this;
139             logLines.forEach(function(text) { self.log(text); });
140         }
141         this._doneFunction();
142         if (window.testRunner)
143             testRunner.notifyDone();
144     }
145 }
146
147 PerfTestRunner._runner = function () {
148     var start = Date.now();
149     var totalTime = 0;
150
151     for (var i = 0; i < this._loopsPerRun; ++i) {
152         var returnValue = this._runFunction.call(window);
153         if (returnValue - 0 === returnValue) {
154             if (returnValue <= 0)
155                 this.log("runFunction returned a non-positive value: " + returnValue);
156             totalTime += returnValue;
157         }
158     }
159
160     // Assume totalTime can never be zero when _runFunction returns a number.
161     var time = totalTime ? totalTime : Date.now() - start;
162
163     this.ignoreWarmUpAndLog(time);
164     this._runLoop();
165 }
166
167 PerfTestRunner.storeHeapResults = function() {
168     if (!window.internals)
169         return;
170     this._jsHeapResults.push(this.getUsedJSHeap());
171     this._fastMallocHeapResults.push(this.getUsedFastMallocHeap());
172 }
173
174 PerfTestRunner.getUsedFastMallocHeap = function() {
175     var stats = window.internals.fastMallocStatistics();
176     return stats.committedVMBytes - stats.freeListBytes;
177 }
178
179 PerfTestRunner.getUsedJSHeap = function() {
180     return console.memory.usedJSHeapSize;
181 }
182
183 PerfTestRunner.getAndPrintMemoryStatistics = function() {
184     if (!window.internals)
185         return;
186     var jsMemoryStats = PerfTestRunner.computeStatistics([PerfTestRunner.getUsedJSHeap()], "bytes");
187     PerfTestRunner.printStatistics(jsMemoryStats, "JS Heap:");
188
189     var fastMallocMemoryStats = PerfTestRunner.computeStatistics([PerfTestRunner.getUsedFastMallocHeap()], "bytes");
190     PerfTestRunner.printStatistics(fastMallocMemoryStats, "FastMalloc:");
191 }
192
193 PerfTestRunner.ignoreWarmUpAndLog = function (result) {
194     this._completedRuns++;
195
196     var labeledResult = result + " " + this.unit;
197     if (this._completedRuns <= 0)
198         this.log("Ignoring warm-up run (" + labeledResult + ")");
199     else {
200         this._results.push(result);
201         this.storeHeapResults();
202         this.log(labeledResult);
203     }
204 }
205
206 PerfTestRunner.initAndStartLoop = function() {
207     this._completedRuns = -1;
208     this.customRunFunction = null;
209     this._results = [];
210     this._jsHeapResults = [];
211     this._fastMallocHeapResults = [];
212     this._logLines = window.testRunner ? [] : null;
213     this.log("Running " + this._runCount + " times");
214     this._runLoop();
215 }
216
217 PerfTestRunner.run = function (runFunction, loopsPerRun, runCount, doneFunction, description) {
218     this._runFunction = runFunction;
219     this._loopsPerRun = loopsPerRun || 10;
220     this._runCount = runCount || 20;
221     this._doneFunction = doneFunction || function () {};
222     this._description = description || "";
223     this.unit = 'ms';
224     this.initAndStartLoop();
225 }
226
227 PerfTestRunner.runPerSecond = function (test) {
228     this._doneFunction = function () { if (test.done) test.done(); };
229     this._description = test.description || "";
230     this._runCount = test.runCount || 20;
231     this._callsPerIteration = 1;
232     this.unit = 'runs/s';
233
234     this._test = test;
235     this._runner = this._perSecondRunner;
236     this.initAndStartLoop();
237 }
238
239 PerfTestRunner._perSecondRunner = function () {
240     var timeToRun = this._test.timeToRun || 750;
241     var totalTime = 0;
242     var i = 0;
243     var callsPerIteration = this._callsPerIteration;
244
245     if (this._test.setup)
246         this._test.setup();
247
248     while (totalTime < timeToRun) {
249         totalTime += this._perSecondRunnerIterator(callsPerIteration);
250         i += callsPerIteration;
251         if (this._completedRuns < 0 && totalTime < 100)
252             callsPerIteration = Math.max(10, 2 * callsPerIteration);
253     }
254     this._callsPerIteration = callsPerIteration;
255
256     this.ignoreWarmUpAndLog(i * 1000 / totalTime);
257     this._runLoop();
258 }
259
260 PerfTestRunner._perSecondRunnerIterator = function (callsPerIteration) {
261     var startTime = Date.now();
262     for (var i = 0; i < callsPerIteration; i++)
263         this._test.run();
264     return Date.now() - startTime;
265 }
266
267 if (window.testRunner) {
268     testRunner.waitUntilDone();
269     testRunner.dumpAsText();
270 }