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