shake-0.2.8: html/shake.js
// first find which runs have built data in them, sorted
// built :: [Int]
var built = function(){
var seen = {};
for (var i = 0; i < shake.length; i++)
seen[shake[i].built] = true;
var seen2 = [];
for (var i in seen)
seen2.push(i);
return seen2.sort(function cmp(a,b){return a-b;});
}();
// what is the index of the last build run
// lastRun :: Int
var lastRun = built.length == 0 ? 0 : built[built.length-1];
function showTime(x){return x.toFixed(2) + "s";}
function showPerc(x){return (x*100).toFixed(2) + "%";}
function showShort(x){return x;}
function plural(n,not1,is1){
return n == 1
? (is1 == undefined ? "" : is1)
: (not1 == undefined ? "s" : not1);
}
function listEq(xs, ys)
{
if (xs.length != ys.length) return false;
for (var i = 0; i < xs.length; i++)
if (xs[i] != ys[i]) return false;
return true;
}
// Return the n top entries, grouping as necessary by words
// huffman :: Int -> [{sum :: Double, name :: String}] -> [{sum :: Double, count :: Int, name :: String}]
function huffman(resultSize, xs)
{
var val = {sum: 0, count: 0, children: {}};
for (var i = 0; i < xs.length; i++)
{
var val2 = val;
var ys = xs[i].name.split(' ');
for (var j = 0; j < ys.length; j++)
{
if (val2.children[ys[j]])
val2 = val2.children[ys[j]];
else
{
var t = {sum: 0, count: 0, children: {}};
val2.children[ys[j]] = t;
val2 = t;
}
val2.sum += xs[i].sum;
val2.count++;
}
}
function compress(val) // return the number of items
{
var n = 0;
for (var j in val.children)
n += compress(val.children[j]);
if (n == 1)
val.children = {};
return Math.max(1,n);
}
compress(val);
function flatten(path, val, res)
{
for (var i in val.children)
{
var ii = val.children[i];
var pp = path.slice(0);
pp.push(i);
res.push({sum:ii.sum, count:ii.count, name:pp, free:path.length==0});
flatten(pp,ii,res);
}
}
var flat = [];
flatten([], val, flat);
while(true)
{
// order everything
flat.sort(function(a,b){var i = b.sum - a.sum; return i != 0 ? i : b.count - a.count;});
// now, if something in the first n is not free, mark it free and delete anyone who relies on it
// then repeat, if nothing is not free, break
var cont = false;
for (var i = 0; i < Math.min(resultSize, flat.length); i++)
{
if (!flat[i].free)
{
cont = true;
flat[i].free = true;
for (var j = 0; j < flat.length; j++)
{
if (listEq(flat[i].name.slice(0,flat[i].name.length-1), flat[j].name))
{
flat[j].count -= flat[i].count;
flat[j].sum -= flat[i].sum;
}
}
break;
}
}
if (!cont) break;
}
for (var i = 0; i < flat.length; i++)
flat[i].name = flat[i].name.join(" ");
return flat.slice(0, resultSize);
}
function load()
{
/////////////////////////////////////////////////////////////////
// SUMMARY INFORMATION
var countLast = 0;
var sumExecution = 0;
var maxExecution = 0;
var countTrace = 0, countTraceLast = 0;
var sumTrace = 0;
var maxTrace = 0;
var maxTraceStopLast = 0;
for (var i = 0; i < shake.length; i++)
{
var isLast = shake[i].built == lastRun;
countLast += isLast ? 1 : 0;
sumExecution += shake[i].execution;
maxExecution = Math.max(maxExecution, shake[i].execution);
var traces = shake[i].traces;
if (!traces) continue;
for (var j = 0; j < traces.length; j++)
{
countTrace += 1;
countTraceLast += isLast ? 1 : 0;
sumTrace += traces[j].stop - traces[j].start;
maxTrace = Math.max(maxTrace, traces[j].stop - traces[j].start);
maxTraceStopLast = Math.max(maxTraceStopLast, isLast ? traces[j].stop : 0);
}
}
var summary =
"<ul>" +
"<li><strong>Runs:</strong> This database has tracked " + built.length + " run" + plural(built.length) + ".</li>" +
"<li><strong>Rules:</strong> There are " + shake.length + " rules (" + countLast + " rebuilt in the last run).</li>" +
"<li><strong>Commands:</strong> Building required " + countTrace + " traced commands (" + countTraceLast + " in the last run).</li>" +
"<li><strong>Build time:</strong> The total (unparallelised) build time is " + showTime(sumExecution) + " of which " + showTime(sumTrace) + " is traced commands.</li>" +
"<li><strong>Longest steps:</strong> The longest rule takes " + showTime(maxExecution) + ", and the longest traced command takes " + showTime(maxTrace) + ".</li>" +
"<li><strong>Parallelism:</strong> Last run gave an average parallelism of " + (sumTrace / maxTraceStopLast).toFixed(2) + " times over " + showTime(maxTraceStopLast) + ".</li>" +
"</ul>";
$('#summary').append(summary);
/////////////////////////////////////////////////////////////////
// PARALLELISM GRAPH
var buckets = [];
var countBuckets = 100;
for (var i = 0; i <= countBuckets; i++)
buckets.push(0); // fill with 1 more element, but the last bucket will always be 0
for (var i = 0; i < shake.length; i++)
{
var traces = shake[i].traces;
if (!traces || shake[i].built != lastRun) continue;
for (var j = 0; j < traces.length; j++)
{
var start = traces[j].start * countBuckets / maxTraceStopLast;
var stop = traces[j].stop * countBuckets / maxTraceStopLast;
if (Math.floor(start) == Math.floor(stop))
buckets[Math.floor(start)] += stop - start;
else
{
for (var k = Math.ceil(start); k < Math.floor(stop); k++)
buckets[k]++;
buckets[Math.floor(start)] += Math.ceil(start) - start;
buckets[Math.floor(stop)] += stop - Math.floor(stop);
}
}
}
var maxBucket = 0;
for (var i = 0; i < countBuckets; i++)
maxBucket = Math.max(maxBucket, buckets[i]);
maxBucket = Math.ceil(maxBucket - 0.00001);
plotvals = [];
for (var i = 0; i < countBuckets; i++)
plotvals.push([i, (buckets[i] * 100 / maxBucket)]);
$.plot($('#shakeplot'), [plotvals]);
/////////////////////////////////////////////////////////////////
// MOST EXPENSIVE RULES
var top = shake.slice(0).sort(function(a,b){return b.execution-a.execution}).slice(0,15);
var rules = "<tbody>";
for (var i = 0; i < top.length; i++)
{
rules += "<tr>" +
"<td><div class='progress progress-success' style='height: 10px'>" +
"<div class='bar' style='width:" + (top[i].execution * 40 / top[0].execution) + "px;'></div></div></td>" +
"<td>" + showTime(top[i].execution) + "</td>" +
"<td>" + showPerc(top[i].execution / sumExecution) + "</td>" +
"<td>" + top[i].name + "</td>" +
"</tr>";
}
rules += "</tbody>";
$('#rule-details').append(rules);
/////////////////////////////////////////////////////////////////
// MOST EXPENSIVE COMMANDS
var tooList = [];
for (var i = 0; i < shake.length; i++)
{
var traces = shake[i].traces;
if (!traces) continue;
for (var j = 0; j < traces.length; j++)
tooList.push({name: traces[j].command, sum: traces[j].stop - traces[j].start});
}
toolList = huffman(15, tooList);
var commands = "<tbody>";
for (var i = 0; i < toolList.length; i++)
{
commands += "<tr>" +
"<td><div class='progress progress-success' style='height: 10px'>" +
"<div class='bar' style='width:" + (toolList[i].sum * 40 / toolList[0].sum) + "px;'></div></div></td>" +
"<td>" + showTime(toolList[i].sum) + "</td>" +
"<td>" + showPerc(toolList[i].sum / sumExecution) + "</td>" +
"<td>" + toolList[i].count + " ×</td>" +
"<td>" + toolList[i].name + "</td>" +
"</tr>";
}
commands += "</tbody>";
$('#cmd-details').append(commands);
}