Move ResultsTable functionality not needed for release tests out.
authorjonlee@apple.com <jonlee@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Feb 2016 03:30:47 +0000 (03:30 +0000)
committerjonlee@apple.com <jonlee@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Feb 2016 03:30:47 +0000 (03:30 +0000)
Move reporting of score and mean to selection of the time-based graph.

* Animometer/developer.html: Rename graph-options to time-graph-options.
* Animometer/resources/debug-runner/animometer.js:
(DeveloperResultsTable): Moved from runner/animometer.js. Switch from mean
values to "average" objects which can hold stdev. Move graph button and
calculation of noisy measurements here. Sophisticated header processing
is not needed in release suite.
(populateTable): Use DeveloperResultsTable.
* Animometer/resources/debug-runner/graph.js: Pull time graph creation to
its own function, and add a new onGraphTypeChanged handler in preparation
of a complexity graph to be added later.
* Animometer/resources/runner/animometer.js:
(ResultsTable): Simplify to just handle test names and scores.

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

PerformanceTests/Animometer/developer.html
PerformanceTests/Animometer/resources/debug-runner/animometer.js
PerformanceTests/Animometer/resources/debug-runner/graph.js
PerformanceTests/Animometer/resources/runner/animometer.js
PerformanceTests/ChangeLog

index 0dfdf67..c2cece2 100644 (file)
@@ -8,14 +8,14 @@
     <script src="resources/strings.js"></script>
     <script src="resources/extensions.js"></script>
 
-    <script src="resources/runner/tests.js" charset="utf-8"></script>
+    <script src="resources/runner/tests.js"></script>
     <script src="resources/debug-runner/tests.js" charset="utf-8"></script>
-    <script src="resources/runner/animometer.js" charset="utf-8"></script>
+    <script src="resources/runner/animometer.js"></script>
     <script src="resources/debug-runner/animometer.js"></script>
 
     <script src="resources/runner/benchmark-runner.js"></script>
     <script src="resources/debug-runner/d3.min.js"></script>
-    <script src="resources/debug-runner/graph.js"></script>
+    <script src="resources/debug-runner/graph.js" charset="utf-8"></script>
 </head>
 <body>
     <main>
                 <h1>Graph:</h1>
             </header>
             <nav>
-                <form name="graph-options">
+                <form name="time-graph-options">
                     <ul>
                         <li><label><input type="checkbox" name="markers" checked> Markers</label>
                             <span>time: <span class="time"></span></span></li>
index 8f2fd36..7f740ae 100644 (file)
@@ -19,6 +19,111 @@ ProgressBar = Utilities.createClass(
     }
 });
 
+DeveloperResultsTable = Utilities.createSubclass(ResultsTable,
+    function(element, headers)
+    {
+        ResultsTable.call(this, element, headers);
+    }, {
+
+    _addGraphButton: function(td, testName, testResults)
+    {
+        var data = testResults[Strings.json.samples];
+        if (!data)
+            return;
+
+        var button = Utilities.createElement("button", { class: "small-button" }, td);
+
+        button.addEventListener("click", function() {
+            var graphData = {
+                axes: [Strings.text.complexity, Strings.text.frameRate],
+                samples: data,
+                complexityAverageSamples: testResults[Strings.json.complexityAverageSamples],
+                averages: {},
+                marks: testResults[Strings.json.marks]
+            };
+            [Strings.json.experiments.complexity, Strings.json.experiments.frameRate].forEach(function(experiment) {
+                if (experiment in testResults)
+                    graphData.averages[experiment] = testResults[experiment];
+            });
+
+            [
+                Strings.json.score,
+                Strings.json.regressions.timeRegressions,
+                Strings.json.regressions.complexityRegression,
+                Strings.json.regressions.complexityAverageRegression,
+                Strings.json.targetFrameLength
+            ].forEach(function(key) {
+                if (testResults[key])
+                    graphData[key] = testResults[key];
+            });
+
+            benchmarkController.showTestGraph(testName, graphData);
+        });
+
+        button.textContent = Strings.text.graph + "...";
+    },
+
+    _isNoisyMeasurement: function(jsonExperiment, data, measurement, options)
+    {
+        const percentThreshold = 10;
+        const averageThreshold = 2;
+
+        if (measurement == Strings.json.measurements.percent)
+            return data[Strings.json.measurements.percent] >= percentThreshold;
+
+        if (jsonExperiment == Strings.json.experiments.frameRate && measurement == Strings.json.measurements.average)
+            return Math.abs(data[Strings.json.measurements.average] - options["frame-rate"]) >= averageThreshold;
+
+        return false;
+    },
+
+    _addTest: function(testName, testResults, options)
+    {
+        var row = Utilities.createElement("tr", {}, this.element);
+
+        var isNoisy = false;
+        [Strings.json.experiments.complexity, Strings.json.experiments.frameRate].forEach(function (experiment) {
+            var data = testResults[experiment];
+            for (var measurement in data) {
+                if (this._isNoisyMeasurement(experiment, data, measurement, options))
+                    isNoisy = true;
+            }
+        }, this);
+
+        this._flattenedHeaders.forEach(function (header) {
+            var className = "";
+            if (header.className) {
+                if (typeof header.className == "function")
+                    className = header.className(testResults, options);
+                else
+                    className = header.className;
+            }
+
+            if (header.title == Strings.text.testName) {
+                if (isNoisy)
+                    className += " noisy-results";
+                var td = Utilities.createElement("td", { class: className }, row);
+                td.textContent = testName;
+                return;
+            }
+
+            var td = Utilities.createElement("td", { class: className }, row);
+            if (header.title == Strings.text.graph) {
+                this._addGraphButton(td, testName, testResults);
+            } else if (!("text" in header)) {
+                td.textContent = testResults[header.title];
+            } else if (typeof header.text == "string") {
+                var data = testResults[header.text];
+                if (typeof data == "number")
+                    data = data.toFixed(2);
+                td.textContent = data;
+            } else {
+                td.textContent = header.text(testResults, testName);
+            }
+        }, this);
+    }
+});
+
 Utilities.extendObject(window.benchmarkRunnerClient, {
     testsCount: null,
     progressBar: null,
@@ -45,6 +150,12 @@ Utilities.extendObject(window.sectionsManager, {
     setSectionHeader: function(sectionIdentifier, title)
     {
         document.querySelector("#" + sectionIdentifier + " h1").textContent = title;
+    },
+
+    populateTable: function(tableIdentifier, headers, data)
+    {
+        var table = new DeveloperResultsTable(document.getElementById(tableIdentifier), headers);
+        table.showIterations(data, benchmarkRunnerClient.options);
     }
 });
 
@@ -343,7 +454,7 @@ Utilities.extendObject(window.benchmarkController, {
     initialize: function()
     {
         document.forms["benchmark-options"].addEventListener("change", benchmarkController.onBenchmarkOptionsChanged, true);
-        document.forms["graph-options"].addEventListener("change", benchmarkController.onGraphOptionsChanged, true);
+        document.forms["time-graph-options"].addEventListener("change", benchmarkController.onTimeGraphOptionsChanged, true);
         optionsManager.updateUIFromLocalStorage();
         suitesManager.createElements();
         suitesManager.updateUIFromLocalStorage();
@@ -397,10 +508,9 @@ Utilities.extendObject(window.benchmarkController, {
         document.querySelector("#results-json div").classList.remove("hidden");
     },
 
-    showTestGraph: function(testName, score, mean, graphData)
+    showTestGraph: function(testName, graphData)
     {
         sectionsManager.setSectionHeader("test-graph", testName);
-        sectionsManager.setSectionScore("test-graph", score, mean);
         sectionsManager.showSection("test-graph", true);
         this.updateGraphData(graphData);
     }
index 46564f0..3384d42 100644 (file)
@@ -5,12 +5,22 @@ Utilities.extendObject(window.benchmarkController, {
     {
         var element = document.getElementById("test-graph-data");
         element.innerHTML = "";
+        element.graphData = graphData;
         document.querySelector("hr").style.width = this.layoutCounter++ + "px";
 
         var margins = new Insets(30, 30, 30, 40);
         var size = Point.elementClientSize(element).subtract(margins.size);
 
+        this.createTimeGraph(graphData, margins, size);
+        this.onTimeGraphOptionsChanged();
+
+        this.onGraphTypeChanged();
+    },
+
+    createTimeGraph: function(graphData, margins, size)
+    {
         var svg = d3.select("#test-graph-data").append("svg")
+            .attr("id", "time-graph")
             .attr("width", size.width + margins.left + margins.right)
             .attr("height", size.height + margins.top + margins.bottom)
             .append("g")
@@ -22,10 +32,14 @@ Utilities.extendObject(window.benchmarkController, {
         // Axis scales
         var x = d3.scale.linear()
                 .range([0, size.width])
-                .domain([0, d3.max(graphData.samples, function(s) { return s.time; })]);
+                .domain([
+                    Math.min(d3.min(graphData.samples, function(s) { return s.time; }), 0),
+                    d3.max(graphData.samples, function(s) { return s.time; })]);
+        var complexityMax = d3.max(graphData.samples, function(s) { return s.complexity; });
+
         var yLeft = d3.scale.linear()
                 .range([size.height, 0])
-                .domain([0, d3.max(graphData.samples, function(s) { return s.complexity; })]);
+                .domain([0, complexityMax]);
         var yRight = d3.scale.linear()
                 .range([size.height, 0])
                 .domain([1000/20, 1000/60]);
@@ -98,31 +112,32 @@ Utilities.extendObject(window.benchmarkController, {
                 .attr("class", "marker")
                 .attr("transform", "translate(" + xLocation + ", 0)");
             markerGroup.append("text")
-                    .attr("transform", "translate(10, " + (yMin - 10) + ") rotate(-90)")
-                    .style("text-anchor", "start")
-                    .text(markName)
+                .attr("transform", "translate(10, " + (yMin - 10) + ") rotate(-90)")
+                .style("text-anchor", "start")
+                .text(markName)
             markerGroup.append("line")
-                    .attr("x1", 0)
-                    .attr("x2", 0)
-                    .attr("y1", yMin)
-                    .attr("y2", yMax);
+                .attr("x1", 0)
+                .attr("x2", 0)
+                .attr("y1", yMin)
+                .attr("y2", yMax);
         }
 
-        // left-mean
-        svg.append("line")
-            .attr("x1", x(0))
-            .attr("x2", size.width)
-            .attr("y1", yLeft(graphData.mean[0]))
-            .attr("y2", yLeft(graphData.mean[0]))
-            .attr("class", "left-mean mean");
-
-        // right-mean
-        svg.append("line")
-            .attr("x1", x(0))
-            .attr("x2", size.width)
-            .attr("y1", yRight(graphData.mean[1]))
-            .attr("y2", yRight(graphData.mean[1]))
-            .attr("class", "right-mean mean");
+        if (Strings.json.experiments.complexity in graphData.averages) {
+            var complexity = graphData.averages[Strings.json.experiments.complexity];
+            var regression = svg.append("g")
+                .attr("class", "complexity mean");
+            this._addRegressionLine(regression, x, yLeft, [[graphData.samples[0].time, complexity.average], [graphData.samples[graphData.samples.length - 1].time, complexity.average]], complexity.stdev);
+        }
+        if (Strings.json.experiments.frameRate in graphData.averages) {
+            var frameRate = graphData.averages[Strings.json.experiments.frameRate];
+            var average = yRight(1000/frameRate.average);
+            svg.append("line")
+                .attr("x1", x(0))
+                .attr("x2", size.width)
+                .attr("y1", average)
+                .attr("y2", average)
+                .attr("class", "fps mean");
+        }
 
         // right-target
         if (targetFrameLength) {
@@ -135,7 +150,7 @@ Utilities.extendObject(window.benchmarkController, {
         }
 
         // Cursor
-        var cursorGroup = svg.append("g").attr("id", "cursor");
+        var cursorGroup = svg.append("g").attr("class", "cursor");
         cursorGroup.append("line")
             .attr("x1", 0)
             .attr("x2", 0)
@@ -179,19 +194,19 @@ Utilities.extendObject(window.benchmarkController, {
             .attr("fill", "transparent")
             .attr("x", 0)
             .attr("y", 0)
-            .attr("width", size.x)
-            .attr("height", size.y);
+            .attr("width", size.width)
+            .attr("height", size.height);
 
         var timeBisect = d3.bisector(function(d) { return d.time; }).right;
         var statsToHighlight = ["complexity", "rawFPS", "filteredFPS"];
         area.on("mouseover", function() {
-            document.getElementById("cursor").classList.remove("hidden");
+            document.querySelector("#time-graph .cursor").classList.remove("hidden");
             document.querySelector("#test-graph nav").classList.remove("hide-data");
         }).on("mouseout", function() {
-            document.getElementById("cursor").classList.add("hidden");
+            document.querySelector("#time-graph .cursor").classList.add("hidden");
             document.querySelector("#test-graph nav").classList.add("hide-data");
         }).on("mousemove", function() {
-            var form = document.forms["graph-options"].elements;
+            var form = document.forms["time-graph-options"].elements;
 
             var mx_domain = x.invert(d3.mouse(this)[0]);
             var index = Math.min(timeBisect(allData, mx_domain), allData.length - 1);
@@ -229,9 +244,9 @@ Utilities.extendObject(window.benchmarkController, {
                     cursorGroup.select("." + name)
                         .attr("cx", cursor_x)
                         .attr("cy", data_y);
-                    document.querySelector("#cursor ." + name).classList.remove("hidden");
+                    document.querySelector("#time-graph .cursor ." + name).classList.remove("hidden");
                 } else
-                    document.querySelector("#cursor ." + name).classList.add("hidden");
+                    document.querySelector("#time-graph .cursor ." + name).classList.add("hidden");
             });
 
             if (form["rawFPS"].checked)
@@ -243,27 +258,58 @@ Utilities.extendObject(window.benchmarkController, {
                 .attr("y2", Math.max.apply(null, ys));
 
         });
-        this.onGraphOptionsChanged();
     },
 
-    onGraphOptionsChanged: function() {
-        var form = document.forms["graph-options"].elements;
-
-        function showOrHideNodes(isShown, selector) {
-            var nodeList = document.querySelectorAll(selector);
-            if (isShown) {
-                for (var i = 0; i < nodeList.length; ++i)
-                    nodeList[i].classList.remove("hidden");
-            } else {
-                for (var i = 0; i < nodeList.length; ++i)
-                    nodeList[i].classList.add("hidden");
+    _showOrHideNodes: function(isShown, selector) {
+        var nodeList = document.querySelectorAll(selector);
+        if (isShown) {
+            for (var i = 0; i < nodeList.length; ++i)
+                nodeList[i].classList.remove("hidden");
+        } else {
+            for (var i = 0; i < nodeList.length; ++i)
+                nodeList[i].classList.add("hidden");
+        }
+    },
+
+    onTimeGraphOptionsChanged: function() {
+        var form = document.forms["time-graph-options"].elements;
+        benchmarkController._showOrHideNodes(form["markers"].checked, ".marker");
+        benchmarkController._showOrHideNodes(form["averages"].checked, "#test-graph-data .mean");
+        benchmarkController._showOrHideNodes(form["complexity"].checked, "#complexity");
+        benchmarkController._showOrHideNodes(form["rawFPS"].checked, "#rawFPS");
+        benchmarkController._showOrHideNodes(form["filteredFPS"].checked, "#filteredFPS");
+        benchmarkController._showOrHideNodes(form["regressions"].checked, "#regressions");
+    },
+
+    onGraphTypeChanged: function() {
+        var form = document.forms["graph-type"].elements;
+        var graphData = document.getElementById("test-graph-data").graphData;
+        var isTimeSelected = true; 
+
+        benchmarkController._showOrHideNodes(isTimeSelected, "#time-graph");
+        benchmarkController._showOrHideNodes(isTimeSelected, "form[name=time-graph-options]");
+
+        var score, mean;
+        if (isTimeSelected) {
+            score = graphData.score.toFixed(2);
+
+            var regression = graphData.averages.complexity;
+            mean = [
+                "mean: ",
+                regression.average.toFixed(2),
+                " ± ",
+                regression.stdev.toFixed(2),
+                " (",
+                regression.percent.toFixed(2),
+                "%)"];
+            if (regression.concern) {
+                mean = mean.concat([
+                    ", worst 5%: ",
+                    regression.concern.toFixed(2)]);
             }
+            mean = mean.join("");
         }
 
-        showOrHideNodes(form["markers"].checked, ".marker");
-        showOrHideNodes(form["averages"].checked, ".mean");
-        showOrHideNodes(form["complexity"].checked, "#complexity");
-        showOrHideNodes(form["rawFPS"].checked, "#rawFPS");
-        showOrHideNodes(form["filteredFPS"].checked, "#filteredFPS");
+        sectionsManager.setSectionScore("test-graph", score, mean);
     }
 });
index fda3468..c15d0af 100644 (file)
@@ -98,58 +98,6 @@ ResultsTable = Utilities.createClass(
         });
     },
 
-    _addGraphButton: function(td, testName, testResults)
-    {
-        var data = testResults[Strings.json.samples];
-        if (!data)
-            return;
-
-        var button = Utilities.createElement("button", { class: "small-button" }, td);
-
-        button.addEventListener("click", function() {
-            var score = testResults[Strings.json.score].toFixed(2);
-            var complexity = testResults[Strings.json.experiments.complexity];
-            var mean = [
-                "mean: ",
-                complexity[Strings.json.measurements.average].toFixed(2),
-                " ± ",
-                complexity[Strings.json.measurements.stdev].toFixed(2),
-                " (",
-                complexity[Strings.json.measurements.percent].toFixed(2),
-                "%), worst 5%: ",
-                complexity[Strings.json.measurements.concern].toFixed(2)].join("");
-
-            var graphData = {
-                axes: [Strings.text.experiments.complexity, Strings.text.experiments.frameRate],
-                mean: [
-                    testResults[Strings.json.experiments.complexity][Strings.json.measurements.average],
-                    1000 / testResults[Strings.json.experiments.frameRate][Strings.json.measurements.average]
-                ],
-                samples: data,
-                marks: testResults[Strings.json.marks]
-            }
-            if (testResults[Strings.json.targetFrameLength])
-                graphData.targetFrameLength = testResults[Strings.json.targetFrameLength];
-            benchmarkController.showTestGraph(testName, score, mean, graphData);
-        });
-
-        button.textContent = Strings.text.results.graph + "...";
-    },
-
-    _isNoisyMeasurement: function(jsonExperiment, data, measurement, options)
-    {
-        const percentThreshold = 10;
-        const averageThreshold = 2;
-
-        if (measurement == Strings.json.measurements.percent)
-            return data[Strings.json.measurements.percent] >= percentThreshold;
-
-        if (jsonExperiment == Strings.json.experiments.frameRate && measurement == Strings.json.measurements.average)
-            return Math.abs(data[Strings.json.measurements.average] - options["frame-rate"]) >= averageThreshold;
-
-        return false;
-    },
-
     _addEmptyRow: function()
     {
         var row = Utilities.createElement("tr", {}, this.element);
@@ -162,45 +110,15 @@ ResultsTable = Utilities.createClass(
     {
         var row = Utilities.createElement("tr", {}, this.element);
 
-        var isNoisy = false;
-        [Strings.json.experiments.complexity, Strings.json.experiments.frameRate].forEach(function (experiment) {
-            var data = testResults[experiment];
-            for (var measurement in data) {
-                if (this._isNoisyMeasurement(experiment, data, measurement, options))
-                    isNoisy = true;
-            }
-        }, this);
-
         this._flattenedHeaders.forEach(function (header) {
-            var className = "";
-            if (header.className) {
-                if (typeof header.className == "function")
-                    className = header.className(testResults, options);
-                else
-                    className = header.className;
-            }
-
+            var td = Utilities.createElement("td", {}, row);
             if (header.title == Strings.text.testName) {
-                var titleClassName = className;
-                if (isNoisy)
-                    titleClassName += " noisy-results";
-                var td = Utilities.createElement("td", { class: titleClassName }, row);
                 td.textContent = testName;
-                return;
-            }
-
-            var td = Utilities.createElement("td", { class: className }, row);
-            if (header.title == Strings.text.results.graph) {
-                this._addGraphButton(td, testName, testResults);
-            } else if (!("text" in header)) {
-                td.textContent = testResults[header.title];
-            } else if (typeof header.text == "string") {
+            } else if (header.text) {
                 var data = testResults[header.text];
                 if (typeof data == "number")
                     data = data.toFixed(2);
                 td.textContent = data;
-            } else {
-                td.textContent = header.text(testResults, testName);
             }
         }, this);
     },
index 64d18f4..d06fd75 100644 (file)
@@ -1,5 +1,23 @@
 2016-02-07  Jon Lee  <jonlee@apple.com>
 
+        Move ResultsTable functionality not needed for release tests out.
+        Move reporting of score and mean to selection of the time-based graph.
+
+        * Animometer/developer.html: Rename graph-options to time-graph-options.
+        * Animometer/resources/debug-runner/animometer.js:
+        (DeveloperResultsTable): Moved from runner/animometer.js. Switch from mean
+        values to "average" objects which can hold stdev. Move graph button and
+        calculation of noisy measurements here. Sophisticated header processing
+        is not needed in release suite.
+        (populateTable): Use DeveloperResultsTable.
+        * Animometer/resources/debug-runner/graph.js: Pull time graph creation to
+        its own function, and add a new onGraphTypeChanged handler in preparation
+        of a complexity graph to be added later.
+        * Animometer/resources/runner/animometer.js:
+        (ResultsTable): Simplify to just handle test names and scores.
+
+2016-02-07  Jon Lee  <jonlee@apple.com>
+
         Tests: reuse objects already made.
 
         Avoid thrash of object creation and removal by maintaining an index that