Render loglog scatter, fix data format, start draw

This commit is contained in:
Christoph Kluge 2023-09-04 12:53:38 +02:00
parent c1b5134627
commit 8d7f942de4
2 changed files with 190 additions and 70 deletions

View File

@ -672,6 +672,7 @@
<div bind:clientWidth={colWidth3}> <div bind:clientWidth={colWidth3}>
<Rooflineuplot <Rooflineuplot
width={colWidth3 - 25} width={colWidth3 - 25}
cluster={$initq.data.clusters.find((c) => c.name == cluster).subClusters[0]}
/> />
</Col> </Col>
</Row> </Row>

View File

@ -7,7 +7,7 @@
export let flopsAny = null export let flopsAny = null
export let memBw = null export let memBw = null
export let cluster = null export let cluster = null
export let maxY = null export let defaultMaxY = null
export let width = 500 export let width = 500
export let height = 300 export let height = 300
export let tiles = null export let tiles = null
@ -19,6 +19,11 @@
let uplot = null let uplot = null
let timeoutId = null let timeoutId = null
const paddingLeft = 40,
paddingRight = 10,
paddingTop = 10,
paddingBottom = 50
// Three Render-Cases: // Three Render-Cases:
// #1 Single-Job Roofline -> Has Time-Information: Use data, allow colorDots && showTime // #1 Single-Job Roofline -> Has Time-Information: Use data, allow colorDots && showTime
// #2 MultiNode Roofline - > Has No Time-Information: Transform from nodeData, only "IST"-state of nodes, no timeInfo // #2 MultiNode Roofline - > Has No Time-Information: Transform from nodeData, only "IST"-state of nodes, no timeInfo
@ -30,6 +35,14 @@
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
} }
function randFloat(min, max) {
return roundTwo(((Math.random() * (max - min + 1)) + min) / randInt(1, 500));
}
function roundTwo(num) {
return Math.round((num + Number.EPSILON) * 100) / 100
}
function filledArr(len, val) { function filledArr(len, val) {
let arr = Array(len); let arr = Array(len);
@ -45,26 +58,42 @@
return arr; return arr;
} }
let points = 100; let points = 1000;
let series = 2;
let time = []
for (let i = 0; i < points; ++i) data = [null, [], []] // Null-Axis required for scatter
time[i] = i; data[1][0] = filledArr(points, i => randFloat(1,5000)) // Intensity
data[1][1] = filledArr(points, i => randFloat(1,5000)) // Performance
data = filledArr(series, v => [ data[2] = filledArr(points, i => 0) // Time Information (Optional)
filledArr(points, i => randInt(0,200)),
filledArr(points, i => randInt(0,200)),
]);
data[0] = null;
console.log("Subcluster: ", cluster);
console.log("Data: ", data); console.log("Data: ", data);
// End Demo Data // End Demo Data
// Helpers // Helpers
const [minX, maxX, minY, maxY] = [0.01, 1000, 1., cluster?.flopRateSimd?.value || defaultMaxY]
const w = width - paddingLeft - paddingRight
const h = height - paddingTop - paddingBottom
const [log10minX, log10maxX, log10minY, log10maxY] =
[Math.log10(minX), Math.log10(maxX), Math.log10(minY), Math.log10(maxY)]
const getCanvasX = (x) => {
x = Math.log10(x)
x -= log10minX; x /= (log10maxX - log10minX)
return Math.round((x * w) + paddingLeft)
}
const getCanvasY = (y) => {
y = Math.log10(y)
y -= log10minY
y /= (log10maxY - log10minY)
return Math.round((h - y * h) + paddingTop)
}
function getGradientR(x) { function getGradientR(x) {
if (x < 0.5) return 0 if (x < 0.5) return 0
if (x > 0.75) return 255 if (x > 0.75) return 255
@ -90,6 +119,78 @@
return `rgb(${getGradientR(c)}, ${getGradientG(c)}, ${getGradientB(c)})` return `rgb(${getGradientR(c)}, ${getGradientG(c)}, ${getGradientB(c)})`
} }
function lineIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
let l = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
let a = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / l
return {
x: x1 + a * (x2 - x1),
y: y1 + a * (y2 - y1)
}
}
function transformData(flopsAny, memBw, colorDots) { // Uses Metric Object
const nodes = flopsAny.series.length
const timesteps = flopsAny.series[0].data.length
/* c will contain values from 0 to 1 representing the time */
const x = [], y = [], c = []
if (flopsAny && memBw) {
for (let i = 0; i < nodes; i++) {
const flopsData = flopsAny.series[i].data
const memBwData = memBw.series[i].data
for (let j = 0; j < timesteps; j++) {
const f = flopsData[j], m = memBwData[j]
const intensity = f / m
if (Number.isNaN(intensity) || !Number.isFinite(intensity))
continue
x.push(intensity)
y.push(f)
c.push(colorDots ? j / timesteps : 0)
}
}
} else {
console.warn("transformData: metrics for 'mem_bw' and/or 'flops_any' missing!")
}
return {
x, y, c,
xLabel: 'Intensity [FLOPS/byte]',
yLabel: 'Performance [GFLOPS]'
}
}
// Return something to be plotted. The argument shall be the result of the
// `nodeMetrics` GraphQL query.
export function transformPerNodeData(nodes) {
const x = [], y = [], c = []
for (let node of nodes) {
let flopsAny = node.metrics.find(m => m.name == 'flops_any' && m.scope == 'node')?.metric
let memBw = node.metrics.find(m => m.name == 'mem_bw' && m.scope == 'node')?.metric
if (!flopsAny || !memBw) {
console.warn("transformPerNodeData: metrics for 'mem_bw' and/or 'flops_any' missing!")
continue
}
let flopsData = flopsAny.series[0].data, memBwData = memBw.series[0].data
const f = flopsData[flopsData.length - 1], m = memBwData[flopsData.length - 1]
const intensity = f / m
if (Number.isNaN(intensity) || !Number.isFinite(intensity))
continue
x.push(intensity)
y.push(f)
c.push(0)
}
return {
x, y, c,
xLabel: 'Intensity [FLOPS/byte]',
yLabel: 'Performance [GFLOPS]'
}
}
// End Helpers // End Helpers
const drawPoints = (u, seriesIdx, idx0, idx1) => { const drawPoints = (u, seriesIdx, idx0, idx1) => {
@ -123,22 +224,6 @@
return null; return null;
}; };
function guardedRange(u, min, max) {
if (max == min) {
if (min == null) {
min = 0;
max = 100;
}
else {
let delta = Math.abs(max) || 100;
max += delta;
min -= delta;
}
}
return [min, max];
}
function render() { function render() {
const opts = { const opts = {
title: "", title: "",
@ -146,8 +231,39 @@
width: width, width: width,
height: height, height: height,
legend: { legend: {
live: false, show: false
}, },
axes: [
{
label: 'Intensity [FLOPS/Byte]'
},
{
label: 'Performace [GFLOPS]'
}
],
scales: {
x: {
time: false,
distr: 3,
log: 10,
},
y: {
distr: 3,
log: 10,
},
},
series: [
{},
{
stroke: (u, seriesIdx) => {
for (let i = 0; i < points; ++i) { return getRGB(data[2][i]) }
},
fill: (u, seriesIdx) => {
for (let i = 0; i < points; ++i) { return getRGB(data[2][i]) }
},
paths: drawPoints,
}
],
hooks: { hooks: {
drawClear: [ drawClear: [
u => { u => {
@ -157,48 +273,51 @@
}); });
}, },
], ],
}, draw: [
scales: { u => { // draw roofs
x: { u.ctx.strokeStyle = 'black'
time: false, u.ctx.lineWidth = 2
// auto: false, u.ctx.beginPath()
// range: [0, 500], if (cluster != null) {
// remove any scale padding, use raw data limits const ycut = 0.01 * cluster.memoryBandwidth.value
range: guardedRange, const scalarKnee = (cluster.flopRateScalar.value - ycut) / cluster.memoryBandwidth.value
}, const simdKnee = (cluster.flopRateSimd.value - ycut) / cluster.memoryBandwidth.value
y: { const scalarKneeX = getCanvasX(scalarKnee),
// auto: false, simdKneeX = getCanvasX(simdKnee),
// range: [0, 500], flopRateScalarY = getCanvasY(cluster.flopRateScalar.value),
// remove any scale padding, use raw data limits flopRateSimdY = getCanvasY(cluster.flopRateSimd.value)
range: guardedRange,
},
},
series: [
{
/*
stroke: "red",
fill: "rgba(255,0,0,0.1)",
paths: (u, seriesIdx, idx0, idx1) => {
uPlot.orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim) => {
let d = u.data[seriesIdx];
console.log(d); if (scalarKneeX < width - paddingRight) {
}); u.ctx.moveTo(scalarKneeX, flopRateScalarY)
u.ctx.lineTo(width - paddingRight, flopRateScalarY)
}
return null; if (simdKneeX < width - paddingRight) {
}, u.ctx.moveTo(simdKneeX, flopRateSimdY)
*/ u.ctx.lineTo(width - paddingRight, flopRateSimdY)
}, }
{
stroke: (u, seriesIdx) => { let x1 = getCanvasX(0.01),
for (let i = 0; i < points; ++i) { return getRGB(time[i]) } y1 = getCanvasY(ycut),
}, x2 = getCanvasX(simdKnee),
fill: (u, seriesIdx) => { y2 = flopRateSimdY
for (let i = 0; i < points; ++i) { return getRGB(time[i]) }
}, let xAxisIntersect = lineIntersect(
paths: drawPoints, x1, y1, x2, y2,
} 0, height - paddingBottom, width, height - paddingBottom)
],
if (xAxisIntersect.x > x1) {
x1 = xAxisIntersect.x
y1 = xAxisIntersect.y
}
u.ctx.moveTo(x1, y1)
u.ctx.lineTo(x2, y2)
}
u.ctx.stroke()
}
]
},
}; };
uplot = new uPlot(opts, data, plotWrapper); uplot = new uPlot(opts, data, plotWrapper);