<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[190541] trunk/PerformanceTests</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/190541">190541</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2015-10-02 20:10:13 -0700 (Fri, 02 Oct 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add shared code for a new a graphics benchmark
https://bugs.webkit.org/show_bug.cgi?id=149691

Patch by Said Abou-Hallawa &lt;sabouhallawa@apple.com&gt; on 2015-10-02
Reviewed by Ryosuke Niwa.

This set of classes will be shared and used by the tests and the runner
of a new graphics benchmark.

* Animometer/resources: Added.
* Animometer/resources/algorithm.js: Added.
(Array.prototype.swap): Swaps two elements in an array.
(Heap): Binary Min/Max Heap object
(Heap.prototype._parentIndex): Given the child node index, it returns the parent index.
(Heap.prototype._leftIndex): Given the parent node index, it returns the left node index.
(Heap.prototype._rightIndex): Given the parent node index, it returns the right node index.
(Heap.prototype._childIndex): Given the parent node index, it returns the child index that may violate the heap property.
(Heap.prototype.init): Initializes the heap state.
(Heap.prototype.top): Returns the value stored at the top of the heap.
(Heap.prototype.push): Pushes a new node at the top of the heap.
(Heap.prototype.pop): Extracts the top node of the heap.
(Heap.prototype._bubble): Fixes the heap property by moving upward.
(Heap.prototype._sink): Fixes the heap property by moving downward.
(Heap.prototype.str): Prints the nodes of the heap to a string.
(Heap.prototype.values): Returns the last &quot;size&quot; heap elements values.

(Algorithm.createMinHeap): Creates a size-bounded min-heap object.
(Algorithm.createMaxHeap): Creates a size-bounded max-heap object.

* Animometer/resources/extensions.js: Added.
(Point): Point object but can be used as size also.
(Point.pointOnCircle): Given, the radius of the circle and the angle of the point, it returns a point object.
(Point.pointOnEllipse): Given, the radiuses of the ellipse and the angle of the point, it returns a point object.
(Point.prototype.get width): Should be called when the point is used as size.
(Point.prototype.get height): Should be called when the point is used as size.
(Point.prototype.get center): Should be called when the point is used as size.
(Point.prototype.add): Returns a new point = this + other.
(Point.prototype.subtract): Returns a new point = this - other.
(Point.prototype.multiply): Returns a new point = this * other.
(Point.prototype.move): Moves the point in a given direction, velocity, time period.

(Insets): Represents borders of a container.
(Insets.prototype.get width): Returns left + right.
(Insets.prototype.get height): Returns top + bottom.

(SimplePromise):
(SimplePromise.prototype.then):
(SimplePromise.prototype.resolve):
Moved from Animometer/runner/resources/benchmark-runner.js since tests also need it.

(Options): Benchmark running options as they are set by the user.

(ProgressBar): Manages a progress bar element. The progress bar is divided into equal length ranges.
(ProgressBar.prototype._progressToPercent): Converts the progress into a percentage.
(ProgressBar.prototype.incRange): Moves to the next range (a range is the running time of a single test).
(ProgressBar.prototype.setPos): Draws the current progress in the current range.

(RecordTable): Shows the results of running a benchmark in a tabular form.
(RecordTable.prototype.clear): Clears the results table.
(RecordTable.prototype._showTitles): Shows the header titles and appends the sub-titles to a queue.
(RecordTable.prototype._showHeader): Shows the table header titles.
(RecordTable.prototype._showEmpty): Shows an empty table cell.
(RecordTable.prototype._showValue): Shows a number value in the results table.
(RecordTable.prototype._showSamples): Shows a button for the sampled data graph.
(RecordTable.prototype._showTest): Shows the results of a single test.
(RecordTable.prototype._showSuite): Shows the results of a single suite.
(RecordTable.prototype.showRecord): Shows a single iteration for a single test.
(RecordTable.prototype.showIterations): Shows the results of all the suites of the iterations.

* Animometer/resources/sampler.js: Added.
(Statistics.sampleMean): Returns the sample mean.
(Statistics.unbiasedSampleStandardDeviation): Returns the unbiased sample variance (i.e. with Bessel's correction)
(Statistics.geometricMean): Returns the geometric mean.

(Experiment): Represents a sampling experiment.
(Experiment.prototype._init): Called when the object is created and when startSampling() is called.
(Experiment.prototype.startSampling): Called after warmup period. Restarts collecting sampled data points.
(Experiment.prototype.sample): Add a new data point.
(Experiment.prototype.mean): Returns the sample mean for the current sampled data points.
(Experiment.prototype.standardDeviation): Returns the sample standard deviation for the current sampled data points.
(Experiment.prototype.percentage): Returns the percentage of the standard deviation divided to the mean.
(Experiment.prototype.confidenceIntervalDelta): Calculates the confidence delta for the current sampled data given a confidence level.
(Experiment.prototype.concern): Returns the average of the worst given percentage from the sampled data.
(Experiment.prototype.score): Returns a score for the sampled data. It is the geometric mean of sampleMean and concern.

(Sampler): Represents a compound experiment. It manages sampling multiple data points at the same time offset.
(Sampler.prototype.startSampling): Called after warming up period. Restarts collecting sampled data points.
(Sampler.prototype.sample): Add a new data vector at a given time offset.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkPerformanceTestsChangeLog">trunk/PerformanceTests/ChangeLog</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/PerformanceTests/Animometer/resources/</li>
<li><a href="#trunkPerformanceTestsAnimometerresourcesalgorithmjs">trunk/PerformanceTests/Animometer/resources/algorithm.js</a></li>
<li><a href="#trunkPerformanceTestsAnimometerresourcesextensionsjs">trunk/PerformanceTests/Animometer/resources/extensions.js</a></li>
<li><a href="#trunkPerformanceTestsAnimometerresourcessamplerjs">trunk/PerformanceTests/Animometer/resources/sampler.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkPerformanceTestsAnimometerresourcesalgorithmjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Animometer/resources/algorithm.js (0 => 190541)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Animometer/resources/algorithm.js                                (rev 0)
+++ trunk/PerformanceTests/Animometer/resources/algorithm.js        2015-10-03 03:10:13 UTC (rev 190541)
</span><span class="lines">@@ -0,0 +1,142 @@
</span><ins>+Array.prototype.swap = function(i, j)
+{
+    var t = this[i];
+    this[i] = this[j];
+    this[j] = t;
+    return this;
+}
+
+function Heap(maxSize, compare)
+{
+    this._maxSize = maxSize;
+    this._compare = compare;
+    this._size = 0;
+    this._values = new Array(this._maxSize);
+}
+
+Heap.prototype =
+{
+    // This is a binary heap represented in an array. The root element is stored
+    // in the first element in the array. The root is followed by its two children.
+    // Then its four grandchildren and so on. So every level in the binary heap is
+    // doubled in the following level. Here is an example of the node indices and
+    // how they are related to their parents and children.
+    // ===========================================================================
+    //              0       1       2       3       4       5       6
+    // PARENT       -1      0       0       1       1       2       2
+    // LEFT         1       3       5       7       9       11      13
+    // RIGHT        2       4       6       8       10      12      14
+    // ===========================================================================
+    _parentIndex: function(i)
+    {
+        return i &gt; 0 ? Math.floor((i - 1) / 2) : -1;
+    },
+    
+    _leftIndex: function(i)
+    {
+        var leftIndex = i * 2 + 1;
+        return leftIndex &lt; this._size ? leftIndex : -1;
+    },
+    
+    _rightIndex: function(i)
+    {
+        var rightIndex = i * 2 + 2;
+        return rightIndex &lt; this._size ? rightIndex : -1;
+    },
+    
+    // Return the child index that may violate the heap property at index i.
+    _childIndex: function(i)
+    {
+        var left = this._leftIndex(i);
+        var right = this._rightIndex(i);
+
+        if (left != -1 &amp;&amp; right != -1)
+            return this._compare(this._values[left], this._values[right]) &gt; 0 ? left : right;
+        
+        return left != -1 ? left : right;
+    },
+    
+    init: function()
+    {
+        this._size = 0;
+    },
+    
+    top: function()
+    {
+        return this._size ? this._values[0] : NaN;
+    },
+    
+    push: function(value)
+    {
+        if (this._size == this._maxSize) {
+            // If size is bounded and the new value can be a parent of the top()
+            // if the size were unbounded, just ignore the new value.
+            if (this._compare(value, this.top()) &gt; 0)
+                return;
+            this.pop();
+        }
+        this._values[this._size++] = value;
+        this._bubble(this._size - 1);
+    },
+
+    pop: function()
+    {
+        if (!this._size)
+            return NaN;
+        
+        this._values[0] = this._values[--this._size];
+        this._sink(0);
+    },
+    
+    _bubble: function(i)
+    {
+        // Fix the heap property at index i given that parent is the only node that
+        // may violate the heap property.
+        for (var pi = this._parentIndex(i); pi != -1; i = pi, pi = this._parentIndex(pi)) {
+            if (this._compare(this._values[pi], this._values[i]) &gt; 0)
+                break;
+                
+            this._values.swap(pi, i);
+        }
+    },
+    
+    _sink: function(i)
+    {
+        // Fix the heap property at index i given that each of the left and the right
+        // sub-trees satisfies the heap property.
+        for (var ci = this._childIndex(i); ci != -1; i = ci, ci = this._childIndex(ci)) {
+            if (this._compare(this._values[i], this._values[ci]) &gt; 0)
+                break;
+            
+            this._values.swap(ci, i);
+        }
+    },
+    
+    str: function()
+    {
+        var out = &quot;Heap[&quot; + this._size + &quot;] = [&quot;;
+        for (var i = 0; i &lt; this._size; ++i) {
+            out += this._values[i];
+            if (i &lt; this._size - 1)
+                out += &quot;, &quot;;
+        }
+        return out + &quot;]&quot;;
+    },
+    
+    values: function(size) {
+        // Return the last &quot;size&quot; heap elements values.
+        var values = this._values.slice(0, this._size);
+        return values.sort(this._compare).slice(0, Math.min(size, this._size));
+    }
+}
+
+var Algorithm = {
+    createMinHeap: function(maxSize)
+    {
+        return new Heap(maxSize, function(a, b) { return b - a; });
+    },
+    
+    createMaxHeap: function(maxSize) {
+        return new Heap(maxSize, function(a, b) { return a - b; });
+    }
+}
</ins></span></pre></div>
<a id="trunkPerformanceTestsAnimometerresourcesextensionsjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Animometer/resources/extensions.js (0 => 190541)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Animometer/resources/extensions.js                                (rev 0)
+++ trunk/PerformanceTests/Animometer/resources/extensions.js        2015-10-03 03:10:13 UTC (rev 190541)
</span><span class="lines">@@ -0,0 +1,306 @@
</span><ins>+function Point(x, y)
+{
+    this.x = x;
+    this.y = y;
+}
+
+Point.pointOnCircle = function(angle, radius)
+{
+    return new Point(radius * Math.cos(angle), radius * Math.sin(angle));
+}
+
+Point.pointOnEllipse = function(angle, radiuses)
+{
+    return new Point(radiuses.x * Math.cos(angle), radiuses.y * Math.sin(angle));
+}
+
+Point.prototype =
+{
+    // Used when the point object is used as a size object.
+    get width()
+    {
+        return this.x;
+    },
+    
+    // Used when the point object is used as a size object.
+    get height()
+    {
+        return this.y;
+    },
+    
+    // Used when the point object is used as a size object.
+    get center()
+    {
+        return new Point(this.x / 2, this.y / 2);
+    },
+    
+    add: function(other)
+    {
+        return new Point(this.x + other.x, this.y + other.y);
+    },
+    
+    subtract: function(other)
+    {
+        return new Point(this.x - other.x, this.y - other.y);
+    },
+    
+    multiply: function(other)
+    {
+        return new Point(this.x * other.x, this.y * other.y);
+    },
+    
+    move: function(angle, velocity, timeDelta)
+    {
+        return this.add(Point.pointOnCircle(angle, velocity * (timeDelta / 1000)));
+    }
+}
+
+function Insets(top, right, bottom, left)
+{
+    this.top = top;
+    this.right = right;
+    this.bottom = bottom;
+    this.left = left;
+}
+
+Insets.prototype =
+{
+    get width() {
+        return this.left + this.right;
+    },
+
+    get height() {
+        return this.top + this.bottom;
+    }
+}
+
+function SimplePromise()
+{
+    this._chainedPromise = null;
+    this._callback = null;
+}
+
+SimplePromise.prototype.then = function (callback)
+{
+    if (this._callback)
+        throw &quot;SimplePromise doesn't support multiple calls to then&quot;;
+        
+    this._callback = callback;
+    this._chainedPromise = new SimplePromise;
+    
+    if (this._resolved)
+        this.resolve(this._resolvedValue);
+
+    return this._chainedPromise;
+}
+
+SimplePromise.prototype.resolve = function (value)
+{
+    if (!this._callback) {
+        this._resolved = true;
+        this._resolvedValue = value;
+        return;
+    }
+
+    var result = this._callback(value);
+    if (result instanceof SimplePromise) {
+        var chainedPromise = this._chainedPromise;
+        result.then(function (result) { chainedPromise.resolve(result); });
+    } else
+        this._chainedPromise.resolve(result);
+}
+
+function Options(testInterval, frameRate)
+{
+    this.testInterval = testInterval;
+    this.frameRate = frameRate;
+}
+
+function ProgressBar(element, ranges)
+{
+    this.element = element;
+    this.ranges = ranges;
+    this.currentRange = 0;
+}
+
+ProgressBar.prototype =
+{
+    _progressToPercent: function(progress)
+    {
+        return progress * (100 / this.ranges);
+    },
+    
+    incRange: function()
+    {
+        ++this.currentRange;
+    },
+    
+    setPos: function(progress)
+    {
+        this.element.style.width = this._progressToPercent(this.currentRange + progress) + &quot;%&quot;;
+    }
+}
+
+function RecordTable(element)
+{
+    this.element = element;
+    this.clear();
+}
+
+RecordTable.prototype =
+{
+    clear: function()
+    {
+        this.element.innerHTML = &quot;&quot;;
+    },
+    
+    _showTitles: function(row, queue, titles, message)
+    {
+        titles.forEach(function (title) {
+            var th = document.createElement(&quot;th&quot;);
+            th.textContent = title.text;
+            if (typeof message != &quot;undefined&quot; &amp;&amp; message.length) {
+                th.appendChild(document.createElement('br'));
+                th.appendChild(document.createTextNode('[' + message +']'));
+                message = &quot;&quot;;
+            }
+            if (&quot;width&quot; in title)
+                th.width = title.width + &quot;%&quot;;
+            row.appendChild(th);
+            queue.push({element: th, titles: title.children });
+        });
+    },
+    
+    _showHeader: function(suiteName, titles, message)
+    {
+        var row = document.createElement(&quot;tr&quot;);
+
+        var queue = [];
+        this._showTitles(row, queue, titles, message);
+        this.element.appendChild(row);
+
+        while (queue.length) {
+            var row = null;
+            var entries = [];
+
+            for (var i = 0, len = queue.length; i &lt; len; ++i) {
+                var entry = queue.shift();
+
+                if (!entry.titles.length) {
+                    entries.push(entry.element);
+                    continue;
+                }
+
+                if (!row)
+                    var row = document.createElement(&quot;tr&quot;);
+
+                this._showTitles(row, queue, entry.titles, &quot;&quot;);
+                entry.element.colSpan = entry.titles.length;
+            }
+
+            if (row) {
+                this.element.appendChild(row);
+                entries.forEach(function(entry) {
+                    ++entry.rowSpan;
+                });
+            }
+        }
+    },
+    
+    _showEmpty: function(row, testName)
+    {
+        var td = document.createElement(&quot;td&quot;);
+        row.appendChild(td);
+    },
+    
+    _showValue: function(row, testName, value)
+    {
+        var td = document.createElement(&quot;td&quot;);
+        td.textContent = value.toFixed(2);
+        row.appendChild(td);
+    },
+    
+    _showSamples: function(row, testName, axes, samples, samplingTimeOffset)
+    {
+        var td = document.createElement(&quot;td&quot;);
+        var button = document.createElement(&quot;div&quot;);
+        button.className = &quot;small-button&quot;;
+            
+        button.addEventListener(&quot;click&quot;, function() {
+            window.showGraph(testName, axes, samples, samplingTimeOffset);
+        });
+            
+        button.textContent = &quot;Graph...&quot;;
+        td.appendChild(button);
+        row.appendChild(td);
+    },
+    
+    _showTest: function(testName, titles, sampler, finalResults)
+    {
+        var row = document.createElement(&quot;tr&quot;);
+        var td = document.createElement(&quot;td&quot;);
+        
+        td.textContent = testName;
+        row.appendChild(td);
+        
+        var axes = [];
+        sampler.experiments.forEach(function(experiment, index) {
+            this._showValue(row, testName, experiment.mean());
+            this._showValue(row, testName, experiment.concern(Experiment.defaults.CONCERN));
+            this._showValue(row, testName, experiment.standardDeviation());
+            this._showValue(row, testName, experiment.percentage());
+            axes.push(titles[index + 1].text);
+            
+        }, this);
+
+        this._showValue(row, testName, sampler.experiments[0].score(Experiment.defaults.CONCERN));
+
+        if (finalResults)
+            this._showSamples(row, testName, axes, sampler.samples, sampler.samplingTimeOffset);
+        else
+            this._showEmpty(row, testName);
+            
+        this.element.appendChild(row);
+    },
+    
+    _showSuite: function(suite, suiteSamplers)
+    {
+        var scores = [];        
+        for (var testName in suiteSamplers) {
+            var test = testFromName(suite, testName);
+            var sampler = suiteSamplers[testName]; 
+            this._showTest(testName, suite.titles, sampler, true);
+            scores.push(sampler.experiments[0].score(Experiment.defaults.CONCERN));
+        }
+        return scores;
+    },
+    
+    showRecord: function(suite, test, sampler, message)
+    {
+        this.clear();        
+        this._showHeader(&quot;&quot;, suite.titles, message);
+        this._showTest(test.name, suite.titles, sampler, false);        
+    },
+    
+    showIterations: function(iterationsSamplers)
+    {
+        this.clear();
+
+        var scores = [];
+        var titles = null;
+        iterationsSamplers.forEach(function(suitesSamplers) {
+            for (var suiteName in suitesSamplers) {
+                var suite = suiteFromName(suiteName);
+                if (titles != suite.titles) {
+                    titles = suite.titles;
+                    this._showHeader(suiteName, titles, &quot;&quot;);
+                }
+
+                var suiteScores = this._showSuite(suite, suitesSamplers[suiteName]);
+                scores.push.apply(scores, suiteScores);
+            }
+        }, this);
+
+        return Statistics.geometricMean(scores);
+    }
+}
</ins></span></pre></div>
<a id="trunkPerformanceTestsAnimometerresourcessamplerjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Animometer/resources/sampler.js (0 => 190541)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Animometer/resources/sampler.js                                (rev 0)
+++ trunk/PerformanceTests/Animometer/resources/sampler.js        2015-10-03 03:10:13 UTC (rev 190541)
</span><span class="lines">@@ -0,0 +1,124 @@
</span><ins>+var Statistics =
+{
+    sampleMean: function(numberOfSamples, sum)
+    {
+        if (numberOfSamples &lt; 1)
+            return 0;
+        return sum / numberOfSamples;
+    },
+
+    // With sum and sum of squares, we can compute the sample standard deviation in O(1).
+    // See https://rniwa.com/2012-11-10/sample-standard-deviation-in-terms-of-sum-and-square-sum-of-samples/
+    unbiasedSampleStandardDeviation: function(numberOfSamples, sum, squareSum)
+    {
+        if (numberOfSamples &lt; 2)
+            return 0;
+        return Math.sqrt((squareSum - sum * sum / numberOfSamples) / (numberOfSamples - 1));
+    },
+
+    geometricMean: function(values)
+    {
+        if (!values.length)
+            return 0;
+        var roots = values.map(function(value) { return  Math.pow(value, 1 / values.length); })
+        return roots.reduce(function(a, b) { return a * b; });
+    }   
+}
+
+function Experiment()
+{
+    this._init();
+    this._maxHeap = Algorithm.createMaxHeap(Experiment.defaults.CONCERN_SIZE);
+}
+
+Experiment.defaults =
+{
+    CONCERN: 5,
+    CONCERN_SIZE: 100,
+}
+
+Experiment.prototype =
+{
+    _init: function()
+    {
+        this._sum = 0;
+        this._squareSum = 0;
+        this._numberOfSamples = 0;
+    },
+    
+    // Called after a warm-up period
+    startSampling: function()
+    {
+        var mean = this.mean();
+        this._init();
+        this._maxHeap.init();
+        this.sample(mean);
+    },
+    
+    sample: function(value)
+    {
+        this._sum += value;
+        this._squareSum += value * value;
+        this._maxHeap.push(value);
+        ++this._numberOfSamples;
+    },
+    
+    mean: function()
+    {
+        return Statistics.sampleMean(this._numberOfSamples, this._sum);
+    },
+    
+    standardDeviation: function()
+    {
+        return Statistics.unbiasedSampleStandardDeviation(this._numberOfSamples, this._sum, this._squareSum);
+    },
+    
+    percentage: function()
+    {
+        var mean = this.mean();
+        return mean ? this.standardDeviation() * 100 / mean : 0;
+    },
+    
+    concern: function(percentage)
+    {
+        var size = Math.ceil(this._numberOfSamples * percentage / 100);
+        var values = this._maxHeap.values(size);
+        return values.length ? values.reduce(function(a, b) { return a + b; }) / values.length : 0;
+    },
+    
+    score: function(percentage)
+    {
+        return Statistics.geometricMean([this.mean(), Math.max(this.concern(percentage), 1)]);
+    }
+}
+
+function Sampler(count)
+{
+    this.experiments = [];
+    while (count--)
+        this.experiments.push(new Experiment());
+    this.samples = [];
+    this.samplingTimeOffset = 0;
+}
+
+Sampler.prototype =
+{
+    startSampling: function(timeOffset)
+    {
+        for (var index = 0; index &lt; this.experiments.length; ++index)
+            this.experiments[index].startSampling();
+            
+        this.samplingTimeOffset = timeOffset / 1000;
+    },
+    
+    sample: function(timeOffset, values)
+    {
+        if (values.length &lt; this.experiments.length)
+            throw &quot;Not enough sample points&quot;;
+                    
+        for (var index = 0; index &lt; this.experiments.length; ++index)
+            this.experiments[index].sample(values[index]);
+            
+        this.samples.push({ timeOffset: timeOffset / 1000, values: values });
+    }
+}
</ins></span></pre></div>
<a id="trunkPerformanceTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/PerformanceTests/ChangeLog (190540 => 190541)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/ChangeLog        2015-10-03 02:28:22 UTC (rev 190540)
+++ trunk/PerformanceTests/ChangeLog        2015-10-03 03:10:13 UTC (rev 190541)
</span><span class="lines">@@ -1,5 +1,95 @@
</span><span class="cx"> 2015-10-02  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        Add shared code for a new a graphics benchmark
+        https://bugs.webkit.org/show_bug.cgi?id=149691
+
+        Reviewed by Ryosuke Niwa.
+
+        This set of classes will be shared and used by the tests and the runner
+        of a new graphics benchmark.
+
+        * Animometer/resources: Added.
+        * Animometer/resources/algorithm.js: Added.
+        (Array.prototype.swap): Swaps two elements in an array.
+        (Heap): Binary Min/Max Heap object
+        (Heap.prototype._parentIndex): Given the child node index, it returns the parent index.
+        (Heap.prototype._leftIndex): Given the parent node index, it returns the left node index.
+        (Heap.prototype._rightIndex): Given the parent node index, it returns the right node index.
+        (Heap.prototype._childIndex): Given the parent node index, it returns the child index that may violate the heap property.
+        (Heap.prototype.init): Initializes the heap state.
+        (Heap.prototype.top): Returns the value stored at the top of the heap.
+        (Heap.prototype.push): Pushes a new node at the top of the heap.
+        (Heap.prototype.pop): Extracts the top node of the heap.
+        (Heap.prototype._bubble): Fixes the heap property by moving upward.
+        (Heap.prototype._sink): Fixes the heap property by moving downward.
+        (Heap.prototype.str): Prints the nodes of the heap to a string.
+        (Heap.prototype.values): Returns the last &quot;size&quot; heap elements values.
+
+        (Algorithm.createMinHeap): Creates a size-bounded min-heap object.
+        (Algorithm.createMaxHeap): Creates a size-bounded max-heap object.
+        
+        * Animometer/resources/extensions.js: Added.
+        (Point): Point object but can be used as size also.
+        (Point.pointOnCircle): Given, the radius of the circle and the angle of the point, it returns a point object.
+        (Point.pointOnEllipse): Given, the radiuses of the ellipse and the angle of the point, it returns a point object.
+        (Point.prototype.get width): Should be called when the point is used as size.
+        (Point.prototype.get height): Should be called when the point is used as size.
+        (Point.prototype.get center): Should be called when the point is used as size.
+        (Point.prototype.add): Returns a new point = this + other.
+        (Point.prototype.subtract): Returns a new point = this - other.
+        (Point.prototype.multiply): Returns a new point = this * other.
+        (Point.prototype.move): Moves the point in a given direction, velocity, time period.
+
+        (Insets): Represents borders of a container.
+        (Insets.prototype.get width): Returns left + right.
+        (Insets.prototype.get height): Returns top + bottom.
+
+        (SimplePromise):
+        (SimplePromise.prototype.then):
+        (SimplePromise.prototype.resolve):
+        Moved from Animometer/runner/resources/benchmark-runner.js since tests also need it.
+
+        (Options): Benchmark running options as they are set by the user.
+
+        (ProgressBar): Manages a progress bar element. The progress bar is divided into equal length ranges.
+        (ProgressBar.prototype._progressToPercent): Converts the progress into a percentage.
+        (ProgressBar.prototype.incRange): Moves to the next range (a range is the running time of a single test).
+        (ProgressBar.prototype.setPos): Draws the current progress in the current range.
+
+        (RecordTable): Shows the results of running a benchmark in a tabular form.
+        (RecordTable.prototype.clear): Clears the results table.
+        (RecordTable.prototype._showTitles): Shows the header titles and appends the sub-titles to a queue.
+        (RecordTable.prototype._showHeader): Shows the table header titles.
+        (RecordTable.prototype._showEmpty): Shows an empty table cell.
+        (RecordTable.prototype._showValue): Shows a number value in the results table.
+        (RecordTable.prototype._showSamples): Shows a button for the sampled data graph.
+        (RecordTable.prototype._showTest): Shows the results of a single test.
+        (RecordTable.prototype._showSuite): Shows the results of a single suite.
+        (RecordTable.prototype.showRecord): Shows a single iteration for a single test.
+        (RecordTable.prototype.showIterations): Shows the results of all the suites of the iterations. 
+
+        * Animometer/resources/sampler.js: Added.
+        (Statistics.sampleMean): Returns the sample mean.
+        (Statistics.unbiasedSampleStandardDeviation): Returns the unbiased sample variance (i.e. with Bessel's correction)
+        (Statistics.geometricMean): Returns the geometric mean.
+
+        (Experiment): Represents a sampling experiment. 
+        (Experiment.prototype._init): Called when the object is created and when startSampling() is called.
+        (Experiment.prototype.startSampling): Called after warmup period. Restarts collecting sampled data points.
+        (Experiment.prototype.sample): Add a new data point.
+        (Experiment.prototype.mean): Returns the sample mean for the current sampled data points.
+        (Experiment.prototype.standardDeviation): Returns the sample standard deviation for the current sampled data points.
+        (Experiment.prototype.percentage): Returns the percentage of the standard deviation divided to the mean.
+        (Experiment.prototype.confidenceIntervalDelta): Calculates the confidence delta for the current sampled data given a confidence level.
+        (Experiment.prototype.concern): Returns the average of the worst given percentage from the sampled data.
+        (Experiment.prototype.score): Returns a score for the sampled data. It is the geometric mean of sampleMean and concern.
+
+        (Sampler): Represents a compound experiment. It manages sampling multiple data points at the same time offset.
+        (Sampler.prototype.startSampling): Called after warming up period. Restarts collecting sampled data points.
+        (Sampler.prototype.sample): Add a new data vector at a given time offset.
+        
+2015-10-02  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
</ins><span class="cx">         Add the test runner for a new a graphics benchmark
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=149683
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>