blob: 29b5d231405a414f9f2de92295fdffcd181b19b1 [file]
(function () {
"use strict";
window.addEventListener(
"beforeprint",
function () {
for (const id in Chart.instances) {
Chart.instances[id].resize();
}
},
false,
);
const errorBarPlugin = (function () {
function drawErrorBar(chart, ctx, low, high, y, height, color) {
ctx.save();
ctx.lineWidth = 3;
ctx.strokeStyle = color;
const area = chart.chartArea;
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
ctx.clip();
ctx.beginPath();
ctx.moveTo(low, y - height);
ctx.lineTo(low, y + height);
ctx.moveTo(low, y);
ctx.lineTo(high, y);
ctx.moveTo(high, y - height);
ctx.lineTo(high, y + height);
ctx.stroke();
ctx.restore();
}
// Avoid sudden jumps in error bars when switching
// between linear and logarithmic scale
function conservativeError(vx, mx, now, final, scale) {
const finalDiff = Math.abs(mx - final);
const diff = Math.abs(vx - now);
return diff > finalDiff ? vx + scale * finalDiff : now;
}
return {
afterDatasetDraw: function (chart, easingOptions) {
const ctx = chart.ctx;
const easing = easingOptions.easingValue;
chart.data.datasets.forEach(function (d, i) {
const bars = chart.getDatasetMeta(i).data;
const axis = chart.scales[chart.options.scales.xAxes[0].id];
bars.forEach(function (b, j) {
const value = axis.getValueForPixel(b._view.x);
const final = axis.getValueForPixel(b._model.x);
const errorBar = d.errorBars[j];
const low = axis.getPixelForValue(value - errorBar.minus);
const high = axis.getPixelForValue(value + errorBar.plus);
const finalLow = axis.getPixelForValue(final - errorBar.minus);
const finalHigh = axis.getPixelForValue(final + errorBar.plus);
const l =
easing === 1
? finalLow
: conservativeError(b._view.x, b._model.x, low, finalLow, -1.0);
const h =
easing === 1
? finalHigh
: conservativeError(b._view.x, b._model.x, high, finalHigh, 1.0);
drawErrorBar(chart, ctx, l, h, b._view.y, 4, errorBar.color);
});
});
},
};
})();
// Formats the ticks on the X-axis on the scatter plot
const iterFormatter = function () {
var denom = 0;
return function (iters, index, values) {
if (iters == 0)
return "";
if (index == values.length - 1)
return "";
var power;
if (iters >= 1e9) {
denom = 1e9;
power = "⁹";
} else if (iters >= 1e6) {
denom = 1e6;
power = "⁶";
} else if (iters >= 1e3) {
denom = 1e3;
power = "³";
} else {
denom = 1;
}
if (denom > 1) {
const value = (iters / denom).toFixed();
return String(value) + "×10" + power;
} else {
return String(iters);
}
};
};
const colors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"];
const errorColors = ["#cda220", "#8fb8d8", "#ab2b2b", "#2d872d", "#7420cd"];
// Positions tooltips at cursor. Required for overview since the bars may
// extend past the canvas width.
Chart.Tooltip.positioners.cursor = function (_elems, position) {
return position;
};
function axisType(logaxis) {
return logaxis ? "logarithmic" : "linear";
}
/* Adds indices and full names to each report, and group */
function processReports(reportData) {
const tests = {};
Object.entries(reportData.functions).forEach(([funcName, func]) => {
/* Group by test name */
if (!tests[func.testName])
tests[func.testName] = {};
const reports = tests[func.testName];
/* Group by report name */
var reportGroup = func.testName
if (func.reportName)
reportGroup += "." + func.reportName;
if (!reports[reportGroup])
reports[reportGroup] = { numVersions: 0, functions: {} };
const report = reports[reportGroup];
const funcNumber = Object.keys(report.functions).length;
report.functions[funcName] = func;
Object.entries(func.versions).forEach(([suffix, version]) => {
/* Group versions by function */
version.groups = [funcName, suffix];
version.groupNumber = funcNumber;
version.reportName = funcName + "_" + suffix;
version.reportNumber = report.numVersions;
report.numVersions += 1;
});
});
return tests;
}
function reportSort(a, b) {
return a.reportNumber - b.reportNumber;
}
function durationSort(a, b) {
return a.adjustedTime.mode - b.adjustedTime.mode;
}
function reverseDurationSort(a, b) {
return -durationSort(a, b);
}
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
// compares 2 arrays lexicographically
function lex(aParts, bParts) {
for (var i = 0; i < aParts.length && i < bParts.length; i++) {
const x = aParts[i];
const y = bParts[i];
const comp = collator.compare(x, y);
if (comp)
return comp;
}
return aParts.length - bParts.length;
}
function lexicalSort(a, b) {
return lex(a.groups, b.groups);
}
function reverseLexicalSort(a, b) {
return lex(a.groups.slice().reverse(), b.groups.slice().reverse());
}
function timeUnits(secs) {
if (secs < 0) return timeUnits(-secs);
else if (secs >= 1e9) return [1e-9, "Gs"];
else if (secs >= 1e6) return [1e-6, "Ms"];
else if (secs >= 1) return [1, "s"];
else if (secs >= 1e-3) return [1e3, "ms"];
else if (secs >= 1e-6) return [1e6, "\u03bcs"];
else if (secs >= 1e-9) return [1e9, "ns"];
else if (secs >= 1e-12) return [1e12, "ps"];
else return [1, "s"];
}
function rawUnits(value) {
if (value >= 1e9) return [1e-9, "G"];
else if (value >= 1e6) return [1e-6, "M"];
else if (value >= 1e3) return [1e-3, "k"];
return [1, ""];
}
function formatUnit(raw, unit, precision) {
const v = precision ? raw.toPrecision(precision) : Math.round(raw);
const label = String(v) + " " + unit;
return label;
}
function formatTime(nsecs, precision) {
const secs = nsecs * 1e-9;
const units = timeUnits(secs);
const scale = units[0];
return formatUnit(secs * scale, units[1], precision);
}
function formatCycles(cycles, unit, precision) {
if (unit === "nsec" || unit === "nsecs") {
return formatTime(cycles, precision);
} else {
const units = rawUnits(cycles);
const scale = units[0];
return formatUnit(cycles * scale, units[1] + unit, precision);
}
}
// pure function that produces the 'data' object of the overview chart
function overviewData(state, reports) {
const order = state.order;
const sorter = order === "report-index" ? reportSort
: order === "lex" ? lexicalSort
: order === "colex" ? reverseLexicalSort
: order === "duration" ? durationSort
: order === "rev-duration" ? reverseDurationSort
: reportSort;
const sortedReports = reports.filter((r) => !state.hidden[r.groupNumber]).sort(sorter);
const data = sortedReports.map((report) => report.adjustedTime.mode);
const labels = sortedReports.map((report) => report.reportName);
const upperBound = (report) => report.adjustedTime.upperCI;
const errorBars = sortedReports.map(function (report) {
const est = report.adjustedTime.mode;
return {
minus: est - report.adjustedTime.lowerCI,
plus: report.adjustedTime.upperCI - est,
color: errorColors[report.groupNumber % errorColors.length],
};
});
const top = sortedReports.map(upperBound).reduce((a, b) => Math.max(a, b), 0);
var scale = top;
if (state.activeReport !== null) {
reports.forEach(function (report) {
if (report.reportNumber === state.activeReport)
scale = upperBound(report);
});
}
return {
labels: labels,
top: top,
max: scale * 1.1,
reports: sortedReports,
datasets: [
{
borderWidth: 1,
backgroundColor: sortedReports.map(function (report) {
const active = report.reportNumber === state.activeReport;
const alpha = active ? "ff" : "a0";
const color = colors[report.groupNumber % colors.length] + alpha;
if (active) {
return Chart.helpers.getHoverColor(color);
} else {
return color;
}
}),
barThickness: 16,
barPercentage: 0.8,
data: data,
errorBars: errorBars,
minBarLength: 2,
},
],
};
}
function inside(box, point) {
return (
point.x >= box.left && point.x <= box.right && point.y >= box.top && point.y <= box.bottom
);
}
function overviewHover(event, elems) {
const chart = this;
const xAxis = chart.scales[chart.options.scales.xAxes[0].id];
const yAxis = chart.scales[chart.options.scales.yAxes[0].id];
const point = Chart.helpers.getRelativePosition(event, chart);
const over = inside(xAxis, point) || inside(yAxis, point) || elems.length > 0;
if (over)
chart.canvas.style.cursor = "pointer";
else
chart.canvas.style.cursor = "default";
}
// Re-renders the overview after clicking/sorting
function renderOverview(state, reports, chart) {
const data = overviewData(state, reports);
const xaxis = chart.options.scales.xAxes[0];
xaxis.ticks.max = data.max;
chart.config.data.datasets[0].backgroundColor = data.datasets[0].backgroundColor;
chart.config.data.datasets[0].errorBars = data.datasets[0].errorBars;
chart.config.data.datasets[0].data = data.datasets[0].data;
chart.options.scales.xAxes[0].type = axisType(state.logaxis);
chart.options.legend.display = state.legend;
chart.data.labels = data.labels;
chart.update();
}
function overviewClick(state, reports) {
return function (event, elems) {
const chart = this;
const xAxis = chart.scales[chart.options.scales.xAxes[0].id];
const yAxis = chart.scales[chart.options.scales.yAxes[0].id];
const point = Chart.helpers.getRelativePosition(event, chart);
const sorted = overviewData(state, reports).reports;
function activateBar(index) {
// Trying to activate active bar disables instead
if (sorted[index].reportNumber === state.activeReport)
state.activeReport = null;
else
state.activeReport = sorted[index].reportNumber;
}
if (inside(xAxis, point)) {
state.activeReport = null;
state.logaxis = !state.logaxis;
renderOverview(state, reports, chart);
} else if (inside(yAxis, point)) {
const index = yAxis.getValueForPixel(point.y);
activateBar(index);
renderOverview(state, reports, chart);
} else if (elems.length > 0) {
const elem = elems[0];
const index = elem._index;
activateBar(index);
state.logaxis = false;
renderOverview(state, reports, chart);
} else if (inside(chart.chartArea, point)) {
state.activeReport = null;
renderOverview(state, reports, chart);
}
};
}
// Returns listener for sort drop-down
function overviewSort(state, reports, chart) {
return function (event) {
state.order = event.currentTarget.value;
renderOverview(state, reports, chart);
};
}
// Returns a formatter for the ticks on the X-axis of the overview
function overviewTick(state) {
return function (value, index, values) {
const label = formatTime(value);
if (state.logaxis) {
const remain = Math.round(value / Math.pow(10, Math.floor(Chart.helpers.log10(value))));
if (index === values.length - 1) {
// Draw endpoint if we don't span a full order of magnitude
if (values[index] / values[1] < 10)
return label;
else
return "";
}
if (remain === 1)
return label;
return "";
} else {
// Don't show the right endpoint
if (index === values.length - 1)
return "";
return label;
}
};
}
function mkOverview(reports) {
const canvas = document.createElement("canvas");
const state = {
logaxis: false,
activeReport: null,
order: "index",
hidden: {},
legend: false,
};
const data = overviewData(state, reports);
const chart = new Chart(canvas.getContext("2d"), {
type: "horizontalBar",
data: data,
plugins: [errorBarPlugin],
options: {
onHover: overviewHover,
onClick: overviewClick(state, reports),
onResize: function (chart, size) {
if (size.width < 800) {
chart.options.scales.yAxes[0].ticks.mirror = true;
chart.options.scales.yAxes[0].ticks.padding = -10;
chart.options.scales.yAxes[0].ticks.fontColor = "#000";
} else {
chart.options.scales.yAxes[0].ticks.fontColor = "#666";
chart.options.scales.yAxes[0].ticks.mirror = false;
chart.options.scales.yAxes[0].ticks.padding = 0;
}
},
elements: {
rectangle: {
borderWidth: 2,
},
},
scales: {
yAxes: [
{
ticks: {
// make sure we draw the ticks above the error bars
z: 2,
},
},
],
xAxes: [
{
display: true,
type: axisType(state.logaxis),
ticks: {
autoSkip: false,
min: 0,
max: data.top * 1.1,
minRotation: 0,
maxRotation: 0,
callback: overviewTick(state),
},
},
],
},
responsive: true,
maintainAspectRatio: false,
legend: {
display: state.legend,
position: "right",
onLeave: function () {
chart.canvas.style.cursor = "default";
},
onHover: function () {
chart.canvas.style.cursor = "pointer";
},
onClick: function (_event, item) {
// toggle hidden
state.hidden[item.groupNumber] = !state.hidden[item.groupNumber];
renderOverview(state, reports, chart);
},
labels: {
boxWidth: 12,
generateLabels: function () {
const groups = [];
const groupNames = [];
reports.forEach(function (report) {
const index = groups.indexOf(report.groupNumber);
if (index === -1) {
groups.push(report.groupNumber);
const groupName = report.groups.slice(0, report.groups.length - 1).join(" / ");
groupNames.push(groupName);
}
});
return groups.map(function (groupNumber, index) {
const color = colors[groupNumber % colors.length];
return {
text: groupNames[index],
fillStyle: color,
hidden: state.hidden[groupNumber],
groupNumber: groupNumber,
};
});
},
},
},
tooltips: {
position: "cursor",
callbacks: {
label: function (item) {
return formatTime(item.xLabel, 3);
},
},
},
title: {
display: false,
text: "Chart.js Horizontal Bar Chart",
},
},
});
document
.getElementById("sort-overview")
.addEventListener("change", overviewSort(state, reports, chart));
const toggle = document.getElementById("legend-toggle");
toggle.addEventListener("mouseup", function () {
state.legend = !state.legend;
if (state.legend)
toggle.classList.add("right");
else
toggle.classList.remove("right");
renderOverview(state, reports, chart);
});
return canvas;
}
function mkKDE(variable, title) {
const canvas = document.createElement("canvas");
const mean = variable.mean;
const mu = variable.logMean;
const sigma = Math.sqrt(variable.logVar);
const units = rawUnits(mean);
const scale = units[0];
const numPoints = 100;
const threshold = 3;
const minVal = Math.exp(mu - threshold * sigma);
const maxVal = Math.exp(mu + threshold * sigma);
const step = (maxVal - minVal) / (numPoints - 1);
let data = [];
for (let i = 0; i < numPoints; i++) {
const x = minVal + i * step;
const y = Math.exp(-0.5 * Math.pow((Math.log(x) - mu) / sigma, 2));
data.push({ x: x * scale, y: y });
}
new Chart(canvas.getContext("2d"), {
type: "line",
data: {
datasets: [
{
label: "KDE",
borderColor: colors[0],
borderWidth: 2,
backgroundColor: "#00000000",
data: data,
hoverBorderWidth: 1,
pointHitRadius: 8,
},
{
label: "mean",
},
],
},
plugins: [
{
afterDraw: function (chart) {
const ctx = chart.ctx;
const area = chart.chartArea;
const axis = chart.scales[chart.options.scales.xAxes[0].id];
const value = axis.getPixelForValue(mean * scale);
ctx.save();
ctx.strokeStyle = colors[1];
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(value, area.top);
ctx.lineTo(value, area.bottom);
ctx.stroke();
ctx.restore();
},
},
],
options: {
title: {
display: true,
text: title + " — probability density",
},
elements: {
point: {
radius: 0,
hitRadius: 0,
},
},
scales: {
xAxes: [
{
display: true,
type: "linear",
scaleLabel: {
display: true,
labelString: variable.unit + "s",
},
ticks: {
min: minVal * scale,
max: maxVal * scale,
callback: function (value, index, values) {
// Don't show endpoints
if (index === 0 || index === values.length - 1)
return "";
const str = String(value) + " " + units[1];
return str;
},
},
},
],
yAxes: [
{
display: false,
type: "linear",
},
],
},
responsive: true,
legend: {
display: false,
position: "right",
},
tooltips: {
mode: "nearest",
callbacks: {
title: function () {
return "";
},
label: function (item) {
return formatUnit(item.xLabel, units[1], 3);
},
},
},
hover: {
intersect: false,
},
},
});
return elem("div", { className: "kde" }, [canvas]);
}
function mkScatter(measurement, title) {
const canvas = document.createElement("canvas");
const cycles = measurement.rawData.map((x) => x.cycles);
const iters = measurement.rawData.map((x) => x.iters);
const lastIter = iters[iters.length - 1];
const slope = measurement.regressionSlope;
const dataPoints = cycles.map(function (time, i) {
return {
x: iters[i],
y: time,
};
});
const units = slope.unit.split("/");
if (units.length === 1)
units.push("iteration");
title += " - " + units[0] + "s per " + units[1];
if (measurement.numMeasurements > 1)
title += " (last of " + measurement.numMeasurements + " runs)";
new Chart(canvas.getContext("2d"), {
type: "scatter",
data: {
datasets: [
{
data: dataPoints,
label: "scatter",
borderWidth: 2,
pointHitRadius: 8,
borderColor: colors[1],
backgroundColor: "#fff",
},
{
data: [
{ x: 0, y: 0 },
{ x: lastIter, y: slope.mode * lastIter },
],
label: "regression",
type: "line",
backgroundColor: "#00000000",
borderColor: colors[0],
pointRadius: 0,
},
{
data: [
{ x: 0, y: 0 },
{ x: lastIter, y: slope.lowerCI * lastIter },
],
label: "lower",
type: "line",
fill: 1,
borderWidth: 0,
pointRadius: 0,
borderColor: "#00000000",
backgroundColor: colors[0] + "33",
},
{
data: [
{ x: 0, y: 0 },
{ x: lastIter, y: slope.upperCI * lastIter },
],
label: "upper",
type: "line",
fill: 1,
borderWidth: 0,
borderColor: "#00000000",
pointRadius: 0,
backgroundColor: colors[0] + "33",
},
],
},
options: {
title: {
display: true,
text: title,
},
scales: {
yAxes: [
{
display: true,
type: "linear",
scaleLabel: {
display: false,
labelString: units[0] + "s",
},
ticks: {
callback: (value, index, values) => formatCycles(value, units[0], 3),
},
},
],
xAxes: [
{
display: true,
type: "linear",
scaleLabel: {
display: false,
labelString: units[1] + "s",
},
ticks: {
callback: iterFormatter(),
max: lastIter,
},
},
],
},
legend: {
display: false,
},
tooltips: {
callbacks: {
label: (ttitem, ttdata) =>
formatCycles(ttitem.yLabel, units[0] + "s", 3) + " / " + ttitem.xLabel.toLocaleString() + " " + units[1] + "s"
},
},
},
});
return elem("div", { className: "scatter" }, [canvas]);
}
// Create an HTML Element with attributes and child nodes
function elem(tag, props, children) {
const node = document.createElement(tag);
if (children) {
children.forEach(function (child) {
if (typeof child === "string") {
const txt = document.createTextNode(child);
node.appendChild(txt);
} else {
node.appendChild(child);
}
});
}
Object.assign(node, props);
return node;
}
function mkTable(rows) {
return elem(
"table",
{
className: "analysis",
},
[
elem("thead", {}, [
elem("tr", {}, [
elem("th"),
elem(
"th",
{
className: "low",
title: "95% lower confidence bound",
},
["lower bound"],
),
elem("th", {}, ["estimate"]),
elem(
"th",
{
className: "high",
title: "95% upper confidence bound",
},
["upper bound"],
),
]),
]),
elem(
"tbody",
{},
rows.map(function (row) {
return elem("tr", {}, [
elem("td", {}, [row.label]),
elem("td", { className: "low" }, [row.formatter(row.lowerCI, 4)]),
elem("td", {}, [row.formatter(row.mode)]),
elem("td", { className: "high" }, [row.formatter(row.upperCI, 4)]),
]);
}),
),
],
);
}
const fmtTime = t => formatTime(t, 3);
const fmtRatio = x => x.toPrecision(3) + 'x';
function fmtCyclesUnit(unit) {
return c => formatCycles(c, unit, 3);
}
function tableEntry(label, formatter, value) {
return Object.assign({ label: label, formatter: formatter }, value);
}
function mkFuncTable(report) {
const fmtCycles = fmtCyclesUnit(report.rawCycles.unit + "s");
var rows = [
tableEntry("Adjusted cycles", fmtCycles, report.adjustedCycles),
tableEntry("Adjusted time", fmtTime, report.adjustedTime),
tableEntry("Raw cycles", fmtCycles, report.rawCycles),
tableEntry("Raw time", fmtTime, report.rawTime),
];
if (report.ratio)
rows.push(tableEntry("Speedup (vs ref)", fmtRatio, report.ratio));
return mkTable(rows);
}
function mkNopTable(report) {
const fmtCycles = fmtCyclesUnit(report.nopCycles.unit + "s");
return mkTable([
tableEntry("No-op cycles", fmtCycles, report.nopCycles),
tableEntry("No-op time", fmtTime, report.nopTime),
]);
}
function mkScaleTable(report) {
const fmtScale = fmtCyclesUnit(report.timerScale.unit);
return mkTable([
tableEntry("Timer scale", fmtScale, report.timerScale),
]);
}
function slugify(groups) {
return groups
.join("_")
.toLowerCase()
.replace(/\s+/g, "_") // Replace spaces with _
.replace(/[^\w\-]+/g, ""); // Remove all non-word chars
}
function mkToggleAll(reportDiv, initialState) {
const toggleReport = elem("button", {}, [
initialState ? "Collapse All" : "Expand All"
]);
toggleReport.state = initialState;
toggleReport.addEventListener("click", () => {
toggleReport.state = !toggleReport.state;
toggleReport.textContent = toggleReport.state ? "Collapse All" : "Expand All";
reportDiv.querySelectorAll("details").forEach(function (d) {
d.open = toggleReport.state;
});
});
return toggleReport;
}
function mkCpuInfo(cpuInfo) {
return elem("div", { id: "cpu-info" }, [
elem("h2", {}, [elem("a", { href: "#cpu-info" }, ["cpu info"])]),
elem("ul", {}, cpuInfo.map(function (info) {
return elem("li", {}, [info]);
})),
]);
}
function mkConfigInfo(config) {
const prettyNames = {
testPattern: "Test pattern",
functionPattern: "Function pattern",
benchUsec: "Bench duration (µs)",
seed: "Random seed",
repeat: "Repeat count",
cpuAffinity: "CPU affinity",
};
var items = [];
Object.entries(config).forEach(function ([key, value]) {
const label = prettyNames[key] || key;
items.push(elem("li", {}, [
elem("em", {}, [label + ": "]),
String(value),
]));
});
return elem("details", { id: "configuration", className: "report-details" }, [
elem("summary", {}, ["configuration"]),
elem("p", {}, [elem("ul", {}, items)]),
]);
}
document.addEventListener(
"DOMContentLoaded",
function () {
const reportJSON = JSON.parse(document.getElementById("report-data").text);
const reportData = processReports(reportJSON);
const version = "checkasm v" + reportJSON.checkasmVersion;
document.getElementById("checkasm-version").textContent = version;
const overview = document.getElementById("overview-charts");
const reports = document.getElementById("reports");
const overviewLineHeight = 16 * 1.25;
if (reportJSON.cpuInfo)
overview.appendChild(mkCpuInfo(reportJSON.cpuInfo));
Object.entries(reportData).forEach(([testName, testData], testNumber) => {
const tid = slugify([testName]);
const testOverview = elem("div", { id: tid }, [
elem("h2", {}, [elem("a", { href: "#" + tid }, [testName])]),
]);
overview.appendChild(testOverview);
Object.entries(testData).forEach(([reportName, report]) => {
const height = overviewLineHeight * report.numVersions + 36;
testOverview.appendChild(
elem("details", { class: "group-summary", open }, [
elem("summary", {}, [reportName]),
elem("p", { style: "height: " + String(height) + "px" }, [
mkOverview(
Object.values(report.functions).flatMap((f) => Object.values(f.versions)),
),
]),
]),
);
const rid = slugify([testName, reportName]);
const reportDiv = elem("div", { id: rid }, [
elem("h1", {}, [elem("a", { href: "#" + rid }, [[testName, reportName].join(" / ")])]),
]);
reportDiv.appendChild(mkToggleAll(reportDiv, false)),
reports.appendChild(reportDiv);
Object.entries(report.functions).forEach(([funcName, func]) => {
const body = elem("p", {}, []);
const details = elem("details", { className: "report-details" }, [
elem("summary", {}, [funcName]), body
]);
reportDiv.appendChild(details);
/* Generate details lazily on demand */
details.addEventListener("toggle", () => {
if (details.open && body.childElementCount === 0) {
Object.values(func.versions).forEach((version) => {
const title = version.groups.join(" / ");
const id = slugify([testName, reportName, version.reportName]);
body.appendChild(elem("h3", { id: id }, [
elem("a", { href: "#" + id }, [version.reportName])
]));
body.appendChild(mkKDE(version.rawCycles, title));
if (version.rawCycles.rawData)
body.appendChild(mkScatter(version.rawCycles, title));
body.appendChild(mkFuncTable(version));
});
}
});
});
});
});
const internalDiv = elem("div", { id: "checkasm-internal" }, [
elem("h1", {}, [elem("a", { href: "#checkasm-internal" }, ["checkasm / internal"])]),
]);
reports.appendChild(internalDiv);
internalDiv.appendChild(mkToggleAll(internalDiv, false));
internalDiv.appendChild(mkConfigInfo(reportJSON.config));
internalDiv.appendChild(elem("details", { className: "report-details" }, [
elem("summary", {}, ["no-op"]),
elem("p", {}, [
mkKDE(reportJSON.nopCycles, "no-op"),
mkScatter(reportJSON.nopCycles, "no-op"),
mkNopTable(reportJSON)
]),
]));
internalDiv.appendChild(elem("details", { className: "report-details" }, [
elem("summary", {}, ["timer scale"]),
elem("p", {}, [
mkKDE(reportJSON.timerScale, "timer scale"),
mkScatter(reportJSON.timerScale, "timer scale"),
mkScaleTable(reportJSON)
]),
]));
const overviewDetails = overview.querySelectorAll("details");
const toggleOverview = document.getElementById("toggleOverview");
toggleOverview.state = true;
toggleOverview.addEventListener("click", () => {
toggleOverview.state = !toggleOverview.state;
toggleOverview.textContent = toggleOverview.state ? "Collapse All" : "Expand All";
overviewDetails.forEach((d) => (d.open = toggleOverview.state));
});
},
false,
);
})();