|
@@ -18,195 +18,63 @@
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
- uniformSeries: function () {
|
|
|
- var series_min_length = 100000000;
|
|
|
- for (i=0; i<arguments.length; i++) {
|
|
|
- if (arguments[i].length < series_min_length) {
|
|
|
- series_min_length = arguments[i].length;
|
|
|
+ durationFormatter:function(d) {
|
|
|
+ if (d==0) { return "0" }
|
|
|
+ var seconds = Math.floor(parseInt(d) / 1000);
|
|
|
+ if ( seconds < 60 )
|
|
|
+ return seconds + "s";
|
|
|
+ var minutes = Math.floor(seconds / 60);
|
|
|
+ if ( minutes < 60 ) {
|
|
|
+ var x = seconds - 60*minutes;
|
|
|
+ return minutes + "m" + (x==0 ? "" : " " + x + "s");
|
|
|
}
|
|
|
- }
|
|
|
- for (i=0; i<arguments.length; i++) {
|
|
|
- if (arguments[i].length > series_min_length) {
|
|
|
- arguments[i].length = series_min_length;
|
|
|
+ var hours = Math.floor(minutes / 60);
|
|
|
+ if ( hours < 24 ) {
|
|
|
+ var x = minutes - 60*hours;
|
|
|
+ return hours + "h" + (x==0 ? "" : " " + x + "m");
|
|
|
}
|
|
|
- }
|
|
|
- },
|
|
|
- /**
|
|
|
- * Get min, max for X and Y for provided series
|
|
|
- * @param series
|
|
|
- * @return {Object}
|
|
|
- */
|
|
|
- getExtInSeries: function(series) {
|
|
|
- var maxY = 0;
|
|
|
- var maxX = 0;
|
|
|
- var minX = 2147465647; // max timestamp value
|
|
|
- var minY = 2147465647;
|
|
|
- if (series.length > 0) {
|
|
|
- series.forEach(function(item){
|
|
|
- if (item.y > maxY) {
|
|
|
- maxY = item.y;
|
|
|
- }
|
|
|
- if (item.y < minY) {
|
|
|
- minY = item.y;
|
|
|
- }
|
|
|
- if (item.x > maxX) {
|
|
|
- maxX = item.x;
|
|
|
- }
|
|
|
- if (item.x < minX) {
|
|
|
- minX = item.x;
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- return {maxX: maxX, minX: minX, maxY: maxY, minY: minY};
|
|
|
+ var days = Math.floor(hours / 24);
|
|
|
+ if ( days < 7 ) {
|
|
|
+ var x = hours - 24*days;
|
|
|
+ return days + "d " + (x==0 ? "" : " " + x + "h");
|
|
|
+ }
|
|
|
+ var weeks = Math.floor(days / 7);
|
|
|
+ var x = days - 7*weeks;
|
|
|
+ return weeks + "w " + (x==0 ? "" : " " + x + "d");
|
|
|
},
|
|
|
- /**
|
|
|
- * Get min, max for x and Y for all provided series
|
|
|
- * @param args array of series
|
|
|
- * @return {Object}
|
|
|
- */
|
|
|
- getExtInAllSeries: function(args) {
|
|
|
- var maxx = [];
|
|
|
- var minx = [];
|
|
|
- var maxy = [];
|
|
|
- var miny = [];
|
|
|
- for (var i = 0; i < args.length; i++) {
|
|
|
- var localExt = this.getExtInSeries(args[i]);
|
|
|
- maxx.push(localExt.maxX);
|
|
|
- minx.push(localExt.minX);
|
|
|
- maxy.push(localExt.maxY);
|
|
|
- miny.push(localExt.minY);
|
|
|
- }
|
|
|
- return {
|
|
|
- maxX: Math.max.apply(null, maxx),
|
|
|
- minX: Math.min.apply(null, minx),
|
|
|
- maxY: Math.max.apply(null, maxy),
|
|
|
- minY: Math.min.apply(null, miny)
|
|
|
- };
|
|
|
+ bytesFormatter:function(y) {
|
|
|
+ if (y >= 1125899906842624) { return Math.floor(10 * y / 1125899906842624)/10 + " PB" }
|
|
|
+ else if (y >= 1099511627776){ return Math.floor(10 * y / 1099511627776)/10 + " TB" }
|
|
|
+ else if (y >= 1073741824) { return Math.floor(10 * y / 1073741824)/10 + " GB" }
|
|
|
+ else if (y >= 1048576) { return Math.floor(10 * y / 1048576)/10 + " MB" }
|
|
|
+ else if (y >= 1024) { return Math.floor(10 * y / 1024)/10 + " KB" }
|
|
|
+ else { return y + " B"}
|
|
|
},
|
|
|
- /**
|
|
|
- * Get coordinates for new circle in the graph
|
|
|
- * New circle needed to prevent cut on the borders of the graph
|
|
|
- * List of arguments - series arrays
|
|
|
- * @return {Object}
|
|
|
- */
|
|
|
- getNewCircle: function() {
|
|
|
- var ext = this.getExtInAllSeries(arguments);
|
|
|
- var newX;
|
|
|
- if (ext.minX != 2147465647) {
|
|
|
- newX = ext.maxX + Math.round((ext.maxX - ext.minX) * 0.2);
|
|
|
- }
|
|
|
- else {
|
|
|
- newX = (new Date()).getTime();
|
|
|
- }
|
|
|
- var newY = ext.maxY * 1.2;
|
|
|
- return {
|
|
|
- x: newX,
|
|
|
- y: newY,
|
|
|
- r: 0,
|
|
|
- io: 0
|
|
|
- };
|
|
|
- },
|
|
|
- /**
|
|
|
- *
|
|
|
- * @param map
|
|
|
- * @param shuffle
|
|
|
- * @param reduce
|
|
|
- * @param w
|
|
|
- * @param h
|
|
|
- * @param element
|
|
|
- * @param legend_id
|
|
|
- * @param timeline_id
|
|
|
- */
|
|
|
- drawJobTimeLine:function (map, shuffle, reduce, w, h, element, legend_id, timeline_id) {
|
|
|
- map = $.parseJSON(map);
|
|
|
- shuffle = $.parseJSON(shuffle);
|
|
|
- reduce = $.parseJSON(reduce);
|
|
|
- if (!map || !shuffle || !reduce) {
|
|
|
- console.warn('drawJobTimeLine');
|
|
|
- return;
|
|
|
- }
|
|
|
- this.uniformSeries(map, reduce, shuffle);
|
|
|
- var ext = this.getExtInAllSeries([map, reduce, shuffle]);
|
|
|
- var submitTime = ext.minX;
|
|
|
- var maxX = ext.maxX; // Used on X-axis time stamps
|
|
|
-
|
|
|
- var graph = new Rickshaw.Graph({
|
|
|
- width:w,
|
|
|
- height:h,
|
|
|
- element:document.querySelector(element),
|
|
|
- renderer:'area',
|
|
|
- interpolation: 'step-after',
|
|
|
- strokeWidth: 2,
|
|
|
- stroke:true,
|
|
|
- series:[
|
|
|
- {
|
|
|
- data:map,
|
|
|
- color:'green',
|
|
|
- name:'maps'
|
|
|
- },
|
|
|
- {
|
|
|
- data:shuffle,
|
|
|
- color:'lightblue',
|
|
|
- name:'shuffles'
|
|
|
- },
|
|
|
- {
|
|
|
- data:reduce,
|
|
|
- color:'steelblue',
|
|
|
- name:'reduces'
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- );
|
|
|
- graph.render();
|
|
|
-
|
|
|
- var legend = new Rickshaw.Graph.Legend({
|
|
|
- graph:graph,
|
|
|
- element:document.getElementById(legend_id)
|
|
|
- });
|
|
|
-
|
|
|
- var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
|
|
|
- graph:graph,
|
|
|
- legend:legend
|
|
|
- });
|
|
|
-
|
|
|
- var order = new Rickshaw.Graph.Behavior.Series.Order({
|
|
|
- graph:graph,
|
|
|
- legend:legend
|
|
|
- });
|
|
|
-
|
|
|
- var highlight = new Rickshaw.Graph.Behavior.Series.Highlight({
|
|
|
- graph:graph,
|
|
|
- legend:legend
|
|
|
- });
|
|
|
-
|
|
|
- var xAxis = new Rickshaw.Graph.Axis.Time({
|
|
|
- graph:graph,
|
|
|
- timeUnit: {
|
|
|
- name: 'Custom',
|
|
|
- seconds: Math.round((maxX - submitTime) / 2),
|
|
|
- formatter: function(d) { return (new Date(d)).getTime() / 1000 - submitTime + 's'; }
|
|
|
- }
|
|
|
- });
|
|
|
- xAxis.render();
|
|
|
-
|
|
|
- var yAxis = new Rickshaw.Graph.Axis.Y({
|
|
|
- orientation: 'left',
|
|
|
- element: document.querySelector('#y-axis'),
|
|
|
- graph:graph
|
|
|
- });
|
|
|
- yAxis.render();
|
|
|
-
|
|
|
- var hoverDetail = new Rickshaw.Graph.HoverDetail({
|
|
|
- graph:graph,
|
|
|
- yFormatter:function (y) {
|
|
|
- return Math.floor(y) + " tasks"
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- /*var annotator = new Rickshaw.Graph.Annotate({
|
|
|
- graph:graph,
|
|
|
- //element:document.getElementById(timeline_id)
|
|
|
- });*/
|
|
|
+ addSeries:function(svgg,series,color,xscale,yscale,margin,startTime,dotInfo) {
|
|
|
+ if (series.length==0) return;
|
|
|
+ var self = this;
|
|
|
+ var g = svgg.append("svg:g").selectAll("g")
|
|
|
+ .data(series)
|
|
|
+ .enter().append("svg:g")
|
|
|
+ .attr("transform", "translate(0,"+margin+")");
|
|
|
+ g.append("svg:circle")
|
|
|
+ .attr("r",function(d) {return d.r;})
|
|
|
+ .attr("cx",function(d) {return xscale(d.x);})
|
|
|
+ .attr("cy",function(d) {return yscale(d.y);})
|
|
|
+ .style("fill",color)
|
|
|
+ .style("fill-opacity",0.8)
|
|
|
+ .style("stroke",d3.interpolateRgb(color, 'black')(0.125))
|
|
|
+ .append("title")
|
|
|
+ .text(function(d) { return dotInfo[Math.round(xscale(d.x))][Math.round(yscale(d.y))]; });
|
|
|
+ g.append("svg:line")
|
|
|
+ .attr("x1", function(d) { return xscale(d.x)+d.r; } )
|
|
|
+ .attr("x2", function(d) { return xscale(d.x+d.y); } )
|
|
|
+ .attr("y1", function(d) { return yscale(d.y); } )
|
|
|
+ .attr("y2", function(d) { return yscale(d.y); } )
|
|
|
+ .style("stroke",d3.interpolateRgb(color, 'black')(0.125))
|
|
|
+ .style("stroke-width",2)
|
|
|
+ .append("title")
|
|
|
+ .text(function(d) { return dotInfo[Math.round(xscale(d.x))][Math.round(yscale(d.y))]; });
|
|
|
},
|
|
|
/**
|
|
|
*
|
|
@@ -214,125 +82,103 @@ module.exports = {
|
|
|
* @param mapRackLocal
|
|
|
* @param mapOffSwitch
|
|
|
* @param reduceOffSwitch
|
|
|
- * @param submitTime
|
|
|
+ * @param startTime
|
|
|
+ * @param endTime
|
|
|
* @param w
|
|
|
* @param h
|
|
|
* @param element
|
|
|
- * @param legend_id
|
|
|
- * @param timeline_id
|
|
|
*/
|
|
|
- drawJobTasks:function (mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch, submitTime, w, h, element, legend_id, timeline_id) {
|
|
|
- mapNodeLocal = $.parseJSON(mapNodeLocal);
|
|
|
- mapRackLocal = $.parseJSON(mapRackLocal);
|
|
|
- mapOffSwitch = $.parseJSON(mapOffSwitch);
|
|
|
- reduceOffSwitch = $.parseJSON(reduceOffSwitch);
|
|
|
- if (!mapNodeLocal || !mapRackLocal || !mapOffSwitch || !reduceOffSwitch) {
|
|
|
- console.warn('drawJobTasks');
|
|
|
- return;
|
|
|
- }
|
|
|
- this.uniformSeries(mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch);
|
|
|
- var newC = this.getNewCircle(mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch);
|
|
|
- var ext = this.getExtInAllSeries([mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch]);
|
|
|
- var maxX = ext.maxX; // Used on X-axis time stamps
|
|
|
- mapNodeLocal.push(newC);
|
|
|
- mapRackLocal.push(newC);
|
|
|
- mapOffSwitch.push(newC);
|
|
|
- reduceOffSwitch.push(newC);
|
|
|
- var graph = new Rickshaw.Graph({
|
|
|
- width:w,
|
|
|
- height:h,
|
|
|
- element:document.querySelector(element),
|
|
|
- renderer:'scatterplot',
|
|
|
- stroke:true,
|
|
|
- series:[
|
|
|
- {
|
|
|
- data:mapNodeLocal,
|
|
|
- color:'green',
|
|
|
- name:'node_local_map'
|
|
|
- },
|
|
|
- {
|
|
|
- data:mapRackLocal,
|
|
|
- color:'#66B366',
|
|
|
- name:'rack_local_map'
|
|
|
- },
|
|
|
- {
|
|
|
- data:mapOffSwitch,
|
|
|
- color:'brown',
|
|
|
- name:'off_switch_map'
|
|
|
- },
|
|
|
- {
|
|
|
- data:reduceOffSwitch,
|
|
|
- color:'steelblue',
|
|
|
- name:'reduce'
|
|
|
- }
|
|
|
- ]
|
|
|
- });
|
|
|
- graph.render();
|
|
|
- var legend = new Rickshaw.Graph.Legend({
|
|
|
- graph:graph,
|
|
|
- element:document.getElementById(legend_id)
|
|
|
- });
|
|
|
-
|
|
|
- var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
|
|
|
- graph:graph,
|
|
|
- legend:legend
|
|
|
- });
|
|
|
-
|
|
|
- var order = new Rickshaw.Graph.Behavior.Series.Order({
|
|
|
- graph:graph,
|
|
|
- legend:legend
|
|
|
- });
|
|
|
-
|
|
|
- var highlight = new Rickshaw.Graph.Behavior.Series.Highlight({
|
|
|
- graph:graph,
|
|
|
- legend:legend
|
|
|
- });
|
|
|
-
|
|
|
- var ticksTreatment = 'glow';
|
|
|
-
|
|
|
- var xAxis = new Rickshaw.Graph.Axis.Time({
|
|
|
- graph:graph,
|
|
|
- timeUnit: {
|
|
|
- name: 'Custom',
|
|
|
- seconds: Math.round((maxX - submitTime) / 2),
|
|
|
- formatter: function(d) { return (new Date(d)).getTime() / 1000 - submitTime + 's'; }
|
|
|
- },
|
|
|
- ticksTreatment:ticksTreatment
|
|
|
- });
|
|
|
- xAxis.render();
|
|
|
-
|
|
|
- var yAxis = new Rickshaw.Graph.Axis.Y({
|
|
|
- graph:graph,
|
|
|
- ticksTreatment:ticksTreatment,
|
|
|
- orientation: 'left',
|
|
|
- element: document.querySelector('#y-axis2'),
|
|
|
- tickFormat: function(y) { return y / 1000 + 's' }
|
|
|
- });
|
|
|
- yAxis.render();
|
|
|
+ drawJobTasks:function (mapNodeLocal, mapRackLocal, mapOffSwitch, reduceOffSwitch, startTime, endTime, svgw, svgh, element) {
|
|
|
+ var rmax = 24; // default value
|
|
|
+ var axisHeight = 24;
|
|
|
+ var margin = {"vertical":10, "horizontal":50};
|
|
|
+ var w = svgw - 2*margin.horizontal;
|
|
|
+ var h = svgh - 2*margin.vertical;
|
|
|
+ var x = d3.time.scale.utc()
|
|
|
+ .domain([startTime, endTime])
|
|
|
+ .range([0, w]);
|
|
|
+ var xrel = d3.time.scale()
|
|
|
+ .domain([0, endTime-startTime])
|
|
|
+ .range([0, w]);
|
|
|
+ // create axes
|
|
|
+ var topAxis = d3.svg.axis()
|
|
|
+ .scale(x)
|
|
|
+ .orient("bottom");
|
|
|
+ var self = this;
|
|
|
+ var bottomAxis = d3.svg.axis()
|
|
|
+ .scale(xrel)
|
|
|
+ .orient("bottom")
|
|
|
+ .tickFormat(function(d) {return self.durationFormatter(d.getTime())});
|
|
|
+
|
|
|
+ var svg = d3.select("div#" + element).append("svg:svg")
|
|
|
+ .attr("width", svgw+"px")
|
|
|
+ .attr("height", svgh+"px");
|
|
|
+ var svgg = svg.append("g")
|
|
|
+ .attr("transform", "translate("+margin.horizontal+","+margin.vertical+")");
|
|
|
+
|
|
|
+ svgg.append("g")
|
|
|
+ .attr("class", "x axis top")
|
|
|
+ .call(topAxis);
|
|
|
+ svgg.append("g")
|
|
|
+ .attr("class", "x axis bottom")
|
|
|
+ .call(bottomAxis)
|
|
|
+ .attr("transform", "translate(0,"+(h-axisHeight)+")");
|
|
|
+
|
|
|
+ var ymax = 0;
|
|
|
+ if (mapNodeLocal.length > 0)
|
|
|
+ ymax = Math.max(ymax, d3.max(mapNodeLocal, function(d) { return d.y; } ));
|
|
|
+ if (mapRackLocal.length > 0)
|
|
|
+ ymax = Math.max(ymax, d3.max(mapRackLocal, function(d) { return d.y; } ));
|
|
|
+ if (mapOffSwitch.length > 0)
|
|
|
+ ymax = Math.max(ymax, d3.max(mapOffSwitch, function(d) { return d.y; } ));
|
|
|
+ if (reduceOffSwitch.length > 0)
|
|
|
+ ymax = Math.max(ymax, d3.max(reduceOffSwitch, function(d) { return d.y; } ));
|
|
|
+
|
|
|
+ var y = d3.scale.linear()
|
|
|
+ .domain([0, ymax])
|
|
|
+ .range([h-2*axisHeight-rmax, 0]);
|
|
|
+
|
|
|
+ var yAxis = d3.svg.axis()
|
|
|
+ .scale(y)
|
|
|
+ .orient("left")
|
|
|
+ .tickFormat(self.durationFormatter);
|
|
|
+
|
|
|
+ svgg.append("svg:g")
|
|
|
+ .attr("class", "y axis")
|
|
|
+ .call(yAxis)
|
|
|
+ .attr("transform", "translate(0,"+(axisHeight+rmax)+")")
|
|
|
+ .append("text")
|
|
|
+ .attr("transform", "rotate(-90)")
|
|
|
+ .attr("x", -(h-2*axisHeight-rmax)/2)
|
|
|
+ .attr("y", -margin.horizontal + 11)
|
|
|
+ .attr("class", "axislabel")
|
|
|
+ .text("Task Attempt Duration");
|
|
|
+
|
|
|
+
|
|
|
+ var dotInfo = new Array();
|
|
|
+ var mapDotInfo = function(d) {
|
|
|
+ var thisx = Math.round(x(d.x));
|
|
|
+ var thisy = Math.round(y(d.y));
|
|
|
+ if (!(thisx in dotInfo))
|
|
|
+ dotInfo[thisx] = new Array();
|
|
|
+ var existing = dotInfo[thisx][thisy];
|
|
|
+ var newInfo = d.label + " \n" +
|
|
|
+ 'Run-time: ' + self.durationFormatter(d.y) + ' \nWait-time: ' + self.durationFormatter(d.x-startTime) +
|
|
|
+ ' \nI/O: ' + self.bytesFormatter(d.io) + ' \nStatus: ' + d.status;
|
|
|
+ if (existing)
|
|
|
+ dotInfo[thisx][thisy] = existing + " \n" + newInfo;
|
|
|
+ else
|
|
|
+ dotInfo[thisx][thisy] = newInfo;
|
|
|
+ };
|
|
|
|
|
|
- var hoverDetail = new Rickshaw.Graph.HoverDetail({
|
|
|
- graph:graph,
|
|
|
- xFormatter:function (x) {
|
|
|
- return (x - submitTime) + 's'
|
|
|
- },
|
|
|
- yFormatter:function (y) {
|
|
|
- return y / 1000 + 's'
|
|
|
- },
|
|
|
- formatter:function (series, x, y, formattedX, formattedY, d) {
|
|
|
- var bytesFormatter = function(y) {
|
|
|
- if (y >= 1125899906842624) { return Math.floor(10 * y / 1125899906842624)/10 + " PB" }
|
|
|
- else if (y >= 1099511627776){ return Math.floor(10 * y / 1099511627776)/10 + " TB" }
|
|
|
- else if (y >= 1073741824) { return Math.floor(10 * y / 1073741824)/10 + " GB" }
|
|
|
- else if (y >= 1048576) { return Math.floor(10 * y / 1048576)/10 + " MB" }
|
|
|
- else if (y >= 1024) { return Math.floor(10 * y / 1024)/10 + " KB" }
|
|
|
- else { return y + " B"}
|
|
|
- };
|
|
|
- var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
|
|
|
- return swatch + d.value.label +
|
|
|
- '<br>Run-time: ' + formattedY + '<br>Wait-time: ' + formattedX +
|
|
|
- '<br>I/O: ' + bytesFormatter(d.value.io) + '<br>Status: ' + d.value.status;
|
|
|
- }
|
|
|
+ mapNodeLocal.forEach(mapDotInfo);
|
|
|
+ mapRackLocal.forEach(mapDotInfo);
|
|
|
+ mapOffSwitch.forEach(mapDotInfo);
|
|
|
+ reduceOffSwitch.forEach(mapDotInfo);
|
|
|
|
|
|
- });
|
|
|
+ this.addSeries(svgg, mapNodeLocal, "green", x, y, axisHeight+rmax, startTime, dotInfo);
|
|
|
+ this.addSeries(svgg, mapRackLocal,'#66B366', x, y, axisHeight+rmax, startTime, dotInfo);
|
|
|
+ this.addSeries(svgg, mapOffSwitch, 'brown', x, y, axisHeight+rmax, startTime, dotInfo);
|
|
|
+ this.addSeries(svgg, reduceOffSwitch, 'steelblue', x, y, axisHeight+rmax, startTime, dotInfo);
|
|
|
}
|
|
|
}
|