1
0
forked from 0ad/0ad

Simulation replay profiling graph overhaul.

Support multiple graphs with different scales, units, rounding and
descriptions and use it for memory allocation and number of garbage
collections.
Have the X axis in number of turns (instead of that divided by 20).
Don't have the legend hide part of the graph.
Add references to that hardcoded 20.
Move graph JS to a separate JS file.
Use JSON instead of JS.
Add legal html structure.

Comments By: Vladislav in irc yesterday
This was SVN commit r21602.
This commit is contained in:
elexis 2018-03-22 13:53:04 +00:00
parent fcceffb3dd
commit ee9e677084
4 changed files with 177 additions and 65 deletions

View File

@ -42,6 +42,12 @@
#include <ctime>
#include <fstream>
/**
* Number of turns between two saved profiler snapshots.
* Keep in sync with source/tools/replayprofile/graph.js
*/
static const int PROFILE_TURN_INTERVAL = 20;
CReplayLogger::CReplayLogger(const ScriptInterface& scriptInterface) :
m_ScriptInterface(scriptInterface), m_Stream(NULL)
{
@ -234,13 +240,11 @@ void CReplayPlayer::Replay(bool serializationtest, int rejointestturn, bool oosl
g_Profiler.Frame();
if (turn % 20 == 0)
if (turn % PROFILE_TURN_INTERVAL == 0)
g_ProfileViewer.SaveToFile();
}
else
{
debug_printf("Unrecognised replay token %s\n", type.c_str());
}
}
}

View File

@ -33,7 +33,7 @@ while (<$f>) {
}
}
print "var graphData = [\n";
print "[\n";
my $n = 0;
for my $k (sort keys %s) {
print ",\n" if $n++;
@ -44,4 +44,4 @@ for my $k (sort keys %s) {
}
print "]}";
}
print "\n];\n";
print "\n]\n";

View File

@ -1,61 +1,28 @@
<!DOCTYPE html>
<script src="jquery.js"></script>
<script src="jquery.flot.js"></script>
<script src="jquery.flot.navigate.js"></script>
<script src="data.js"></script>
<script>
var newGraphData = [];
for (var i = 0; i < graphData.length; ++i) {
var d = graphData[i];
if (d.label == "allocated bytes" || d.label == "max nominal heap bytes" || d.label == "max JS_malloc bytes")
d.yaxis = 2;
// Uninteresting:
if (d.label == "max nominal heap bytes" || d.label == "max JS_malloc bytes" || d.label == "number of GCs" || d.label == "allocated bytes")
continue;
// Bogus data:
if (d.label == "unlogged")
continue;
newGraphData.push(d);
}
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css( {
position: 'absolute',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body");
}
$(function () {
$.plot($("#placeholder"), newGraphData, {
grid: { hoverable: true },
zoom: { interactive: true },
pan: { interactive: true },
legend: { position: "nw" }
});
$("#placeholder").bind("plothover", function (event, pos, item) {
$("#tooltip").remove();
if (item) {
var x = item.datapoint[0].toFixed(2);
var y = item.datapoint[1].toFixed(2);
showTooltip(item.pageX, item.pageY, item.series.label + " at " + Math.round(x) + ": " + y);
}
});
});
</script>
<div id="placeholder" style="width:1100px; height:600px;"></div>
<p>x axis: turn number / 20
<p>y axis (left): time per frame / msecs
<p>y axis (right): bytes
<p>Drag to pan, mouse-wheel to zoom
<html>
<head>
<meta charset="UTF-8"/>
<title>0 A.D. Simulation Profiling</title>
<link rel="stylesheet" type="text/css" href="graph.css">
<script src="jquery.js"></script>
<script src="jquery.flot.js"></script>
<script src="jquery.flot.navigate.js"></script>
<script src="graph.js"></script>
</head>
<body>
<div>
<label>Graph:
<select id="replayGraphSelection" size="1" onchange="showReplayData()">
<option value="time" selected="selected">Performance</option>
<option value="bytes">Memory</option>
<option value="garbageCollection">Garbage Collections</option>
</select>
</label>
</div>
<div id="replayGraphLegend" style="width:300px;float:left"></div>
<div>
<div id="replayGraph" style="width:1100px; height:600px; float:left"></div>
<div id="replayGraphDescription" style="float:top"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,141 @@
/**
* Contents of data.json
*/
var replayData;
/**
* Number of turns between two saved profiler snapshots.
* Keep in sync with Replay.cpp.
*/
var PROFILE_TURN_INTERVAL = 20;
/**
* These columns are never displayed.
*/
var filteredColumns = [
"sim update", // indistringuishable from Total
"unlogged" // Bogus
];
/**
* Determines how the the different graphs are formatted.
*/
var graphFormat = {
"time": {
"axisTitle": "Time per frame",
"unit": "milliseconds",
"digits": 2,
"scale": 1,
},
"bytes": {
"axisTitle": "Memory",
"unit": "Megabytes",
"digits": 2,
"scale": 1 / 1024 / 1024,
"isColumn": function(label) {
return label.indexOf("bytes") != -1;
}
},
"garbageCollection": {
"axisTitle": "Number of Garbace Collections",
"unit": "",
"scale": 1,
"digits": 0,
"isColumn": function(label) {
return label == "number of GCs";
}
}
};
function showReplayData()
{
var displayedColumn = $("#replayGraphSelection").val();
$.plot($("#replayGraph"), getReplayGraphData(displayedColumn), {
"grid": {
"hoverable": true
},
"zoom": {
"interactive": true
},
"pan": {
"interactive": true
},
"legend": {
"container": $("#replayGraphLegend")
}
});
$("#replayGraph").bind("plothover", function (event, pos, item) {
$("#tooltip").remove();
if (!item)
return;
showTooltip(
item.pageX,
item.pageY,
displayedColumn,
item.series.label,
item.datapoint[0],
item.datapoint[1].toFixed(graphFormat[displayedColumn].digits));
});
$("#replayGraphDescription").html(
"<p>X axis: Turn Number</p>" +
"<p>Y axis: " + graphFormat[displayedColumn].axisTitle +
(graphFormat[displayedColumn].unit ? " [" + graphFormat[displayedColumn].unit + "]" : "") + "</p>" +
"<p>Drag to pan, mouse-wheel to zoom</p>"
);
}
/**
* Filter the affected columns and apply the scaling and rounding.
*/
function getReplayGraphData(displayedColumn)
{
var replayGraphData = [];
for (var i = 0; i < replayData.length; ++i)
{
var label = replayData[i].label;
if (filteredColumns.indexOf(label) != -1 ||
(displayedColumn == "bytes") != graphFormat.bytes.isColumn(label) ||
(displayedColumn == "garbageCollection") != (graphFormat.garbageCollection.isColumn(label)))
continue
var data = [];
for (var j = 0; j < replayData[i].data.length; ++j)
data.push([
replayData[i].data[j][0] * PROFILE_TURN_INTERVAL,
replayData[i].data[j][1] * graphFormat[displayedColumn].scale
]);
replayGraphData.push({
"label": label,
"data": data
});
}
return replayGraphData;
}
function showTooltip(x, y, displayedColumn, label, turn, value)
{
$("body").append(
$('<div id="tooltip">' + label + " at turn " + turn + ": " + value + " " + graphFormat[displayedColumn].unit + "</div>").css({
"position": "absolute",
"top": y + 5,
"left": x + 5,
"border": "1px solid #fdd",
"padding": "2px",
"background-color": "#fee",
"opacity": 0.8
}));
}
$.getJSON("data.json", function(data) {
replayData = data;
showReplayData();
});