ES6SampleBench should have a harness
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 29 Jun 2016 05:55:37 +0000 (05:55 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 29 Jun 2016 05:55:37 +0000 (05:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=159246

Reviewed by Saam Barati.

This adds a simple web harness for ES6SampleBench. It runs Air and Basic 10 times for 200
iterations each and reports three metrics:

First iteration: the time it takes for the first iteration to run. This is the first
iteration after the benchmark is loaded into the iframe, so it's representative of what
would happen if one of these workloads only ran for a short time.

Worst 2%: of the last 199 iterations, the average of the worst 2% iterations. If code like
any of these workloads was used in an important event handler, you'd want that code to run
well in the worst case in addition to having great throughput.

Steady state: the total of the last 199 iterations. This is representative of what would
happen if you ran code like this for a long time.

The total score is the geomean of the firstIteration/worstCase/steadyState numbers of the
two benchmarks.

The harness does statistics using Student's T-distribution confidence intervals.

* ES6SampleBench/Basic/benchmark.js:
(Benchmark):
* ES6SampleBench/air_benchmark.js: Added.
* ES6SampleBench/basic_benchmark.js: Added.
* ES6SampleBench/driver.js: Added.
(Driver):
(Driver.prototype.addBenchmark):
(Driver.prototype.start):
(Driver.prototype.reportResult):
(Driver.prototype.reportError):
(Driver.prototype._recomputeSummary.Geomean):
(Driver.prototype._recomputeSummary.Geomean.prototype.add):
(Driver.prototype._recomputeSummary.Geomean.prototype.get result):
(Driver.prototype._recomputeSummary):
(Driver.prototype._iterate):
(Driver.prototype._updateIterations):
* ES6SampleBench/glue.js: Added.
* ES6SampleBench/index.html: Added.
* ES6SampleBench/results.js: Added.
(Results):
(Results.prototype.get benchmark):
(Results.prototype.reset):
(Results.prototype.reportRunning):
(Results.prototype.reportDone):
(Results.prototype.reportResult.averageAbovePercentile):
(Results.prototype.reportResult):
(Results.prototype.reportError):
* ES6SampleBench/stats.js: Added.
(Stats):
(Stats.prototype.reset):
(Stats.prototype.add):
(Stats.prototype.get numIterations):
(Stats.prototype.valueForIteration):
(Stats.get result.tDist):
(Stats.prototype.get result):
(Stats.prototype.toString):
(Stats.prototype._update):
* ES6SampleBench/style.css: Added.
(body):
(body, th, tr):
(h1, h2, h3, h4):
(h1):
(h2):
(h3):
(hr):
(address):
(img):
(.underline):
(ol.loweralpha):
(ol.upperalpha):
(ol.lowerroman):
(ol.upperroman):
(ol.arabic):
(.banner-link:link, .banner-link:visited):
(:link, :visited):
(h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited):
(.anchor:link, .anchor:visited):
(* > .anchor:link, * > .anchor:visited):
(span:hover .anchor):
(a.forbidden, span.forbidden):
(a.missing:hover):
(a.closed:link, a.closed:visited, span.closed):
(pre):
(div.code):
(div.code pre):
(dt):
(dd):
(dd:last-child):
(.site-logo):
(.site-logo .tagline):
(table):
(#contents):
(p):
(p:last-child):
(th):
(td):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@202613 268f45cc-cd09-0410-ab3c-d52691b4dbfc

PerformanceTests/ChangeLog
PerformanceTests/ES6SampleBench/Basic/benchmark.js
PerformanceTests/ES6SampleBench/air_benchmark.js [new file with mode: 0644]
PerformanceTests/ES6SampleBench/basic_benchmark.js [new file with mode: 0644]
PerformanceTests/ES6SampleBench/driver.js [new file with mode: 0644]
PerformanceTests/ES6SampleBench/glue.js [new file with mode: 0644]
PerformanceTests/ES6SampleBench/index.html [new file with mode: 0644]
PerformanceTests/ES6SampleBench/results.js [new file with mode: 0644]
PerformanceTests/ES6SampleBench/stats.js [new file with mode: 0644]
PerformanceTests/ES6SampleBench/style.css [new file with mode: 0644]

index 8c66c87..be2df6e 100644 (file)
@@ -1,3 +1,106 @@
+2016-06-28  Filip Pizlo  <fpizlo@apple.com>
+
+        ES6SampleBench should have a harness
+        https://bugs.webkit.org/show_bug.cgi?id=159246
+
+        Reviewed by Saam Barati.
+        
+        This adds a simple web harness for ES6SampleBench. It runs Air and Basic 10 times for 200
+        iterations each and reports three metrics:
+        
+        First iteration: the time it takes for the first iteration to run. This is the first
+        iteration after the benchmark is loaded into the iframe, so it's representative of what
+        would happen if one of these workloads only ran for a short time.
+        
+        Worst 2%: of the last 199 iterations, the average of the worst 2% iterations. If code like
+        any of these workloads was used in an important event handler, you'd want that code to run
+        well in the worst case in addition to having great throughput.
+        
+        Steady state: the total of the last 199 iterations. This is representative of what would
+        happen if you ran code like this for a long time.
+        
+        The total score is the geomean of the firstIteration/worstCase/steadyState numbers of the
+        two benchmarks.
+        
+        The harness does statistics using Student's T-distribution confidence intervals.
+
+        * ES6SampleBench/Basic/benchmark.js:
+        (Benchmark):
+        * ES6SampleBench/air_benchmark.js: Added.
+        * ES6SampleBench/basic_benchmark.js: Added.
+        * ES6SampleBench/driver.js: Added.
+        (Driver):
+        (Driver.prototype.addBenchmark):
+        (Driver.prototype.start):
+        (Driver.prototype.reportResult):
+        (Driver.prototype.reportError):
+        (Driver.prototype._recomputeSummary.Geomean):
+        (Driver.prototype._recomputeSummary.Geomean.prototype.add):
+        (Driver.prototype._recomputeSummary.Geomean.prototype.get result):
+        (Driver.prototype._recomputeSummary):
+        (Driver.prototype._iterate):
+        (Driver.prototype._updateIterations):
+        * ES6SampleBench/glue.js: Added.
+        * ES6SampleBench/index.html: Added.
+        * ES6SampleBench/results.js: Added.
+        (Results):
+        (Results.prototype.get benchmark):
+        (Results.prototype.reset):
+        (Results.prototype.reportRunning):
+        (Results.prototype.reportDone):
+        (Results.prototype.reportResult.averageAbovePercentile):
+        (Results.prototype.reportResult):
+        (Results.prototype.reportError):
+        * ES6SampleBench/stats.js: Added.
+        (Stats):
+        (Stats.prototype.reset):
+        (Stats.prototype.add):
+        (Stats.prototype.get numIterations):
+        (Stats.prototype.valueForIteration):
+        (Stats.get result.tDist):
+        (Stats.prototype.get result):
+        (Stats.prototype.toString):
+        (Stats.prototype._update):
+        * ES6SampleBench/style.css: Added.
+        (body):
+        (body, th, tr):
+        (h1, h2, h3, h4):
+        (h1):
+        (h2):
+        (h3):
+        (hr):
+        (address):
+        (img):
+        (.underline):
+        (ol.loweralpha):
+        (ol.upperalpha):
+        (ol.lowerroman):
+        (ol.upperroman):
+        (ol.arabic):
+        (.banner-link:link, .banner-link:visited):
+        (:link, :visited):
+        (h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited):
+        (.anchor:link, .anchor:visited):
+        (* > .anchor:link, * > .anchor:visited):
+        (span:hover .anchor):
+        (a.forbidden, span.forbidden):
+        (a.missing:hover):
+        (a.closed:link, a.closed:visited, span.closed):
+        (pre):
+        (div.code):
+        (div.code pre):
+        (dt):
+        (dd):
+        (dd:last-child):
+        (.site-logo):
+        (.site-logo .tagline):
+        (table):
+        (#contents):
+        (p):
+        (p:last-child):
+        (th):
+        (td):
+
 2016-06-28  Jon Lee  <jonlee@apple.com>
 
         Update focus test
index 902242d..9ec071c 100644 (file)
@@ -25,7 +25,7 @@
 "use strict";
 
 class Benchmark {
-    constructor(verbose)
+    constructor(verbose = 0)
     {
         this._verbose = verbose;
     }
diff --git a/PerformanceTests/ES6SampleBench/air_benchmark.js b/PerformanceTests/ES6SampleBench/air_benchmark.js
new file mode 100644 (file)
index 0000000..33cac4e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+const AirBenchmarkCode = String.raw`
+<script src="Air/symbols.js"></script>
+<script src="Air/tmp_base.js"></script>
+<script src="Air/arg.js"></script>
+<script src="Air/basic_block.js"></script>
+<script src="Air/code.js"></script>
+<script src="Air/frequented_block.js"></script>
+<script src="Air/inst.js"></script>
+<script src="Air/opcode.js"></script>
+<script src="Air/reg.js"></script>
+<script src="Air/stack_slot.js"></script>
+<script src="Air/tmp.js"></script>
+<script src="Air/util.js"></script>
+<script src="Air/custom.js"></script>
+<script src="Air/liveness.js"></script>
+<script src="Air/insertion_set.js"></script>
+<script src="Air/allocate_stack.js"></script>
+<script src="Air/payload-gbemu-executeIteration.js"></script>
+<script src="Air/payload-imaging-gaussian-blur-gaussianBlur.js"></script>
+<script src="Air/payload-airjs-ACLj8C.js"></script>
+<script src="Air/payload-typescript-scanIdentifier.js"></script>
+<script src="Air/benchmark.js"></script>
+<script>
+var results = [];
+var benchmark = new Benchmark();
+var numIterations = 200;
+for (var i = 0; i < numIterations; ++i) {
+    var before = currentTime();
+    benchmark.runIteration();
+    var after = currentTime();
+    results.push(after - before);
+}
+reportResult(results);
+</script>`;
+
+const AirBenchmark = {
+    code: AirBenchmarkCode,
+    cells: {
+        firstIteration: document.getElementById("AirFirstIteration"),
+        averageWorstCase: document.getElementById("AirAverageWorstCase"),
+        steadyState: document.getElementById("AirSteadyState"),
+        message: document.getElementById("AirMessage")
+    }
+};
+
diff --git a/PerformanceTests/ES6SampleBench/basic_benchmark.js b/PerformanceTests/ES6SampleBench/basic_benchmark.js
new file mode 100644 (file)
index 0000000..4ed5ab5
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+const BasicBenchmarkCode = String.raw`
+<script src="Basic/ast.js"></script>
+<script src="Basic/basic.js"></script>
+<script src="Basic/caseless_map.js"></script>
+<script src="Basic/lexer.js"></script>
+<script src="Basic/number.js"></script>
+<script src="Basic/parser.js"></script>
+<script src="Basic/random.js"></script>
+<script src="Basic/state.js"></script>
+<script src="Basic/util.js"></script>
+<script src="Basic/benchmark.js"></script>
+<script>
+var results = [];
+var benchmark = new Benchmark();
+var numIterations = 200;
+for (var i = 0; i < numIterations; ++i) {
+    var before = currentTime();
+    benchmark.runIteration();
+    var after = currentTime();
+    results.push(after - before);
+}
+reportResult(results);
+</script>`;
+
+const BasicBenchmark = {
+    code: BasicBenchmarkCode,
+    cells: {
+        firstIteration: document.getElementById("BasicFirstIteration"),
+        averageWorstCase: document.getElementById("BasicAverageWorstCase"),
+        steadyState: document.getElementById("BasicSteadyState"),
+        message: document.getElementById("BasicMessage")
+    }
+};
diff --git a/PerformanceTests/ES6SampleBench/driver.js b/PerformanceTests/ES6SampleBench/driver.js
new file mode 100644 (file)
index 0000000..4edcd2f
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class Driver {
+    constructor(triggerCell, magicCell, summaryCell, key)
+    {
+        if (!magicCell)
+            throw new Error("Need magic cell");
+        if (!summaryCell)
+            throw new Error("Need summary cell");
+        
+        this._benchmarks = new Map();
+        this._triggerCell = triggerCell;
+        this._magicCell = magicCell;
+        this._summary = new Stats(summaryCell);
+        this._key = key;
+        window[key] = this;
+    }
+    
+    addBenchmark(benchmark)
+    {
+        this._benchmarks.set(benchmark, new Results(benchmark));
+    }
+    
+    start(numIterations)
+    {
+        this._triggerCellSaved = this._triggerCell.innerHTML;
+        this._updateIterations();
+        
+        this._summary.reset();
+        for (let [benchmark, results] of this._benchmarks)
+            results.reset();
+        this._isRunning = true;
+        this._numIterations = numIterations;
+        this._iterator = null;
+        this._iterate();
+    }
+    
+    reportResult(...args)
+    {
+        this._benchmarks.get(this._benchmark).reportResult(...args);
+        this._recomputeSummary();
+        this._iterate();
+    }
+    
+    reportError(...args)
+    {
+        this._benchmarks.get(this._benchmark).reportError(...args);
+        this._recomputeSummary();
+        this._iterate();
+    }
+    
+    _recomputeSummary()
+    {
+        class Geomean {
+            constructor()
+            {
+                this._count = 0;
+                this._sum = 0;
+            }
+            
+            add(value)
+            {
+                this._count++;
+                this._sum += Math.log(value);
+            }
+            
+            get result()
+            {
+                return Math.exp(this._sum * (1 / this._count));
+            }
+        }
+        
+        let statses = [];
+        for (let results of this._benchmarks.values()) {
+            for (let subResult of Results.subResults)
+                statses.push(results[subResult]);
+        }
+        
+        let numIterations = Math.min(...statses.map(stats => stats.numIterations));
+        let data = new Array(numIterations);
+        for (let i = 0; i < data.length; ++i)
+            data[i] = new Geomean();
+        
+        for (let stats of statses) {
+            for (let i = 0; i < data.length; ++i)
+                data[i].add(stats.valueForIteration(i));
+        }
+        
+        let geomeans = data.map(geomean => geomean.result);
+        
+        this._summary.reset(...geomeans);
+    }
+    
+    _iterate()
+    {
+        this._benchmark = this._iterator ? this._iterator.next().value : null;
+        if (!this._benchmark) {
+            if (!this._numIterations) {
+                this._triggerCell.innerHTML = this._triggerCellSaved;
+                return;
+            }
+            this._numIterations--;
+            this._updateIterations();
+            this._iterator = this._benchmarks.keys();
+            this._benchmark = this._iterator.next().value;
+        }
+        
+        this._benchmarks.get(this._benchmark).reportRunning();
+        
+        window.setTimeout(() => {
+            if (!this._isRunning)
+                return;
+            
+            this._magicCell.contentDocument.body.textContent = "";
+            this._magicCell.contentDocument.body.innerHTML = "<iframe id=\"magicFrame\" frameborder=\"0\">";
+            
+            let magicFrame = this._magicCell.contentDocument.getElementById("magicFrame");
+            magicFrame.contentDocument.open();
+            magicFrame.contentDocument.write(
+                `<!DOCTYPE html><head><title>benchmark payload</title></head><body><script>` +
+                `window.onerror = top.${this._key}.reportError;\n` +
+                `function reportResult()\n` +
+                `{\n` +
+                `    var driver = top.${this._key};\n` +
+                `    driver.reportResult.apply(driver, arguments);\n` +
+                `}\n` +
+                `</script>\n` +
+                `${this._benchmark.code}</body></html>`);
+        }, 100);
+    }
+    
+    _updateIterations()
+    {
+        this._triggerCell.innerHTML = "Running... (" + (this._numIterations + 1) + " to go)";
+    }
+}
+
diff --git a/PerformanceTests/ES6SampleBench/glue.js b/PerformanceTests/ES6SampleBench/glue.js
new file mode 100644 (file)
index 0000000..aa2264d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+const driver = new Driver(
+    document.getElementById("trigger"),
+    document.getElementById("magic"),
+    document.getElementById("Geomean"),
+    "sampleBench");
+
+driver.addBenchmark(AirBenchmark);
+driver.addBenchmark(BasicBenchmark);
diff --git a/PerformanceTests/ES6SampleBench/index.html b/PerformanceTests/ES6SampleBench/index.html
new file mode 100644 (file)
index 0000000..fe515ac
--- /dev/null
@@ -0,0 +1,44 @@
+<html>
+<head>
+<title>ES6 Sample Bench</title>
+<link rel="stylesheet" type="text/css" href="style.css">
+</head>
+<script src="driver.js"></script>
+<script src="results.js"></script>
+<script src="stats.js"></script>
+<body>
+<div id="contents">
+<h1>ES6 Sample Bench</h1>
+<p>This is a benchmark suite that consists of ES6 sample code written by the
+<a href="http://www.webkit.org/">WebKit</a> team.  All results are in milliseconds with 95%
+confidence intervals.  Lower is better.</p>
+<p id="trigger"><a href="javascript:driver.start(10)">Start Benchmark</a></p>
+<p><b>Geometric Mean Result: <span id="Geomean">...</span></b></p>
+<table cellspacing=0 cellpadding=0 border=0>
+  <tr>
+    <th width=180>Benchmark</th>
+    <th width=180>First Iteration</th>
+    <th width=180>Worst 2%</th>
+    <th width=180>Steady State</th>
+  <tr>
+    <th>Air</th>
+    <td id="AirFirstIteration">...</td>
+    <td id="AirAverageWorstCase">...</td>
+    <td id="AirSteadyState">...</td>
+    <td id="AirMessage"></td>
+  </tr>
+  <tr>
+    <th>Basic</th>
+    <td id="BasicFirstIteration">...</td>
+    <td id="BasicAverageWorstCase">...</td>
+    <td id="BasicSteadyState">...</td>
+    <td id="BasicMessage"></td>
+  </tr>
+</table>
+</div>
+<iframe id="magic" frameBorder=0></iframe>
+<script src="air_benchmark.js"></script>
+<script src="basic_benchmark.js"></script>
+<script src="glue.js"></script>
+</body>
+</html>
diff --git a/PerformanceTests/ES6SampleBench/results.js b/PerformanceTests/ES6SampleBench/results.js
new file mode 100644 (file)
index 0000000..b4ed0c4
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class Results {
+    constructor(benchmark)
+    {
+        this._benchmark = benchmark;
+        for (let subResult of Results.subResults)
+            this[subResult] = new Stats(benchmark.cells[subResult]);
+    }
+    
+    get benchmark() { return this._benchmark; }
+    
+    reset()
+    {
+        for (let subResult of Results.subResults)
+            this[subResult].reset();
+    }
+    
+    reportRunning()
+    {
+        this._benchmark.cells.message.innerHTML = "Running...";
+    }
+    
+    reportDone()
+    {
+        this._benchmark.cells.message.innerHTML = "";
+    }
+    
+    reportResult(times)
+    {
+        function averageAbovePercentile(numbers, percentile) {
+            // Don't change the original array.
+            numbers = numbers.slice();
+            
+            // Sort in ascending order.
+            numbers.sort(function(a, b) { return a - b; });
+            
+            // Now the elements we want are at the end. Keep removing them until the array size shrinks too much.
+            // Examples assuming percentile = 99:
+            //
+            // - numbers.length starts at 100: we will remove just the worst entry and then not remove anymore,
+            //   since then numbers.length / originalLength = 0.99.
+            //
+            // - numbers.length starts at 1000: we will remove the ten worst.
+            //
+            // - numbers.length starts at 10: we will remove just the worst.
+            var numbersWeWant = [];
+            var originalLength = numbers.length;
+            while (numbers.length / originalLength > percentile / 100)
+                numbersWeWant.push(numbers.pop());
+            
+            var sum = 0;
+            for (var i = 0; i < numbersWeWant.length; ++i)
+                sum += numbersWeWant[i];
+            
+            var result = sum / numbersWeWant.length;
+            
+            // Do a sanity check.
+            if (numbers.length && result < numbers[numbers.length - 1]) {
+                throw "Sanity check fail: the worst case result is " + result +
+                    " but we didn't take into account " + numbers;
+            }
+            
+            return result;
+        }
+
+        this.firstIteration.add(times[0]);
+        let steadyTimes = times.slice(1);
+        this.averageWorstCase.add(averageAbovePercentile(steadyTimes, 98));
+        this.steadyState.add(steadyTimes.reduce((previous, current) => previous + current));
+        this.reportDone();
+    }
+    
+    reportError(message, url, lineNumber)
+    {
+        for (let subResult of Results.subResults)
+            this[subResult].reportResult(Stats.error);
+        this._benchmark.cells.message.innerHTML = url + ":" + lineNumber + ": " + message;
+    }
+}
+
+Results.subResults = ["firstIteration", "averageWorstCase", "steadyState"];
diff --git a/PerformanceTests/ES6SampleBench/stats.js b/PerformanceTests/ES6SampleBench/stats.js
new file mode 100644 (file)
index 0000000..31ec316
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class Stats {
+    constructor(cell)
+    {
+        this._cell = cell;
+        this._data = [];
+    }
+    
+    reset(...data)
+    {
+        this._data = data;
+        this._update();
+    }
+    
+    add(...data)
+    {
+        this._data.push(...data);
+        this._update();
+    }
+    
+    get numIterations() { return this._data.length; }
+    valueForIteration(index) { return this._data[index]; }
+    
+    get result()
+    {
+        var tDistribution = [NaN, NaN, 12.71, 4.30, 3.18, 2.78, 2.57, 2.45, 2.36, 2.31, 2.26, 2.23, 2.20, 2.18, 2.16, 2.14, 2.13, 2.12, 2.11, 2.10, 2.09, 2.09, 2.08, 2.07, 2.07, 2.06, 2.06, 2.06, 2.05, 2.05, 2.05, 2.04, 2.04, 2.04, 2.03, 2.03, 2.03, 2.03, 2.03, 2.02, 2.02, 2.02, 2.02, 2.02, 2.02, 2.02, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.96];
+        var tMax = tDistribution.length;
+        var tLimit = 1.96;
+
+        function tDist(n)
+        {
+            if (n > tMax)
+                return tLimit;
+            return tDistribution[n];
+        }
+        
+        let sum = 0;
+        let n = 0;
+        for (let datum of this._data) {
+            sum += datum;
+            n++;
+        }
+        
+        let mean = sum / n;
+        
+        if (n <= 2)
+            return {n, mean};
+        
+        let sumForStdDev = 0;
+        for (let datum of this._data)
+            sumForStdDev += Math.pow(datum - mean, 2);
+        let standardDeviation = Math.sqrt(sumForStdDev / (n - 1));
+        let standardError = standardDeviation / Math.sqrt(n);
+        let interval = tDist(n) * standardError;
+        return {n, mean, interval};
+    }
+    
+    toString()
+    {
+        let result = this.result;
+        
+        if (!result.n)
+            return "...";
+        
+        if (result.mean != result.mean)
+            return "ERROR";
+        
+        if ("interval" in result)
+            return `${result.mean.toFixed(2)} ms &plusmn; ${result.interval.toFixed(2)} ms`;
+
+        return `${result.mean.toFixed(2)} ms`;
+    }
+    
+    _update()
+    {
+        if (this._cell)
+            this._cell.innerHTML = this.toString();
+    }
+}
+
diff --git a/PerformanceTests/ES6SampleBench/style.css b/PerformanceTests/ES6SampleBench/style.css
new file mode 100644 (file)
index 0000000..3ca3059
--- /dev/null
@@ -0,0 +1,142 @@
+body {
+    background: #fff;
+    color: #000;
+    margin: 10px;
+    padding: 0;
+}
+body, th, tr {
+    font: normal 13px Verdana,Arial,'Bitstream Vera Sans',Helvetica,sans-serif;
+}
+h1, h2, h3, h4 {
+    font-family: Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif;
+    font-weight: bold;
+    letter-spacing: -0.018em;
+    page-break-after: avoid;
+}
+h1 { font-size: 20px; text-indent: -10px }
+h2 { font-size: 17px; text-indent: -10px }
+h3 { font-size: 15px; text-indent: -10px }
+hr { border: none;  border-top: 1px solid #ccb; margin: 2em 0 }
+address { font-style: normal }
+img { border: none }
+
+.underline { text-decoration: underline }
+ol.loweralpha { list-style-type: lower-alpha }
+ol.upperalpha { list-style-type: upper-alpha }
+ol.lowerroman { list-style-type: lower-roman }
+ol.upperroman { list-style-type: upper-roman }
+ol.arabic { list-style-type: decimal }
+
+.banner-link:link, .banner-link:visited {
+    text-decoration: none;
+    color: #b00;
+    border-bottom: 0px;
+}
+
+/* Link styles */
+:link, :visited {
+    text-decoration: none;
+    color: #b00;
+    border-bottom: 1px dotted #bbb;
+}
+h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited,
+h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited {
+    color: inherit;
+}
+
+/* Heading anchors */
+.anchor:link, .anchor:visited {
+    border: none;
+    color: #d7d7d7;
+    font-size: .8em;
+    vertical-align: text-top;
+}
+* > .anchor:link, * > .anchor:visited {
+    visibility: hidden;
+}
+h1:hover .anchor, h2:hover .anchor, h3:hover .anchor,
+h4:hover .anchor, h5:hover .anchor, h6:hover .anchor,
+span:hover .anchor {
+    visibility: visible;
+}
+
+a.missing:link, a.missing:visited, a.missing, span.missing,
+a.forbidden, span.forbidden { color: #998 }
+a.missing:hover { color: #000 }
+a.closed:link, a.closed:visited, span.closed { text-decoration: line-through }
+
+pre {
+    background: #f7f7f7;
+    border: 1px solid #d7d7d7;
+    margin: 1em 1.75em;
+    padding: .25em;
+    overflow: auto;
+}
+
+div.code {
+    background: #f7f7f7;
+    border: 1px solid #d7d7d7;
+    margin: 1em 1.75em;
+    padding: .25em;
+    overflow: auto
+}
+
+div.code pre { margin: 0; }
+
+dt {
+    font-weight: bold;
+}
+dd {
+    padding: 0 0 0.8em 0;
+}
+dd:last-child {
+    padding: 0 0 0 0;
+}
+
+/** Logo **/
+.site-logo {
+    font-size: 3rem;
+    line-height: 3rem;
+    font-weight: 200;
+    display: inline-block;
+    background: url('../../wp-content/themes/webkit/images/webkit.svg') no-repeat;
+    padding-left: 7rem;
+    color: #333;
+}
+
+.site-logo .tagline {
+    display: block;
+    font-size: 1.2rem;
+    line-height: 2rem;
+    letter-spacing: -0.05rem;
+    color: #666;
+}
+
+table {
+    padding: 0 0 0 0;
+}
+
+#contents {
+    font-size: 90%;
+    padding: 1em 2em 2em 2em
+}
+
+p {
+    margin: 0 0 1em 0;
+}
+
+p:last-child {
+    margin: 0 0 0 0;
+}
+
+th {
+    font-size: 90%;
+    font-weight: bold;   
+    padding: 0 0 1em 0;
+}
+
+td {
+    font-size: 90%;
+    text-align: center;
+    padding: 0 0 1em 0;
+}