[PerformanceTests] run-perf-tests should output correct units
[WebKit-https.git] / PerformanceTests / resources / runner.js
1
2 var PerfTestRunner = {};
3
4 // To make the benchmark results predictable, we replace Math.random with a
5 // 100% deterministic alternative.
6 PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed = 49734321;
7
8 PerfTestRunner.resetRandomSeed = function() {
9     PerfTestRunner.randomSeed = PerfTestRunner.initialRandomSeed
10 }
11
12 PerfTestRunner.random = Math.random = function() {
13     // Robert Jenkins' 32 bit integer hash function.
14     var randomSeed = PerfTestRunner.randomSeed;
15     randomSeed = ((randomSeed + 0x7ed55d16) + (randomSeed << 12))  & 0xffffffff;
16     randomSeed = ((randomSeed ^ 0xc761c23c) ^ (randomSeed >>> 19)) & 0xffffffff;
17     randomSeed = ((randomSeed + 0x165667b1) + (randomSeed << 5))   & 0xffffffff;
18     randomSeed = ((randomSeed + 0xd3a2646c) ^ (randomSeed << 9))   & 0xffffffff;
19     randomSeed = ((randomSeed + 0xfd7046c5) + (randomSeed << 3))   & 0xffffffff;
20     randomSeed = ((randomSeed ^ 0xb55a4f09) ^ (randomSeed >>> 16)) & 0xffffffff;
21     PerfTestRunner.randomSeed = randomSeed;
22     return (randomSeed & 0xfffffff) / 0x10000000;
23 };
24
25 PerfTestRunner.log = function (text) {
26     if (!document.getElementById("log")) {
27         var pre = document.createElement('pre');
28         pre.id = 'log';
29         document.body.appendChild(pre);
30     }
31     document.getElementById("log").innerHTML += text + "\n";
32     window.scrollTo(0, document.body.height);
33 }
34
35 PerfTestRunner.info = function (text) {
36     this.log("Info: " + text);
37 }
38
39 PerfTestRunner.logInfo = function (text) {
40     if (!window.layoutTestController)
41         this.log(text);
42 }
43
44 PerfTestRunner.loadFile = function (path) {
45     var xhr = new XMLHttpRequest();
46     xhr.open("GET", path, false);
47     xhr.send(null);
48     return xhr.responseText;
49 }
50
51 PerfTestRunner.computeStatistics = function (times) {
52     var data = times.slice();
53
54     // Add values from the smallest to the largest to avoid the loss of significance
55     data.sort();
56
57     var middle = Math.floor(data.length / 2);
58     var result = {
59         min: data[0],
60         max: data[data.length - 1],
61         median: data.length % 2 ? data[middle] : (data[middle - 1] + data[middle]) / 2,
62     };
63
64     // Compute the mean and variance using a numerically stable algorithm.
65     var squareSum = 0;
66     result.mean = data[0];
67     result.sum = data[0];
68     for (var i = 1; i < data.length; ++i) {
69         var x = data[i];
70         var delta = x - result.mean;
71         var sweep = i + 1.0;
72         result.mean += delta / sweep;
73         result.sum += x;
74         squareSum += delta * delta * (i / sweep);
75     }
76     result.variance = squareSum / data.length;
77     result.stdev = Math.sqrt(result.variance);
78     result.unit = "ms";
79
80     return result;
81 }
82
83 PerfTestRunner.logStatistics = function (times) {
84     this.log("");
85     var statistics = this.computeStatistics(times);
86     this.printStatistics(statistics);
87 }
88
89 PerfTestRunner.printStatistics = function (statistics) {
90     this.log("");
91     this.log("avg " + statistics.mean + " " + statistics.unit);
92     this.log("median " + statistics.median + " " + statistics.unit);
93     this.log("stdev " + statistics.stdev + " " + statistics.unit);
94     this.log("min " + statistics.min + " " + statistics.unit);
95     this.log("max " + statistics.max + " " + statistics.unit);
96 }
97
98 PerfTestRunner.gc = function () {
99     if (window.GCController)
100         window.GCController.collect();
101     else {
102         function gcRec(n) {
103             if (n < 1)
104                 return {};
105             var temp = {i: "ab" + i + (i / 100000)};
106             temp += "foo";
107             gcRec(n-1);
108         }
109         for (var i = 0; i < 1000; i++)
110             gcRec(10);
111     }
112 }
113
114 PerfTestRunner._runLoop = function () {
115     if (this._completedRuns < this._runCount) {
116         this.gc();
117         window.setTimeout(function () { PerfTestRunner._runner(); }, 0);
118     } else {
119         this.logStatistics(this._times);
120         this._doneFunction();
121         if (window.layoutTestController)
122             layoutTestController.notifyDone();
123     }
124 }
125
126 PerfTestRunner._runner = function () {
127     var start = Date.now();
128     var totalTime = 0;
129
130     for (var i = 0; i < this._loopsPerRun; ++i) {
131         var returnValue = this._runFunction.call(window);
132         if (returnValue - 0 === returnValue) {
133             if (returnValue <= 0)
134                 this.log("runFunction returned a non-positive value: " + returnValue);
135             totalTime += returnValue;
136         }
137     }
138
139     // Assume totalTime can never be zero when _runFunction returns a number.
140     var time = totalTime ? totalTime : Date.now() - start;
141
142     this._completedRuns++;
143     if (this._completedRuns <= 0)
144         this.log("Ignoring warm-up run (" + time + ")");
145     else {
146         this._times.push(time);
147         this.log(time);
148     }
149     this._runLoop();
150 }
151
152 PerfTestRunner.run = function (runFunction, loopsPerRun, runCount, doneFunction) {
153     this._runFunction = runFunction;
154     this._loopsPerRun = loopsPerRun || 10;
155     this._runCount = runCount || 20;
156     this._doneFunction = doneFunction || function () {};
157     this._completedRuns = -1;
158     this.customRunFunction = null;
159     this._times = [];
160
161     this.log("Running " + this._runCount + " times");
162     this._runLoop();
163 }
164
165 if (window.layoutTestController) {
166     layoutTestController.waitUntilDone();
167     layoutTestController.dumpAsText();
168 }