<!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 <sabouhallawa@apple.com> 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 "size" 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 > 0 ? Math.floor((i - 1) / 2) : -1;
+ },
+
+ _leftIndex: function(i)
+ {
+ var leftIndex = i * 2 + 1;
+ return leftIndex < this._size ? leftIndex : -1;
+ },
+
+ _rightIndex: function(i)
+ {
+ var rightIndex = i * 2 + 2;
+ return rightIndex < 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 && right != -1)
+ return this._compare(this._values[left], this._values[right]) > 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()) > 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]) > 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]) > 0)
+ break;
+
+ this._values.swap(ci, i);
+ }
+ },
+
+ str: function()
+ {
+ var out = "Heap[" + this._size + "] = [";
+ for (var i = 0; i < this._size; ++i) {
+ out += this._values[i];
+ if (i < this._size - 1)
+ out += ", ";
+ }
+ return out + "]";
+ },
+
+ values: function(size) {
+ // Return the last "size" 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 "SimplePromise doesn't support multiple calls to then";
+
+ 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) + "%";
+ }
+}
+
+function RecordTable(element)
+{
+ this.element = element;
+ this.clear();
+}
+
+RecordTable.prototype =
+{
+ clear: function()
+ {
+ this.element.innerHTML = "";
+ },
+
+ _showTitles: function(row, queue, titles, message)
+ {
+ titles.forEach(function (title) {
+ var th = document.createElement("th");
+ th.textContent = title.text;
+ if (typeof message != "undefined" && message.length) {
+ th.appendChild(document.createElement('br'));
+ th.appendChild(document.createTextNode('[' + message +']'));
+ message = "";
+ }
+ if ("width" in title)
+ th.width = title.width + "%";
+ row.appendChild(th);
+ queue.push({element: th, titles: title.children });
+ });
+ },
+
+ _showHeader: function(suiteName, titles, message)
+ {
+ var row = document.createElement("tr");
+
+ 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 < len; ++i) {
+ var entry = queue.shift();
+
+ if (!entry.titles.length) {
+ entries.push(entry.element);
+ continue;
+ }
+
+ if (!row)
+ var row = document.createElement("tr");
+
+ this._showTitles(row, queue, entry.titles, "");
+ 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("td");
+ row.appendChild(td);
+ },
+
+ _showValue: function(row, testName, value)
+ {
+ var td = document.createElement("td");
+ td.textContent = value.toFixed(2);
+ row.appendChild(td);
+ },
+
+ _showSamples: function(row, testName, axes, samples, samplingTimeOffset)
+ {
+ var td = document.createElement("td");
+ var button = document.createElement("div");
+ button.className = "small-button";
+
+ button.addEventListener("click", function() {
+ window.showGraph(testName, axes, samples, samplingTimeOffset);
+ });
+
+ button.textContent = "Graph...";
+ td.appendChild(button);
+ row.appendChild(td);
+ },
+
+ _showTest: function(testName, titles, sampler, finalResults)
+ {
+ var row = document.createElement("tr");
+ var td = document.createElement("td");
+
+ 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("", 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, "");
+ }
+
+ 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 < 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 < 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 < this.experiments.length; ++index)
+ this.experiments[index].startSampling();
+
+ this.samplingTimeOffset = timeOffset / 1000;
+ },
+
+ sample: function(timeOffset, values)
+ {
+ if (values.length < this.experiments.length)
+ throw "Not enough sample points";
+
+ for (var index = 0; index < 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 <sabouhallawa@apple.com>
</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 "size" 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 <sabouhallawa@apple.com>
+
</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>