Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 194 additions & 4 deletions bench/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,170 @@
}
}

// Renders a stacked area chart where each series fills from the previous,
// showing both individual contributions and total composition.
function renderStackedAreaGraph(parent, name, stackedItems, fullKey) {
const isDark = document.body.classList.contains("dark-theme");
const textColor = isDark ? "#e0e0e0" : "#4a4a4a";

let grid = parent.querySelector(".benchmark-graphs");
if (!grid) {
grid = document.createElement("div");
grid.className = "benchmark-graphs";
parent.appendChild(grid);
}
const container = document.createElement("div");
container.className = "chart-container";
container.style.gridColumn = "1 / -1";
container.style.minHeight = "400px";
grid.appendChild(container);

const canvas = document.createElement("canvas");
canvas.className = "benchmark-chart";
container.appendChild(canvas);

// Collect all unique commits across all component series
const commitOrder = [];
const commitSet = new Set();
const commitInfoMap = new Map();
stackedItems.forEach((item) => {
item.benches.forEach((entry) => {
if (!commitSet.has(entry.commit.id)) {
commitSet.add(entry.commit.id);
commitOrder.push(entry.commit.id);
commitInfoMap.set(entry.commit.id, entry.commit);
}
});
});

const labels = commitOrder.map((id) => id.slice(0, 7));

// Stacked area colors (semi-opaque for fill)
const colors = [
"#e24a4a", "#4a90e2", "#2ecc71", "#e2c94a", "#9b59b6",
"#1abc9c", "#e67e22", "#3498db", "#e74c3c", "#27ae60",
"#f39c12", "#8e44ad", "#16a085", "#d35400", "#2980b9",
"#c0392b", "#f1c40f", "#7d3c98", "#148f77", "#d68910",
];

// Sort alphabetically (largest categories tend to be named first)
const sorted = [...stackedItems].sort((a, b) => {
const aName = a.fullKey.split("/").pop();
const bName = b.fullKey.split("/").pop();
return aName.localeCompare(bName);
});

const datasets = sorted.map((item, i) => {
const color = colors[i % colors.length];
const metricName = item.fullKey.split("/").pop();
const valueMap = new Map();
item.benches.forEach((entry) => {
valueMap.set(entry.commit.id, entry.bench.value);
});
const data = commitOrder.map((id) => valueMap.get(id) ?? 0);
return {
label: metricName,
data,
borderColor: color,
backgroundColor: color + "80",
borderWidth: 1,
pointRadius: 1,
fill: true,
};
});

const unit = stackedItems[0]?.benches[0]?.bench?.unit || "MB";

const chart = new Chart(canvas, {
type: "line",
data: { labels, datasets },
options: {
responsive: true,
title: {
display: true,
text: name,
fontColor: textColor,
fontSize: 14,
},
legend: {
display: true,
position: "bottom",
labels: {
fontColor: textColor,
fontSize: 11,
padding: 12,
usePointStyle: true,
},
},
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: "commit",
fontColor: textColor,
},
ticks: { fontColor: textColor },
},
],
yAxes: [
{
stacked: true,
scaleLabel: {
display: true,
labelString: unit,
fontColor: textColor,
},
ticks: { beginAtZero: true, fontColor: textColor },
},
],
},
tooltips: {
mode: "index",
intersect: false,
callbacks: {
title: (tooltipItems) => {
if (tooltipItems.length > 0) {
const idx = tooltipItems[0].index;
const commitId = commitOrder[idx];
const commit = commitInfoMap.get(commitId);
return commit
? commitId.slice(0, 7) + " - " + commit.message
: commitId.slice(0, 7);
}
return "";
},
label: (item) => {
const dsLabel = datasets[item.datasetIndex].label;
return " " + dsLabel + ": " + item.value + " " + unit;
},
afterBody: (tooltipItems) => {
const total = tooltipItems.reduce(
(sum, item) => sum + parseFloat(item.value || 0),
0
);
return " Total: " + total.toFixed(1) + " " + unit;
},
},
},
onClick: (_mouseEvent, activeElems) => {
if (activeElems.length === 0) return;
const index = activeElems[0]._index;
const commitId = commitOrder[index];
const commit = commitInfoMap.get(commitId);
if (commit && commit.url) {
window.open(commit.url, "_blank");
}
},
},
});

window.chartInstances.push(chart);
if (fullKey) {
window.chartsByBenchName.set(fullKey, chart);
}
}

function renderBenchSet(name, benchSet, main) {
const setElem = document.createElement("div");
setElem.className = "benchmark-set";
Expand All @@ -929,14 +1093,23 @@
}

// Detect and consolidate stacked chart entries.
// Entries with extra: "stacked:GROUP_NAME" are grouped into a single stacked chart.
// Entries with extra: "stacked:GROUP_NAME" are grouped into overlaid line charts.
// Entries with extra: "stacked-area:GROUP_NAME" are grouped into stacked area charts.
const stackedGroups = new Map();
const stackedAreaGroups = new Map();
const regularItems = [];
items.forEach((item) => {
const latestBench = item.benches[item.benches.length - 1]?.bench;
const extra = latestBench?.extra || "";
const stackedAreaMatch = extra.match(/^stacked-area:(.+)$/);
const stackedMatch = extra.match(/^stacked:(.+)$/);
if (stackedMatch) {
if (stackedAreaMatch) {
const groupName = stackedAreaMatch[1];
if (!stackedAreaGroups.has(groupName)) {
stackedAreaGroups.set(groupName, []);
}
stackedAreaGroups.get(groupName).push(item);
} else if (stackedMatch) {
const groupName = stackedMatch[1];
if (!stackedGroups.has(groupName)) {
stackedGroups.set(groupName, []);
Expand All @@ -947,7 +1120,7 @@
}
});

// Add consolidated stacked chart entries
// Add consolidated stacked line chart entries
stackedGroups.forEach((groupItems, groupName) => {
const group = groupItems[0].group;
const parts = groupName.split("/");
Expand All @@ -961,6 +1134,20 @@
});
});

// Add consolidated stacked area chart entries
stackedAreaGroups.forEach((groupItems, groupName) => {
const group = groupItems[0].group;
const parts = groupName.split("/");
const chartName = parts[parts.length - 1];
regularItems.push({
group,
chartName,
benches: null,
fullKey: groupName,
stackedAreaItems: groupItems,
});
});

// Build hierarchical tree from group paths.
const tree = new Map();
regularItems.forEach((item) => {
Expand All @@ -976,6 +1163,7 @@
benches: item.benches,
fullKey: item.fullKey,
stackedItems: item.stackedItems || null,
stackedAreaItems: item.stackedAreaItems || null,
});
}
current = current.get(part).children;
Expand Down Expand Up @@ -1078,7 +1266,9 @@

// Render charts for this node.
value.charts.forEach((item) => {
if (item.stackedItems) {
if (item.stackedAreaItems) {
renderStackedAreaGraph(groupContent, item.chartName, item.stackedAreaItems, item.fullKey);
} else if (item.stackedItems) {
renderStackedGraph(groupContent, item.chartName, item.stackedItems, item.fullKey);
} else {
renderGraph(groupContent, item.chartName, item.benches, item.fullKey);
Expand Down
Loading