blob: 69ad6b80bc52a512e2c6063085d9f091f6dfe003 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>Telemetry Performance Test Results</title>
<style type="text/css">
section {
background: white;
padding: 10px;
position: relative;
}
.collapsed:before {
content: '\25B8\00A0';
}
.expanded:before {
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 {
background: #fff;
border: 1px solid black;
border-radius: 2px;
display: inline-block;
float: right;
line-height: 11px;
margin-top: 3px;
width: 11px;
}
.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;
}
tr.even {
background: #f6f6f6;
}
table td {
position: relative;
font-family: monospace;
}
th, td {
cursor: pointer;
cursor: hand;
}
th {
background: #e6eeee;
background: -webkit-gradient(linear, left top, left bottom, from(rgb(244, 244, 244)), to(rgb(217, 217, 217)));
border: 1px solid #ccc;
}
th:after {
content: ' \25B8';
}
th.headerSortUp:after {
content: ' \25BE';
}
th.headerSortDown:after {
content: ' \25B4';
}
td.comparison, td.result {
text-align: right;
}
td.better {
color: #6c6;
}
td.worse {
color: #c66;
}
td.missing {
color: #aaa;
text-align: center;
}
.checkbox {
display: inline-block;
background: #eee;
background: -webkit-gradient(linear, left bottom, left top, from(rgb(220, 220, 220)), to(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: -webkit-gradient(linear, left top, left bottom, from(rgb(255, 255, 255)), to(rgb(235, 235, 235)));
border: outset 1px #eee;
border-radius: 5px;
}
</style>
</head>
<body onload="init()">
<div style="padding: 0 10px; white-space: nowrap;">
Result <span id="time-memory" class="checkbox"><span class="checked">Time</span><span>Memory</span></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>
Run Telemetry 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>
function TestResult(metric, values, associatedRun) {
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) { /* ignore, assume not a JSON string */ }
}
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 () {
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) { return (other.unscaledMean() - this.unscaledMean()) / this.unscaledMean(); }
this.isStatisticallySignificant = function (other) {
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']; }
this.revision = function () { return entry['revision']; }
this.label = function () {
if (labelKey in localStorage)
return localStorage[labelKey];
if (entry['label'])
return entry['label'];
return 'r' + this.revision();
}
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() {
var label = this.label();
if (label != 'r' + this.revision())
label = ' ' + label;
else
label = '';
return new Date(entry['buildTime']).toLocaleString() + '\n' + entry['platform'] + ' r' + entry['revision'] + 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: '&mu;', 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 createTable(tests, runs, shouldIgnoreMemory, referenceIndex, useLargeLinePlots) {
$('#container').html('<thead><tr><th class="headerSortDown">Test</th><th>Unit</th>' + runs.map(function (run, index) {
return '<th colspan="' + (index == referenceIndex ? 2 : 3) + '" class="{sorter: \'comparison\'}" id="' + run.id() + '" title="' + run.description() + '"><span class="label" title="Edit run label">' + run.label() + '</span><div class="closeButton" title="Delete run">x</div></th>';
}).reduce(function (markup, cell) { return markup + cell; }, '') + '</tr></head><tbody></tbody>');
var testNames = [];
for (testName in tests)
testNames.push(testName);
window.isFirstImportantRow = true;
testNames.sort().map(function (testName) {
var test = tests[testName];
if (test.isMemoryTest() != shouldIgnoreMemory)
createTableRow(runs, test, referenceIndex, useLargeLinePlots);
});
$('.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();
});
$('.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();
});
$('#container').tablesorter({widgets: ['zebra']});
}
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 createTableRow(runs, test, referenceIndex, useLargeLinePlots) {
var tableRow = $('<tr><td class="test collapsed"' + (test.isImportant ? ' style="font-weight:bold"' : '') + '>' + test.name() + '</td><td class="unit">' + test.unit() + '</td></tr>');
function markupForRun(result, referenceResult) {
var comparisonCell = '';
var hiddenValue = '';
var shouldCompare = result !== referenceResult;
if (shouldCompare && referenceResult) {
var percentDifference = referenceResult.percentDifference(result);
var better = test.biggerIsBetter() ? percentDifference > 0 : percentDifference < 0;
var comparison = '';
var className = 'comparison';
if (referenceResult.isStatisticallySignificant(result)) {
comparison = formatPercentage(Math.abs(percentDifference)) + (better ? ' Better' : ' Worse&nbsp;');
className += better ? ' better' : ' worse';
}
hiddenValue = '<span style="display: none">|' + comparison + '</span>';
comparisonCell = '<td class="' + className + '">' + comparison + '</td>';
} else if (shouldCompare)
comparisonCell = '<td class="comparison"></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 statistics = '&sigma;=' + toFixedWidthPrecision(result.confidenceIntervalDelta()) + ', min=' + toFixedWidthPrecision(result.min())
+ ', max=' + toFixedWidthPrecision(result.max()) + '\n' + regressionAnalysis;
// Tablesorter doesn't know about the second cell so put the comparison in the invisible element.
return '<td class="result" title="' + statistics + '">' + toFixedWidthPrecision(result.mean()) + hiddenValue
+ '</td><td class="confidenceIntervalDelta" title="' + statistics + '">&plusmn; '
+ formatPercentage(result.confidenceIntervalDeltaRatio()) + warning + '</td>' + comparisonCell;
}
function markupForMissingRun(isReference) {
return '<td colspan="' + (isReference ? 2 : 3) + '" class="missing">Missing</td>';
}
var runIndex = 0;
var results = test.results();
var referenceResult = undefined;
var resultIndexMap = {};
for (var i = 0; i < results.length; i++) {
while (runs[runIndex] !== results[i].run())
runIndex++;
if (runIndex == referenceIndex)
referenceResult = results[i];
resultIndexMap[runIndex] = i;
}
for (var i = 0; i < runs.length; i++) {
var resultIndex = resultIndexMap[i];
if (resultIndex == undefined)
tableRow.append(markupForMissingRun(i == referenceIndex));
else
tableRow.append(markupForRun(results[resultIndex], referenceResult));
}
$('#container').children('tbody').last().append(tableRow);
function toggle() {
var firstCell = tableRow.children('td').first();
if (firstCell.children('section').length) {
firstCell.children('section').remove();
tableRow.children('td').css({'padding-bottom': ''});
tableRow.children('td').first().addClass('collapsed');
tableRow.children('td').first().removeClass('expanded');
} else {
var plot = createPlot(firstCell, test, useLargeLinePlots);
plot.css({'position': 'absolute', 'z-index': 2});
var offset = tableRow.offset();
offset.left += 1;
offset.top += tableRow.outerHeight();
plot.offset(offset);
tableRow.children('td').css({'padding-bottom': plot.outerHeight() + 5});
tableRow.children('td').first().removeClass('collapsed');
tableRow.children('td').first().addClass('expanded');
}
return false;
};
tableRow.click(function(event) {
if (event.target != tableRow[0] && event.target.parentNode != tableRow[0])
return;
event.preventDefault();
toggle();
});
if (test.isImportant && window.isFirstImportantRow) {
window.isFirstImportantRow = false;
toggle();
}
}
function init() {
$.tablesorter.addParser({
id: 'comparison',
is: function(s) {
return s.indexOf('|') >= 0;
},
format: function(s) {
var parsed = parseFloat(s.substring(s.indexOf('|') + 1));
return isNaN(parsed) ? 0 : parsed;
},
type: 'numeric',
});
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;
}
metric.addResult(new TestResult(metric, rawMetrics[metricName].current, run));
}
}
}
addTests(entry.tests);
});
var useLargeLinePlots = false;
var shouldIgnoreMemory= true;
var referenceIndex = 0;
createTable(metrics, runs, shouldIgnoreMemory, referenceIndex, useLargeLinePlots);
$('#time-memory').bind('change', function (event, checkedElement) {
shouldIgnoreMemory = checkedElement.textContent == 'Time';
createTable(metrics, runs, shouldIgnoreMemory, referenceIndex, useLargeLinePlots);
});
$('#scatter-line').bind('change', function (event, checkedElement) {
useLargeLinePlots = checkedElement.textContent == 'Line';
createTable(metrics, runs, shouldIgnoreMemory, referenceIndex, useLargeLinePlots);
});
runs.map(function (run, index) {
$('#reference').append('<span value="' + index + '"' + (index == referenceIndex ? ' class="checked"' : '') + ' title="' + run.description() + '">' + run.label() + '</span>');
})
$('#reference').bind('change', function (event, checkedElement) {
referenceIndex = parseInt(checkedElement.getAttribute('value'));
createTable(metrics, runs, shouldIgnoreMemory, 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));
});
});
if (undeleteManager.mostRecentlyDeletedId()) {
$('#undelete').html('Undelete ' + deletedRunsById[undeleteManager.mostRecentlyDeletedId()].label());
$('#undelete').attr('title', deletedRunsById[undeleteManager.mostRecentlyDeletedId()].description());
$('#undelete').click(function (event) {
deletedRunsById[undeleteManager.mostRecentlyDeletedId()].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>