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