| Index: tools/telemetry/support/html_output/results-template.html
|
| diff --git a/tools/telemetry/support/html_output/results-template.html b/tools/telemetry/support/html_output/results-template.html
|
| deleted file mode 100644
|
| index b27c08003bb1bff6b8e2dd0d3c54e9303c80f8db..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/support/html_output/results-template.html
|
| +++ /dev/null
|
| @@ -1,1488 +0,0 @@
|
| -<!DOCTYPE html>
|
| -<html>
|
| -<head>
|
| -<title>Telemetry Performance Test Results</title>
|
| -<style type="text/css">
|
| -
|
| -section {
|
| - background: white;
|
| - padding: 10px;
|
| - position: relative;
|
| -}
|
| -
|
| -.collapsed:before {
|
| - color: #ccc;
|
| - content: '\25B8\00A0';
|
| -}
|
| -
|
| -.expanded:before {
|
| - color: #eee;
|
| - content: '\25BE\00A0';
|
| -}
|
| -
|
| -.line-plots {
|
| - padding-left: 25px;
|
| -}
|
| -
|
| -.line-plots > div {
|
| - display: inline-block;
|
| - width: 90px;
|
| - height: 40px;
|
| - margin-right: 10px;
|
| -}
|
| -
|
| -.lage-line-plots {
|
| - padding-left: 25px;
|
| -}
|
| -
|
| -.large-line-plots > div, .histogram-plots > div {
|
| - display: inline-block;
|
| - width: 400px;
|
| - height: 200px;
|
| - margin-right: 10px;
|
| -}
|
| -
|
| -.large-line-plot-labels > div, .histogram-plot-labels > div {
|
| - display: inline-block;
|
| - width: 400px;
|
| - height: 11px;
|
| - margin-right: 10px;
|
| - color: #545454;
|
| - text-align: center;
|
| - font-size: 11px;
|
| -}
|
| -
|
| -.closeButton {
|
| - display: inline-block;
|
| - background: #eee;
|
| - background: linear-gradient(rgb(220, 220, 220), rgb(255, 255, 255));
|
| - border: inset 1px #ddd;
|
| - border-radius: 4px;
|
| - float: right;
|
| - font-size: small;
|
| - -webkit-user-select: none;
|
| - font-weight: bold;
|
| - padding: 1px 4px;
|
| -}
|
| -
|
| -.closeButton:hover {
|
| - background: #F09C9C;
|
| -}
|
| -
|
| -.label {
|
| - cursor: text;
|
| -}
|
| -
|
| -.label:hover {
|
| - background: #ffcc66;
|
| -}
|
| -
|
| -section h1 {
|
| - text-align: center;
|
| - font-size: 1em;
|
| -}
|
| -
|
| -section .tooltip {
|
| - position: absolute;
|
| - text-align: center;
|
| - background: #ffcc66;
|
| - border-radius: 5px;
|
| - padding: 0px 5px;
|
| -}
|
| -
|
| -body {
|
| - padding: 0px;
|
| - margin: 0px;
|
| - font-family: sans-serif;
|
| -}
|
| -
|
| -table {
|
| - background: white;
|
| - width: 100%;
|
| -}
|
| -
|
| -table, td, th {
|
| - border-collapse: collapse;
|
| - padding: 5px;
|
| - white-space: nowrap;
|
| -}
|
| -
|
| -.highlight:hover {
|
| - color: #202020;
|
| - background: #e0e0e0;
|
| -}
|
| -
|
| -.nestedRow {
|
| - background: #f8f8f8;
|
| -}
|
| -
|
| -.importantNestedRow {
|
| - background: #e0e0e0;
|
| - font-weight: bold;
|
| -}
|
| -
|
| -table td {
|
| - position: relative;
|
| -}
|
| -
|
| -th, td {
|
| - cursor: pointer;
|
| - cursor: hand;
|
| -}
|
| -
|
| -th {
|
| - background: #e6eeee;
|
| - background: linear-gradient(rgb(244, 244, 244), rgb(217, 217, 217));
|
| - border: 1px solid #ccc;
|
| -}
|
| -
|
| -th.sortUp:after {
|
| - content: ' \25BE';
|
| -}
|
| -
|
| -th.sortDown:after {
|
| - content: ' \25B4';
|
| -}
|
| -
|
| -td.comparison, td.result {
|
| - text-align: right;
|
| -}
|
| -
|
| -td.better {
|
| - color: #6c6;
|
| -}
|
| -
|
| -td.fadeOut {
|
| - opacity: 0.5;
|
| -}
|
| -
|
| -td.unknown {
|
| - color: #ccc;
|
| -}
|
| -
|
| -td.worse {
|
| - color: #c66;
|
| -}
|
| -
|
| -td.reference {
|
| - font-style: italic;
|
| - font-weight: bold;
|
| - color: #444;
|
| -}
|
| -
|
| -td.missing {
|
| - color: #ccc;
|
| - text-align: center;
|
| -}
|
| -
|
| -td.missingReference {
|
| - color: #ccc;
|
| - text-align: center;
|
| - font-style: italic;
|
| -}
|
| -
|
| -.checkbox {
|
| - display: inline-block;
|
| - background: #eee;
|
| - background: linear-gradient(rgb(220, 220, 220), rgb(200, 200, 200));
|
| - border: inset 1px #ddd;
|
| - border-radius: 5px;
|
| - margin: 10px;
|
| - font-size: small;
|
| - cursor: pointer;
|
| - cursor: hand;
|
| - -webkit-user-select: none;
|
| - font-weight: bold;
|
| -}
|
| -
|
| -.checkbox span {
|
| - display: inline-block;
|
| - line-height: 100%;
|
| - padding: 5px 8px;
|
| - border: outset 1px transparent;
|
| -}
|
| -
|
| -.checkbox .checked {
|
| - background: #e6eeee;
|
| - background: linear-gradient(rgb(255, 255, 255), rgb(235, 235, 235));
|
| - border: outset 1px #eee;
|
| - border-radius: 5px;
|
| -}
|
| -
|
| -.openAllButton {
|
| - display: inline-block;
|
| - colour: #6c6
|
| - background: #eee;
|
| - background: linear-gradient(rgb(220, 220, 220), rgb(255, 255, 255));
|
| - border: inset 1px #ddd;
|
| - border-radius: 5px;
|
| - float: left;
|
| - font-size: small;
|
| - -webkit-user-select: none;
|
| - font-weight: bold;
|
| - padding: 1px 4px;
|
| -}
|
| -
|
| -.openAllButton:hover {
|
| - background: #60f060;
|
| -}
|
| -
|
| -.closeAllButton {
|
| - display: inline-block;
|
| - colour: #c66
|
| - background: #eee;
|
| - background: linear-gradient(rgb(220, 220, 220),rgb(255, 255, 255));
|
| - border: inset 1px #ddd;
|
| - border-radius: 5px;
|
| - float: left;
|
| - font-size: small;
|
| - -webkit-user-select: none;
|
| - font-weight: bold;
|
| - padding: 1px 4px;
|
| -}
|
| -
|
| -.closeAllButton:hover {
|
| - background: #f04040;
|
| -}
|
| -
|
| -</style>
|
| -</head>
|
| -<body onload="init()">
|
| -<div style="padding: 0 10px; white-space: nowrap;">
|
| -Result <span id="time-memory" class="checkbox"></span>
|
| -Reference <span id="reference" class="checkbox"></span>
|
| -Style <span id="scatter-line" class="checkbox"><span class="checked">Scatter</span><span>Line</span></span>
|
| -<span class="checkbox"><span class="checked" id="undelete">Undelete</span></span><br>
|
| -Run your test with --reset-results to clear all runs
|
| -</div>
|
| -<table id="container"></table>
|
| -<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
|
| -<script>
|
| -%plugins%
|
| -</script>
|
| -<script>
|
| -
|
| -var EXPANDED = true;
|
| -var COLLAPSED = false;
|
| -var SMALLEST_PERCENT_DISPLAYED = 0.01;
|
| -var INVISIBLE = false;
|
| -var VISIBLE = true;
|
| -var COMPARISON_SUFFIX = '_compare';
|
| -var SORT_DOWN_CLASS = 'sortDown';
|
| -var SORT_UP_CLASS = 'sortUp';
|
| -var BETTER_CLASS = 'better';
|
| -var WORSE_CLASS = 'worse';
|
| -var UNKNOWN_CLASS = 'unknown'
|
| -// px Indentation for graphs
|
| -var GRAPH_INDENT = 64;
|
| -var PADDING_UNDER_GRAPH = 5;
|
| -// px Indentation for nested children left-margins
|
| -var INDENTATION = 40;
|
| -
|
| -function TestResult(metric, values, associatedRun, std, degreesOfFreedom) {
|
| - if (values) {
|
| - if (values[0] instanceof Array) {
|
| - var flattenedValues = [];
|
| - for (var i = 0; i < values.length; i++)
|
| - flattenedValues = flattenedValues.concat(values[i]);
|
| - values = flattenedValues;
|
| - }
|
| -
|
| - if (jQuery.type(values[0]) === 'string') {
|
| - try {
|
| - var current = JSON.parse(values[0]);
|
| - if (current.params.type === 'HISTOGRAM') {
|
| - this.histogramValues = current;
|
| - // Histogram results have no values (per se). Instead we calculate
|
| - // the values from the histogram bins.
|
| - var values = [];
|
| - var buckets = current.buckets
|
| - for (var i = 0; i < buckets.length; i++) {
|
| - var bucket = buckets[i];
|
| - var bucket_mean = (bucket.high + bucket.low) / 2;
|
| - for (var b = 0; b < bucket.count; b++) {
|
| - values.push(bucket_mean);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - catch (e) {
|
| - console.error(e, e.stack);
|
| - }
|
| - }
|
| - } else {
|
| - values = [];
|
| - }
|
| -
|
| - this.test = function() { return metric; }
|
| - this.values = function() { return values.map(function(value) { return metric.scalingFactor() * value; }); }
|
| - this.unscaledMean = function() { return Statistics.sum(values) / values.length; }
|
| - this.mean = function() { return metric.scalingFactor() * this.unscaledMean(); }
|
| - this.min = function() { return metric.scalingFactor() * Statistics.min(values); }
|
| - this.max = function() { return metric.scalingFactor() * Statistics.max(values); }
|
| - this.confidenceIntervalDelta = function() {
|
| - if (std !== undefined) {
|
| - return metric.scalingFactor() * Statistics.confidenceIntervalDeltaFromStd(0.95, values.length,
|
| - std, degreesOfFreedom);
|
| - }
|
| - return metric.scalingFactor() * Statistics.confidenceIntervalDelta(0.95, values.length,
|
| - Statistics.sum(values), Statistics.squareSum(values));
|
| - }
|
| - this.confidenceIntervalDeltaRatio = function() { return this.confidenceIntervalDelta() / this.mean(); }
|
| - this.percentDifference = function(other) {
|
| - if (other === undefined) {
|
| - return undefined;
|
| - }
|
| - return (other.unscaledMean() - this.unscaledMean()) / this.unscaledMean();
|
| - }
|
| - this.isStatisticallySignificant = function(other) {
|
| - if (other === undefined) {
|
| - return false;
|
| - }
|
| - var diff = Math.abs(other.mean() - this.mean());
|
| - return diff > this.confidenceIntervalDelta() && diff > other.confidenceIntervalDelta();
|
| - }
|
| - this.run = function() { return associatedRun; }
|
| -}
|
| -
|
| -function TestRun(entry) {
|
| - this.id = function() { return entry['buildTime'].replace(/[:.-]/g,''); }
|
| - this.label = function() {
|
| - if (labelKey in localStorage)
|
| - return localStorage[labelKey];
|
| - return entry['label'];
|
| - }
|
| - this.setLabel = function(label) { localStorage[labelKey] = label; }
|
| - this.isHidden = function() { return localStorage[hiddenKey]; }
|
| - this.hide = function() { localStorage[hiddenKey] = true; }
|
| - this.show = function() { localStorage.removeItem(hiddenKey); }
|
| - this.description = function() {
|
| - return new Date(entry['buildTime']).toLocaleString() + '\n' + entry['platform'] + ' ' + this.label();
|
| - }
|
| -
|
| - var labelKey = 'telemetry_label_' + this.id();
|
| - var hiddenKey = 'telemetry_hide_' + this.id();
|
| -}
|
| -
|
| -function PerfTestMetric(name, metric, unit, isImportant) {
|
| - var testResults = [];
|
| - var cachedUnit = null;
|
| - var cachedScalingFactor = null;
|
| -
|
| - // We can't do this in TestResult because all results for each test need to share the same unit and the same scaling factor.
|
| - function computeScalingFactorIfNeeded() {
|
| - // FIXME: We shouldn't be adjusting units on every test result.
|
| - // We can only do this on the first test.
|
| - if (!testResults.length || cachedUnit)
|
| - return;
|
| -
|
| - var mean = testResults[0].unscaledMean(); // FIXME: We should look at all values.
|
| - var kilo = unit == 'bytes' ? 1024 : 1000;
|
| - if (mean > 10 * kilo * kilo && unit != 'ms') {
|
| - cachedScalingFactor = 1 / kilo / kilo;
|
| - cachedUnit = 'M ' + unit;
|
| - } else if (mean > 10 * kilo) {
|
| - cachedScalingFactor = 1 / kilo;
|
| - cachedUnit = unit == 'ms' ? 's' : ('K ' + unit);
|
| - } else {
|
| - cachedScalingFactor = 1;
|
| - cachedUnit = unit;
|
| - }
|
| - }
|
| -
|
| - this.name = function() { return name + ':' + metric; }
|
| - this.isImportant = isImportant;
|
| - this.isMemoryTest = function() {
|
| - return (unit == 'kb' ||
|
| - unit == 'KB' ||
|
| - unit == 'MB' ||
|
| - unit == 'bytes' ||
|
| - unit == 'count' ||
|
| - !metric.indexOf('V8.'));
|
| - }
|
| - this.addResult = function(newResult) {
|
| - testResults.push(newResult);
|
| - cachedUnit = null;
|
| - cachedScalingFactor = null;
|
| - }
|
| - this.results = function() { return testResults; }
|
| - this.scalingFactor = function() {
|
| - computeScalingFactorIfNeeded();
|
| - return cachedScalingFactor;
|
| - }
|
| - this.unit = function() {
|
| - computeScalingFactorIfNeeded();
|
| - return cachedUnit;
|
| - }
|
| - this.biggerIsBetter = function() {
|
| - if (window.unitToBiggerIsBetter == undefined) {
|
| - window.unitToBiggerIsBetter = {};
|
| - var units = JSON.parse(document.getElementById('units-json').textContent);
|
| - for (var u in units) {
|
| - if (units[u].improvement_direction == 'up') {
|
| - window.unitToBiggerIsBetter[u] = true;
|
| - }
|
| - }
|
| - }
|
| - return window.unitToBiggerIsBetter[unit];
|
| - }
|
| -}
|
| -
|
| -function UndeleteManager() {
|
| - var key = 'telemetry_undeleteIds'
|
| - var undeleteIds = localStorage[key];
|
| - if (undeleteIds) {
|
| - undeleteIds = JSON.parse(undeleteIds);
|
| - } else {
|
| - undeleteIds = [];
|
| - }
|
| -
|
| - this.ondelete = function(id) {
|
| - undeleteIds.push(id);
|
| - localStorage[key] = JSON.stringify(undeleteIds);
|
| - }
|
| - this.undeleteMostRecent = function() {
|
| - if (!this.mostRecentlyDeletedId())
|
| - return;
|
| - undeleteIds.pop();
|
| - localStorage[key] = JSON.stringify(undeleteIds);
|
| - }
|
| - this.mostRecentlyDeletedId = function() {
|
| - if (!undeleteIds.length)
|
| - return undefined;
|
| - return undeleteIds[undeleteIds.length-1];
|
| - }
|
| -}
|
| -var undeleteManager = new UndeleteManager();
|
| -
|
| -var plotColor = 'rgb(230,50,50)';
|
| -var subpointsPlotOptions = {
|
| - lines: {show:true, lineWidth: 0},
|
| - color: plotColor,
|
| - points: {show: true, radius: 1},
|
| - bars: {show: false}};
|
| -
|
| -var mainPlotOptions = {
|
| - xaxis: {
|
| - min: -0.5,
|
| - tickSize: 1,
|
| - },
|
| - crosshair: { mode: 'y' },
|
| - series: { shadowSize: 0 },
|
| - bars: {show: true, align: 'center', barWidth: 0.5},
|
| - lines: { show: false },
|
| - points: { show: true },
|
| - grid: {
|
| - borderWidth: 1,
|
| - borderColor: '#ccc',
|
| - backgroundColor: '#fff',
|
| - hoverable: true,
|
| - autoHighlight: false,
|
| - }
|
| -};
|
| -
|
| -var linePlotOptions = {
|
| - yaxis: { show: false },
|
| - xaxis: { show: false },
|
| - lines: { show: true },
|
| - grid: { borderWidth: 1, borderColor: '#ccc' },
|
| - colors: [ plotColor ]
|
| -};
|
| -
|
| -var largeLinePlotOptions = {
|
| - xaxis: {
|
| - show: true,
|
| - tickDecimals: 0,
|
| - },
|
| - lines: { show: true },
|
| - grid: { borderWidth: 1, borderColor: '#ccc' },
|
| - colors: [ plotColor ]
|
| -};
|
| -
|
| -var histogramPlotOptions = {
|
| - bars: {show: true, fill: 1}
|
| -};
|
| -
|
| -function createPlot(container, test, useLargeLinePlots) {
|
| - if (test.results()[0].histogramValues) {
|
| - var section = $('<section><div class="histogram-plots"></div>'
|
| - + '<div class="histogram-plot-labels"></div>'
|
| - + '<span class="tooltip"></span></section>');
|
| - $(container).append(section);
|
| - attachHistogramPlots(test, section.children('.histogram-plots'));
|
| - }
|
| - else if (useLargeLinePlots) {
|
| - var section = $('<section><div class="large-line-plots"></div>'
|
| - + '<div class="large-line-plot-labels"></div>'
|
| - + '<span class="tooltip"></span></section>');
|
| - $(container).append(section);
|
| - attachLinePlots(test, section.children('.large-line-plots'), useLargeLinePlots);
|
| - attachLinePlotLabels(test, section.children('.large-line-plot-labels'));
|
| - } else {
|
| - var section = $('<section><div class="plot"></div><div class="line-plots"></div>'
|
| - + '<span class="tooltip"></span></section>');
|
| - section.children('.plot').css({'width': (100 * test.results().length + 25) + 'px', 'height': '300px'});
|
| - $(container).append(section);
|
| -
|
| - var plotContainer = section.children('.plot');
|
| - var minIsZero = true;
|
| - attachPlot(test, plotContainer, minIsZero);
|
| -
|
| - attachLinePlots(test, section.children('.line-plots'), useLargeLinePlots);
|
| -
|
| - var tooltip = section.children('.tooltip');
|
| - plotContainer.bind('plothover', function(event, position, item) {
|
| - if (item) {
|
| - var postfix = item.series.id ? ' (' + item.series.id + ')' : '';
|
| - tooltip.html(item.datapoint[1].toPrecision(4) + postfix);
|
| - var sectionOffset = $(section).offset();
|
| - tooltip.css({left: item.pageX - sectionOffset.left - tooltip.outerWidth() / 2, top: item.pageY - sectionOffset.top + 10});
|
| - tooltip.fadeIn(200);
|
| - } else
|
| - tooltip.hide();
|
| - });
|
| - plotContainer.mouseout(function() {
|
| - tooltip.hide();
|
| - });
|
| - plotContainer.click(function(event) {
|
| - event.preventDefault();
|
| - minIsZero = !minIsZero;
|
| - attachPlot(test, plotContainer, minIsZero);
|
| - });
|
| - }
|
| - return section;
|
| -}
|
| -
|
| -function attachLinePlots(test, container, useLargeLinePlots) {
|
| - var results = test.results();
|
| - var attachedPlot = false;
|
| -
|
| - if (useLargeLinePlots) {
|
| - var maximum = 0;
|
| - for (var i = 0; i < results.length; i++) {
|
| - var values = results[i].values();
|
| - if (!values)
|
| - continue;
|
| - var local_max = Math.max.apply(Math, values);
|
| - if (local_max > maximum)
|
| - maximum = local_max;
|
| - }
|
| - }
|
| -
|
| - for (var i = 0; i < results.length; i++) {
|
| - container.append('<div></div>');
|
| - var values = results[i].values();
|
| - if (!values)
|
| - continue;
|
| - attachedPlot = true;
|
| -
|
| - if (useLargeLinePlots) {
|
| - var options = $.extend(true, {}, largeLinePlotOptions,
|
| - {yaxis: {min: 0.0, max: maximum},
|
| - xaxis: {min: 0.0, max: values.length - 1},
|
| - points: {show: (values.length < 2) ? true : false}});
|
| - } else {
|
| - var options = $.extend(true, {}, linePlotOptions,
|
| - {yaxis: {min: Math.min.apply(Math, values) * 0.9, max: Math.max.apply(Math, values) * 1.1},
|
| - xaxis: {min: -0.5, max: values.length - 0.5},
|
| - points: {show: (values.length < 2) ? true : false}});
|
| - }
|
| - $.plot(container.children().last(), [values.map(function(value, index) { return [index, value]; })], options);
|
| - }
|
| - if (!attachedPlot)
|
| - container.children().remove();
|
| -}
|
| -
|
| -function attachHistogramPlots(test, container) {
|
| - var results = test.results();
|
| - var attachedPlot = false;
|
| -
|
| - for (var i = 0; i < results.length; i++) {
|
| - container.append('<div></div>');
|
| - var histogram = results[i].histogramValues
|
| - if (!histogram)
|
| - continue;
|
| - attachedPlot = true;
|
| -
|
| - var buckets = histogram.buckets
|
| - var bucket;
|
| - var max_count = 0;
|
| - for (var j = 0; j < buckets.length; j++) {
|
| - bucket = buckets[j];
|
| - max_count = Math.max(max_count, bucket.count);
|
| - }
|
| - var xmax = bucket.high * 1.1;
|
| - var ymax = max_count * 1.1;
|
| -
|
| - var options = $.extend(true, {}, histogramPlotOptions,
|
| - {yaxis: {min: 0.0, max: ymax},
|
| - xaxis: {min: histogram.params.min, max: xmax}});
|
| - var plot = $.plot(container.children().last(), [[]], options);
|
| - // Flot only supports fixed with bars and our histogram's buckets are
|
| - // variable width, so we need to do our own bar drawing.
|
| - var ctx = plot.getCanvas().getContext("2d");
|
| - ctx.lineWidth="1";
|
| - ctx.fillStyle = "rgba(255, 0, 0, 0.2)";
|
| - ctx.strokeStyle="red";
|
| - for (var j = 0; j < buckets.length; j++) {
|
| - bucket = buckets[j];
|
| - var bl = plot.pointOffset({ x: bucket.low, y: 0});
|
| - var tr = plot.pointOffset({ x: bucket.high, y: bucket.count});
|
| - ctx.fillRect(bl.left, bl.top, tr.left - bl.left, tr.top - bl.top);
|
| - ctx.strokeRect(bl.left, bl.top, tr.left - bl.left, tr.top - bl.top);
|
| - }
|
| - }
|
| - if (!attachedPlot)
|
| - container.children().remove();
|
| -}
|
| -
|
| -function attachLinePlotLabels(test, container) {
|
| - var results = test.results();
|
| - var attachedPlot = false;
|
| - for (var i = 0; i < results.length; i++) {
|
| - container.append('<div>' + results[i].run().label() + '</div>');
|
| - }
|
| -}
|
| -
|
| -function attachPlot(test, plotContainer, minIsZero) {
|
| - var results = test.results();
|
| -
|
| - var values = results.reduce(function(values, result, index) {
|
| - var newValues = result.values();
|
| - return newValues ? values.concat(newValues.map(function(value) { return [index, value]; })) : values;
|
| - }, []);
|
| -
|
| - var plotData = [$.extend(true, {}, subpointsPlotOptions, {data: values})];
|
| - plotData.push({id: 'μ', data: results.map(function(result, index) { return [index, result.mean()]; }), color: plotColor});
|
| -
|
| - var overallMax = Statistics.max(results.map(function(result, index) { return result.max(); }));
|
| - var overallMin = Statistics.min(results.map(function(result, index) { return result.min(); }));
|
| - var margin = (overallMax - overallMin) * 0.1;
|
| - var currentPlotOptions = $.extend(true, {}, mainPlotOptions, {yaxis: {
|
| - min: minIsZero ? 0 : overallMin - margin,
|
| - max: minIsZero ? overallMax * 1.1 : overallMax + margin}});
|
| -
|
| - currentPlotOptions.xaxis.max = results.length - 0.5;
|
| - currentPlotOptions.xaxis.ticks = results.map(function(result, index) { return [index, result.run().label()]; });
|
| -
|
| - $.plot(plotContainer, plotData, currentPlotOptions);
|
| -}
|
| -
|
| -function toFixedWidthPrecision(value) {
|
| - var decimal = value.toFixed(2);
|
| - return decimal;
|
| -}
|
| -
|
| -function formatPercentage(fraction) {
|
| - var percentage = fraction * 100;
|
| - return (fraction * 100).toFixed(2) + '%';
|
| -}
|
| -
|
| -function setUpSortClicks(runs)
|
| -{
|
| - $('#nameColumn').click(sortByName);
|
| -
|
| - $('#unitColumn').click(sortByUnit);
|
| -
|
| - runs.forEach(function(run) {
|
| - $('#' + run.id()).click(sortByResult);
|
| - $('#' + run.id() + COMPARISON_SUFFIX).click(sortByReference);
|
| - });
|
| -}
|
| -
|
| -function TestTypeSelector(tests) {
|
| - this.recognizers = {
|
| - 'Time': function(test) { return test.isMemoryTest(); },
|
| - 'Memory': function(test) { return !test.isMemoryTest(); }
|
| - };
|
| - this.testTypeNames = this.generateUsedTestTypeNames(tests);
|
| - // Default to selecting the first test-type name in the list.
|
| - this.testTypeName = this.testTypeNames[0];
|
| -}
|
| -
|
| -TestTypeSelector.prototype = {
|
| - set testTypeName(testTypeName) {
|
| - this._testTypeName = testTypeName;
|
| - this.shouldShowTest = this.recognizers[testTypeName];
|
| - },
|
| -
|
| - generateUsedTestTypeNames: function(allTests) {
|
| - var testTypeNames = [];
|
| -
|
| - for (var recognizedTestName in this.recognizers) {
|
| - var recognizes = this.recognizers[recognizedTestName];
|
| - for (var testName in allTests) {
|
| - var test = allTests[testName];
|
| - if (recognizes(test)) {
|
| - testTypeNames.push(recognizedTestName);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (testTypeNames.length === 0) {
|
| - // No test types we recognize, add 'No Results' with a dummy recognizer.
|
| - var noResults = 'No Results';
|
| - this.recognizers[noResults] = function() { return false; };
|
| - testTypeNames.push(noResults);
|
| - } else if (testTypeNames.length > 1) {
|
| - // We have more than one test type, so add 'All' with a recognizer that always succeeds.
|
| - var allResults = 'All';
|
| - this.recognizers[allResults] = function() { return true; };
|
| - testTypeNames.push(allResults);
|
| - }
|
| -
|
| - return testTypeNames;
|
| - },
|
| -
|
| - buildButtonHTMLForUsedTestTypes: function() {
|
| - var selectedTestTypeName = this._testTypeName;
|
| - // Build spans for all recognised test names with the selected test highlighted.
|
| - return this.testTypeNames.map(function(testTypeName) {
|
| - var classAttribute = testTypeName === selectedTestTypeName ? ' class=checked' : '';
|
| - return '<span' + classAttribute + '>' + testTypeName + '</span>';
|
| - }).join('');
|
| - }
|
| -};
|
| -
|
| -var topLevelRows;
|
| -var allTableRows;
|
| -
|
| -function displayTable(tests, runs, testTypeSelector, referenceIndex, useLargeLinePlots) {
|
| - var resultHeaders = runs.map(function(run, index) {
|
| - var header = '<th id="' + run.id() + '" ' +
|
| - 'colspan=2 ' +
|
| - 'title="' + run.description() + '">' +
|
| - '<span class="label" ' +
|
| - 'title="Edit run label">' +
|
| - run.label() +
|
| - '</span>' +
|
| - '<div class="closeButton" ' +
|
| - 'title="Delete run">' +
|
| - '×' +
|
| - '</div>' +
|
| - '</th>';
|
| - if (index !== referenceIndex) {
|
| - header += '<th id="' + run.id() + COMPARISON_SUFFIX + '" ' +
|
| - 'title="Sort by better/worse">' +
|
| - 'Δ' +
|
| - '</th>';
|
| - }
|
| - return header;
|
| - });
|
| -
|
| - resultHeaders = resultHeaders.join('');
|
| -
|
| - htmlString = '<thead>' +
|
| - '<tr>' +
|
| - '<th id="nameColumn">' +
|
| - '<div class="openAllButton" ' +
|
| - 'title="Open all rows or graphs">' +
|
| - 'Open All' +
|
| - '</div>' +
|
| - '<div class="closeAllButton" ' +
|
| - 'title="Close all rows">' +
|
| - 'Close All' +
|
| - '</div>' +
|
| - 'Test' +
|
| - '</th>' +
|
| - '<th id="unitColumn">' +
|
| - 'Unit' +
|
| - '</th>' +
|
| - resultHeaders +
|
| - '</tr>' +
|
| - '</head>' +
|
| - '<tbody>' +
|
| - '</tbody>';
|
| -
|
| - $('#container').html(htmlString);
|
| -
|
| - var testNames = [];
|
| - for (testName in tests)
|
| - testNames.push(testName);
|
| -
|
| - allTableRows = [];
|
| - testNames.forEach(function(testName) {
|
| - var test = tests[testName];
|
| - if (testTypeSelector.shouldShowTest(test)) {
|
| - allTableRows.push(new TableRow(runs, test, referenceIndex, useLargeLinePlots));
|
| - }
|
| - });
|
| -
|
| - // Build a list of top level rows with attached children
|
| - topLevelRows = [];
|
| - allTableRows.forEach(function(row) {
|
| - // Add us to top level if we are a top-level row...
|
| - if (row.hasNoURL) {
|
| - topLevelRows.push(row);
|
| - // Add a duplicate child row that holds the graph for the parent
|
| - var graphHolder = new TableRow(runs, row.test, referenceIndex, useLargeLinePlots);
|
| - graphHolder.isImportant = true;
|
| - graphHolder.URL = 'Summary';
|
| - graphHolder.hideRowData();
|
| - allTableRows.push(graphHolder);
|
| - row.addNestedChild(graphHolder);
|
| - return;
|
| - }
|
| -
|
| - // ...or add us to our parent if we have one ...
|
| - for (var i = 0; i < allTableRows.length; i++) {
|
| - if (allTableRows[i].isParentOf(row)) {
|
| - allTableRows[i].addNestedChild(row);
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // ...otherwise this result is orphaned, display it at top level with a graph
|
| - row.hasGraph = true;
|
| - topLevelRows.push(row);
|
| - });
|
| -
|
| - buildTable(topLevelRows);
|
| -
|
| - $('.closeButton').click(function(event) {
|
| - for (var i = 0; i < runs.length; i++) {
|
| - if (runs[i].id() == event.target.parentNode.id) {
|
| - runs[i].hide();
|
| - undeleteManager.ondelete(runs[i].id());
|
| - location.reload();
|
| - break;
|
| - }
|
| - }
|
| - event.stopPropagation();
|
| - });
|
| -
|
| - $('.closeAllButton').click(function(event) {
|
| - for (var i = 0; i < allTableRows.length; i++) {
|
| - allTableRows[i].closeRow();
|
| - }
|
| - event.stopPropagation();
|
| - });
|
| -
|
| - $('.openAllButton').click(function(event) {
|
| - for (var i = 0; i < topLevelRows.length; i++) {
|
| - topLevelRows[i].openRow();
|
| - }
|
| - event.stopPropagation();
|
| - });
|
| -
|
| - setUpSortClicks(runs);
|
| -
|
| - $('.label').click(function(event) {
|
| - for (var i = 0; i < runs.length; i++) {
|
| - if (runs[i].id() == event.target.parentNode.id) {
|
| - $(event.target).replaceWith('<input id="labelEditor" type="text" value="' + runs[i].label() + '">');
|
| - $('#labelEditor').focusout(function() {
|
| - runs[i].setLabel(this.value);
|
| - location.reload();
|
| - });
|
| - $('#labelEditor').keypress(function(event) {
|
| - if (event.which == 13) {
|
| - runs[i].setLabel(this.value);
|
| - location.reload();
|
| - }
|
| - });
|
| - $('#labelEditor').click(function(event) {
|
| - event.stopPropagation();
|
| - });
|
| - $('#labelEditor').mousedown(function(event) {
|
| - event.stopPropagation();
|
| - });
|
| - $('#labelEditor').select();
|
| - break;
|
| - }
|
| - }
|
| - event.stopPropagation();
|
| - });
|
| -}
|
| -
|
| -function validForSorting(row) {
|
| - return ($.type(row.sortValue) === 'string') || !isNaN(row.sortValue);
|
| -}
|
| -
|
| -var sortDirection = 1;
|
| -
|
| -function sortRows(rows) {
|
| - rows.sort(
|
| - function(rowA,rowB) {
|
| - if (validForSorting(rowA) !== validForSorting(rowB)) {
|
| - // Sort valid values upwards when compared to invalid
|
| - if (validForSorting(rowA)) {
|
| - return -1;
|
| - }
|
| - if (validForSorting(rowB)) {
|
| - return 1;
|
| - }
|
| - }
|
| -
|
| - // Some rows always sort to the top
|
| - if (rowA.isImportant) {
|
| - return -1;
|
| - }
|
| - if (rowB.isImportant) {
|
| - return 1;
|
| - }
|
| -
|
| - if (rowA.sortValue === rowB.sortValue) {
|
| - // Sort identical values by name to keep the sort stable,
|
| - // always keep name alphabetical (even if a & b sort values
|
| - // are invalid)
|
| - return rowA.test.name() > rowB.test.name() ? 1 : -1;
|
| - }
|
| -
|
| - return rowA.sortValue > rowB.sortValue ? sortDirection : -sortDirection;
|
| - } );
|
| -
|
| - // Sort the rows' children
|
| - rows.forEach(function(row) {
|
| - sortRows(row.children);
|
| - });
|
| -}
|
| -
|
| -function buildTable(rows) {
|
| - rows.forEach(function(row) {
|
| - row.removeFromPage();
|
| - });
|
| -
|
| - sortRows(rows);
|
| -
|
| - rows.forEach(function(row) {
|
| - row.addToPage();
|
| - });
|
| -}
|
| -
|
| -var activeSortHeaderElement = undefined;
|
| -var columnSortDirection = {};
|
| -
|
| -function determineColumnSortDirection(element) {
|
| - columnDirection = columnSortDirection[element.id];
|
| -
|
| - if (columnDirection === undefined) {
|
| - // First time we've sorted this row, default to down
|
| - columnSortDirection[element.id] = SORT_DOWN_CLASS;
|
| - } else if (element === activeSortHeaderElement) {
|
| - // Clicking on same header again, swap direction
|
| - columnSortDirection[element.id] = (columnDirection === SORT_UP_CLASS) ? SORT_DOWN_CLASS : SORT_UP_CLASS;
|
| - }
|
| -}
|
| -
|
| -function updateSortDirection(element) {
|
| - // Remove old header's sort arrow
|
| - if (activeSortHeaderElement !== undefined) {
|
| - activeSortHeaderElement.classList.remove(columnSortDirection[activeSortHeaderElement.id]);
|
| - }
|
| -
|
| - determineColumnSortDirection(element);
|
| -
|
| - sortDirection = (columnSortDirection[element.id] === SORT_UP_CLASS) ? 1 : -1;
|
| -
|
| - // Add new header's sort arrow
|
| - element.classList.add(columnSortDirection[element.id]);
|
| - activeSortHeaderElement = element;
|
| -}
|
| -
|
| -function sortByName(event) {
|
| - updateSortDirection(event.toElement);
|
| -
|
| - allTableRows.forEach(function(row) {
|
| - row.prepareToSortByName();
|
| - });
|
| -
|
| - buildTable(topLevelRows);
|
| -}
|
| -
|
| -function sortByUnit(event) {
|
| - updateSortDirection(event.toElement);
|
| -
|
| - allTableRows.forEach(function(row) {
|
| - row.prepareToSortByUnit();
|
| - });
|
| -
|
| - buildTable(topLevelRows);
|
| -}
|
| -
|
| -function sortByResult(event) {
|
| - updateSortDirection(event.toElement);
|
| -
|
| - var runId = event.target.id;
|
| -
|
| - allTableRows.forEach(function(row) {
|
| - row.prepareToSortByTestResults(runId);
|
| - });
|
| -
|
| - buildTable(topLevelRows);
|
| -}
|
| -
|
| -function sortByReference(event) {
|
| - updateSortDirection(event.toElement);
|
| -
|
| - // The element ID has _compare appended to allow us to set up a click event
|
| - // remove the _compare to return a useful Id
|
| - var runIdWithCompare = event.target.id;
|
| - var runId = runIdWithCompare.split('_')[0];
|
| -
|
| - allTableRows.forEach(function(row) {
|
| - row.prepareToSortRelativeToReference(runId);
|
| - });
|
| -
|
| - buildTable(topLevelRows);
|
| -}
|
| -
|
| -function linearRegression(points) {
|
| - // Implement http://www.easycalculation.com/statistics/learn-correlation.php.
|
| - // x = magnitude
|
| - // y = iterations
|
| - var sumX = 0;
|
| - var sumY = 0;
|
| - var sumXSquared = 0;
|
| - var sumYSquared = 0;
|
| - var sumXTimesY = 0;
|
| -
|
| - for (var i = 0; i < points.length; i++) {
|
| - var x = i;
|
| - var y = points[i];
|
| - sumX += x;
|
| - sumY += y;
|
| - sumXSquared += x * x;
|
| - sumYSquared += y * y;
|
| - sumXTimesY += x * y;
|
| - }
|
| -
|
| - var r = (points.length * sumXTimesY - sumX * sumY) /
|
| - Math.sqrt((points.length * sumXSquared - sumX * sumX) *
|
| - (points.length * sumYSquared - sumY * sumY));
|
| -
|
| - if (isNaN(r) || r == Math.Infinity)
|
| - r = 0;
|
| -
|
| - var slope = (points.length * sumXTimesY - sumX * sumY) / (points.length * sumXSquared - sumX * sumX);
|
| - var intercept = sumY / points.length - slope * sumX / points.length;
|
| - return {slope: slope, intercept: intercept, rSquared: r * r};
|
| -}
|
| -
|
| -var warningSign = '<svg viewBox="0 0 100 100" style="width: 18px; height: 18px; vertical-align: bottom;" version="1.1">'
|
| - + '<polygon fill="red" points="50,10 90,80 10,80 50,10" stroke="red" stroke-width="10" stroke-linejoin="round" />'
|
| - + '<polygon fill="white" points="47,30 48,29, 50, 28.7, 52,29 53,30 50,60" stroke="white" stroke-width="10" stroke-linejoin="round" />'
|
| - + '<circle cx="50" cy="73" r="6" fill="white" />'
|
| - + '</svg>';
|
| -
|
| -function TableRow(runs, test, referenceIndex, useLargeLinePlots) {
|
| - this.runs = runs;
|
| - this.test = test;
|
| - this.referenceIndex = referenceIndex;
|
| - this.useLargeLinePlots = useLargeLinePlots;
|
| - this.children = [];
|
| -
|
| - this.tableRow = $('<tr class="highlight">' +
|
| - '<td class="test collapsed" >' +
|
| - this.test.name() +
|
| - '</td>' +
|
| - '<td class="unit">' +
|
| - this.test.unit() +
|
| - '</td>' +
|
| - '</tr>');
|
| -
|
| - var runIndex = 0;
|
| - var results = this.test.results();
|
| - var referenceResult = undefined;
|
| -
|
| - this.resultIndexMap = {};
|
| - for (var i = 0; i < results.length; i++) {
|
| - while (this.runs[runIndex] !== results[i].run())
|
| - runIndex++;
|
| - if (runIndex === this.referenceIndex)
|
| - referenceResult = results[i];
|
| - this.resultIndexMap[runIndex] = i;
|
| - }
|
| - for (var i = 0; i < this.runs.length; i++) {
|
| - var resultIndex = this.resultIndexMap[i];
|
| - if (resultIndex === undefined)
|
| - this.tableRow.append(this.markupForMissingRun(i == this.referenceIndex));
|
| - else
|
| - this.tableRow.append(this.markupForRun(results[resultIndex], referenceResult));
|
| - }
|
| -
|
| - // Use the test name (without URL) to bind parents and their children
|
| - var nameAndURL = this.test.name().split('.');
|
| - var benchmarkName = nameAndURL.shift();
|
| - this.testName = nameAndURL.shift();
|
| - this.hasNoURL = (nameAndURL.length === 0);
|
| -
|
| - if (!this.hasNoURL) {
|
| - // Re-join the URL
|
| - this.URL = nameAndURL.join('.');
|
| - }
|
| -
|
| - this.isImportant = false;
|
| - this.hasGraph = false;
|
| - this.currentIndentationClass = ''
|
| - this.indentLevel = 0;
|
| - this.setRowNestedState(COLLAPSED);
|
| - this.setVisibility(VISIBLE);
|
| - this.prepareToSortByName();
|
| -}
|
| -
|
| -TableRow.prototype.hideRowData = function() {
|
| - data = this.tableRow.children('td');
|
| -
|
| - for (index in data) {
|
| - if (index > 0) {
|
| - // Blank out everything except the test name
|
| - data[index].innerHTML = '';
|
| - }
|
| - }
|
| -}
|
| -
|
| -TableRow.prototype.prepareToSortByTestResults = function(runId) {
|
| - var testResults = this.test.results();
|
| - // Find the column in this row that matches the runId and prepare to
|
| - // sort by the mean of that test.
|
| - for (index in testResults) {
|
| - sourceId = testResults[index].run().id();
|
| - if (runId === sourceId) {
|
| - this.sortValue = testResults[index].mean();
|
| - return;
|
| - }
|
| - }
|
| - // This row doesn't have any results for the passed runId
|
| - this.sortValue = undefined;
|
| -}
|
| -
|
| -TableRow.prototype.prepareToSortRelativeToReference = function(runId) {
|
| - var testResults = this.test.results();
|
| -
|
| - // Get index of test results that correspond to the reference column.
|
| - var remappedReferenceIndex = this.resultIndexMap[this.referenceIndex];
|
| -
|
| - if (remappedReferenceIndex === undefined) {
|
| - // This test has no results in the reference run.
|
| - this.sortValue = undefined;
|
| - return;
|
| - }
|
| -
|
| - otherResults = testResults[remappedReferenceIndex];
|
| -
|
| - // Find the column in this row that matches the runId and prepare to
|
| - // sort by the difference from the reference.
|
| - for (index in testResults) {
|
| - sourceId = testResults[index].run().id();
|
| - if (runId === sourceId) {
|
| - this.sortValue = testResults[index].percentDifference(otherResults);
|
| - if (this.test.biggerIsBetter()) {
|
| - // For this test bigger is not better
|
| - this.sortValue = -this.sortValue;
|
| - }
|
| - return;
|
| - }
|
| - }
|
| - // This row doesn't have any results for the passed runId
|
| - this.sortValue = undefined;
|
| -}
|
| -
|
| -TableRow.prototype.prepareToSortByUnit = function() {
|
| - this.sortValue = this.test.unit().toLowerCase();
|
| -}
|
| -
|
| -TableRow.prototype.prepareToSortByName = function() {
|
| - this.sortValue = this.test.name().toLowerCase();
|
| -}
|
| -
|
| -TableRow.prototype.isParentOf = function(row) {
|
| - return this.hasNoURL && (this.testName === row.testName);
|
| -}
|
| -
|
| -TableRow.prototype.addNestedChild = function(child) {
|
| - this.children.push(child);
|
| -
|
| - // Indent child one step in from parent
|
| - child.indentLevel = this.indentLevel + INDENTATION;
|
| - child.hasGraph = true;
|
| - // Start child off as hidden (i.e. collapsed inside parent)
|
| - child.setVisibility(INVISIBLE);
|
| - child.updateIndentation();
|
| - // Show URL in the title column
|
| - child.tableRow.children()[0].innerHTML = child.URL;
|
| - // Set up class to change background colour of nested rows
|
| - if (child.isImportant) {
|
| - child.tableRow.addClass('importantNestedRow');
|
| - } else {
|
| - child.tableRow.addClass('nestedRow');
|
| - }
|
| -}
|
| -
|
| -TableRow.prototype.setVisibility = function(visibility) {
|
| - this.visibility = visibility;
|
| - this.tableRow[0].style.display = (visibility === INVISIBLE) ? 'none' : '';
|
| -}
|
| -
|
| -TableRow.prototype.setRowNestedState = function(newState) {
|
| - this.rowState = newState;
|
| - this.updateIndentation();
|
| -}
|
| -
|
| -TableRow.prototype.updateIndentation = function() {
|
| - var element = this.tableRow.children('td').first();
|
| -
|
| - element.removeClass(this.currentIndentationClass);
|
| -
|
| - this.currentIndentationClass = (this.rowState === COLLAPSED) ? 'collapsed' : 'expanded';
|
| -
|
| - element[0].style.marginLeft = this.indentLevel.toString() + 'px';
|
| - element[0].style.float = 'left';
|
| -
|
| - element.addClass(this.currentIndentationClass);
|
| -}
|
| -
|
| -TableRow.prototype.addToPage = function() {
|
| - $('#container').children('tbody').last().append(this.tableRow);
|
| -
|
| - // Set up click callback
|
| - var owningObject = this;
|
| - this.tableRow.click(function(event) {
|
| - event.preventDefault();
|
| - owningObject.toggle();
|
| - });
|
| -
|
| - // Add children to the page too
|
| - this.children.forEach(function(child) {
|
| - child.addToPage();
|
| - });
|
| -}
|
| -
|
| -TableRow.prototype.removeFromPage = function() {
|
| - // Remove children
|
| - this.children.forEach(function(child) {
|
| - child.removeFromPage();
|
| - });
|
| - // Remove us
|
| - this.tableRow.remove();
|
| -}
|
| -
|
| -
|
| -TableRow.prototype.markupForRun = function(result, referenceResult) {
|
| - var comparisonCell = '';
|
| - var shouldCompare = result !== referenceResult;
|
| - if (shouldCompare) {
|
| - var comparisonText = '';
|
| - var className = '';
|
| -
|
| - if (referenceResult) {
|
| - var percentDifference = referenceResult.percentDifference(result);
|
| - if (isNaN(percentDifference)) {
|
| - comparisonText = 'Unknown';
|
| - className = UNKNOWN_CLASS;
|
| - } else if (Math.abs(percentDifference) < SMALLEST_PERCENT_DISPLAYED) {
|
| - comparisonText = 'Equal';
|
| - // Show equal values in green
|
| - className = BETTER_CLASS;
|
| - } else {
|
| - var better = this.test.biggerIsBetter() ? percentDifference > 0 : percentDifference < 0;
|
| - comparisonText = formatPercentage(Math.abs(percentDifference)) + (better ? ' Better' : ' Worse');
|
| - className = better ? BETTER_CLASS : WORSE_CLASS;
|
| - }
|
| -
|
| - if (!referenceResult.isStatisticallySignificant(result)) {
|
| - // Put result in brackets and fade if not statistically significant
|
| - className += ' fadeOut';
|
| - comparisonText = '(' + comparisonText + ')';
|
| - }
|
| - }
|
| - comparisonCell = '<td class="comparison ' + className + '">' + comparisonText + '</td>';
|
| - }
|
| -
|
| - var values = result.values();
|
| - var warning = '';
|
| - var regressionAnalysis = '';
|
| - if (result.histogramValues) {
|
| - // Don't calculate regression result for histograms.
|
| - } else if (values && values.length > 3) {
|
| - regressionResult = linearRegression(values);
|
| - regressionAnalysis = 'slope=' + toFixedWidthPrecision(regressionResult.slope)
|
| - + ', R^2=' + toFixedWidthPrecision(regressionResult.rSquared);
|
| - if (regressionResult.rSquared > 0.6 && Math.abs(regressionResult.slope) > 0.01) {
|
| - warning = ' <span class="regression-warning" title="Detected a time dependency with ' + regressionAnalysis + '">' + warningSign + ' </span>';
|
| - }
|
| - }
|
| -
|
| - var referenceClass = shouldCompare ? '' : 'reference';
|
| -
|
| - var statistics = 'σ=' + toFixedWidthPrecision(result.confidenceIntervalDelta()) + ', min=' + toFixedWidthPrecision(result.min())
|
| - + ', max=' + toFixedWidthPrecision(result.max()) + '\n' + regressionAnalysis;
|
| -
|
| - var confidence;
|
| - if (isNaN(result.confidenceIntervalDeltaRatio())) {
|
| - // Don't bother showing +- Nan as it is meaningless
|
| - confidence = '';
|
| - } else {
|
| - confidence = '± ' + formatPercentage(result.confidenceIntervalDeltaRatio());
|
| - }
|
| -
|
| - return '<td class="result ' + referenceClass + '" title="' + statistics + '">' + toFixedWidthPrecision(result.mean())
|
| - + '</td><td class="confidenceIntervalDelta ' + referenceClass + '" title="' + statistics + '">' + confidence + warning + '</td>' + comparisonCell;
|
| -}
|
| -
|
| -TableRow.prototype.markupForMissingRun = function(isReference) {
|
| - if (isReference) {
|
| - return '<td colspan=2 class="missingReference">Missing</td>';
|
| - }
|
| - return '<td colspan=3 class="missing">Missing</td>';
|
| -}
|
| -
|
| -TableRow.prototype.openRow = function() {
|
| - if (this.rowState === EXPANDED) {
|
| - // If we're already expanded, open our children instead
|
| - this.children.forEach(function(child) {
|
| - child.openRow();
|
| - });
|
| - return;
|
| - }
|
| -
|
| - this.setRowNestedState(EXPANDED);
|
| -
|
| - if (this.hasGraph) {
|
| - var firstCell = this.tableRow.children('td').first();
|
| - var plot = createPlot(firstCell, this.test, this.useLargeLinePlots);
|
| - plot.css({'position': 'absolute', 'z-index': 2});
|
| - var offset = this.tableRow.offset();
|
| - offset.left += GRAPH_INDENT;
|
| - offset.top += this.tableRow.outerHeight();
|
| - plot.offset(offset);
|
| - this.tableRow.children('td').css({'padding-bottom': plot.outerHeight() + PADDING_UNDER_GRAPH});
|
| - }
|
| -
|
| - this.children.forEach(function(child) {
|
| - child.setVisibility(VISIBLE);
|
| - });
|
| -
|
| - if (this.children.length === 1) {
|
| - // If we only have a single child...
|
| - var child = this.children[0];
|
| - if (child.isImportant) {
|
| - // ... and it is important (i.e. the summary row) just open it when
|
| - // parent is opened to save needless clicking
|
| - child.openRow();
|
| - }
|
| - }
|
| -}
|
| -
|
| -TableRow.prototype.closeRow = function() {
|
| - if (this.rowState === COLLAPSED) {
|
| - return;
|
| - }
|
| -
|
| - this.setRowNestedState(COLLAPSED);
|
| -
|
| - if (this.hasGraph) {
|
| - var firstCell = this.tableRow.children('td').first();
|
| - firstCell.children('section').remove();
|
| - this.tableRow.children('td').css({'padding-bottom': ''});
|
| - }
|
| -
|
| - this.children.forEach(function(child) {
|
| - // Make children invisible, but leave their collapsed status alone
|
| - child.setVisibility(INVISIBLE);
|
| - });
|
| -}
|
| -
|
| -TableRow.prototype.toggle = function() {
|
| - if (this.rowState === EXPANDED) {
|
| - this.closeRow();
|
| - } else {
|
| - this.openRow();
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -function init() {
|
| - var runs = [];
|
| - var metrics = {};
|
| - var deletedRunsById = {};
|
| - $.each(JSON.parse(document.getElementById('results-json').textContent), function(index, entry) {
|
| - var run = new TestRun(entry);
|
| - if (run.isHidden()) {
|
| - deletedRunsById[run.id()] = run;
|
| - return;
|
| - }
|
| -
|
| - runs.push(run);
|
| -
|
| - function addTests(tests) {
|
| - for (var testName in tests) {
|
| - var rawMetrics = tests[testName].metrics;
|
| -
|
| - for (var metricName in rawMetrics) {
|
| - var fullMetricName = testName + ':' + metricName;
|
| - var metric = metrics[fullMetricName];
|
| - if (!metric) {
|
| - metric = new PerfTestMetric(testName, metricName, rawMetrics[metricName].units, rawMetrics[metricName].important);
|
| - metrics[fullMetricName] = metric;
|
| - }
|
| - // std & degrees_of_freedom could be undefined
|
| - metric.addResult(
|
| - new TestResult(metric, rawMetrics[metricName].current,
|
| - run, rawMetrics[metricName]['std'], rawMetrics[metricName]['degrees_of_freedom']));
|
| - }
|
| - }
|
| - }
|
| -
|
| - addTests(entry.tests);
|
| - });
|
| -
|
| - var useLargeLinePlots = false;
|
| - var referenceIndex = 0;
|
| -
|
| - var testTypeSelector = new TestTypeSelector(metrics);
|
| - var buttonHTML = testTypeSelector.buildButtonHTMLForUsedTestTypes();
|
| - $('#time-memory').append(buttonHTML);
|
| -
|
| - $('#scatter-line').bind('change', function(event, checkedElement) {
|
| - useLargeLinePlots = checkedElement.textContent == 'Line';
|
| - displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
|
| - });
|
| -
|
| - runs.map(function(run, index) {
|
| - $('#reference').append('<span value="' + index + '"' + (index == referenceIndex ? ' class="checked"' : '') + ' title="' + run.description() + '">' + run.label() + '</span>');
|
| - })
|
| -
|
| - $('#time-memory').bind('change', function(event, checkedElement) {
|
| - testTypeSelector.testTypeName = checkedElement.textContent;
|
| - displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
|
| - });
|
| -
|
| - $('#reference').bind('change', function(event, checkedElement) {
|
| - referenceIndex = parseInt(checkedElement.getAttribute('value'));
|
| - displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
|
| - });
|
| -
|
| - displayTable(metrics, runs, testTypeSelector, referenceIndex, useLargeLinePlots);
|
| -
|
| - $('.checkbox').each(function(index, checkbox) {
|
| - $(checkbox).children('span').click(function(event) {
|
| - if ($(this).hasClass('checked'))
|
| - return;
|
| - $(checkbox).children('span').removeClass('checked');
|
| - $(this).addClass('checked');
|
| - $(checkbox).trigger('change', $(this));
|
| - });
|
| - });
|
| -
|
| - runToUndelete = deletedRunsById[undeleteManager.mostRecentlyDeletedId()];
|
| -
|
| - if (runToUndelete) {
|
| - $('#undelete').html('Undelete ' + runToUndelete.label());
|
| - $('#undelete').attr('title', runToUndelete.description());
|
| - $('#undelete').click(function(event) {
|
| - runToUndelete.show();
|
| - undeleteManager.undeleteMostRecent();
|
| - location.reload();
|
| - });
|
| - } else {
|
| - $('#undelete').hide();
|
| - }
|
| -}
|
| -
|
| -</script>
|
| -<script id="results-json" type="application/json">%json_results%</script>
|
| -<script id="units-json" type="application/json">%json_units%</script>
|
| -</body>
|
| -</html>
|
|
|