Skip to content

Commit a913db0

Browse files
feat: add stacked area chart for memory breakdown visualization
Add renderStackedAreaGraph function for proper stacked area charts, triggered by extra: "stacked-area:GROUP_NAME". Unlike the existing overlaid line charts (stacked:), area charts show both individual contributions and total composition, with a tooltip showing the sum. Used by the new --memory_profile_out memory profiling in barretenberg to visualize polynomial memory breakdown by category over time.
1 parent a38503f commit a913db0

1 file changed

Lines changed: 194 additions & 4 deletions

File tree

bench/index.html

Lines changed: 194 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,170 @@
904904
}
905905
}
906906

907+
// Renders a stacked area chart where each series fills from the previous,
908+
// showing both individual contributions and total composition.
909+
function renderStackedAreaGraph(parent, name, stackedItems, fullKey) {
910+
const isDark = document.body.classList.contains("dark-theme");
911+
const textColor = isDark ? "#e0e0e0" : "#4a4a4a";
912+
913+
let grid = parent.querySelector(".benchmark-graphs");
914+
if (!grid) {
915+
grid = document.createElement("div");
916+
grid.className = "benchmark-graphs";
917+
parent.appendChild(grid);
918+
}
919+
const container = document.createElement("div");
920+
container.className = "chart-container";
921+
container.style.gridColumn = "1 / -1";
922+
container.style.minHeight = "400px";
923+
grid.appendChild(container);
924+
925+
const canvas = document.createElement("canvas");
926+
canvas.className = "benchmark-chart";
927+
container.appendChild(canvas);
928+
929+
// Collect all unique commits across all component series
930+
const commitOrder = [];
931+
const commitSet = new Set();
932+
const commitInfoMap = new Map();
933+
stackedItems.forEach((item) => {
934+
item.benches.forEach((entry) => {
935+
if (!commitSet.has(entry.commit.id)) {
936+
commitSet.add(entry.commit.id);
937+
commitOrder.push(entry.commit.id);
938+
commitInfoMap.set(entry.commit.id, entry.commit);
939+
}
940+
});
941+
});
942+
943+
const labels = commitOrder.map((id) => id.slice(0, 7));
944+
945+
// Stacked area colors (semi-opaque for fill)
946+
const colors = [
947+
"#e24a4a", "#4a90e2", "#2ecc71", "#e2c94a", "#9b59b6",
948+
"#1abc9c", "#e67e22", "#3498db", "#e74c3c", "#27ae60",
949+
"#f39c12", "#8e44ad", "#16a085", "#d35400", "#2980b9",
950+
"#c0392b", "#f1c40f", "#7d3c98", "#148f77", "#d68910",
951+
];
952+
953+
// Sort alphabetically (largest categories tend to be named first)
954+
const sorted = [...stackedItems].sort((a, b) => {
955+
const aName = a.fullKey.split("/").pop();
956+
const bName = b.fullKey.split("/").pop();
957+
return aName.localeCompare(bName);
958+
});
959+
960+
const datasets = sorted.map((item, i) => {
961+
const color = colors[i % colors.length];
962+
const metricName = item.fullKey.split("/").pop();
963+
const valueMap = new Map();
964+
item.benches.forEach((entry) => {
965+
valueMap.set(entry.commit.id, entry.bench.value);
966+
});
967+
const data = commitOrder.map((id) => valueMap.get(id) ?? 0);
968+
return {
969+
label: metricName,
970+
data,
971+
borderColor: color,
972+
backgroundColor: color + "80",
973+
borderWidth: 1,
974+
pointRadius: 1,
975+
fill: true,
976+
};
977+
});
978+
979+
const unit = stackedItems[0]?.benches[0]?.bench?.unit || "MB";
980+
981+
const chart = new Chart(canvas, {
982+
type: "line",
983+
data: { labels, datasets },
984+
options: {
985+
responsive: true,
986+
title: {
987+
display: true,
988+
text: name,
989+
fontColor: textColor,
990+
fontSize: 14,
991+
},
992+
legend: {
993+
display: true,
994+
position: "bottom",
995+
labels: {
996+
fontColor: textColor,
997+
fontSize: 11,
998+
padding: 12,
999+
usePointStyle: true,
1000+
},
1001+
},
1002+
scales: {
1003+
xAxes: [
1004+
{
1005+
scaleLabel: {
1006+
display: true,
1007+
labelString: "commit",
1008+
fontColor: textColor,
1009+
},
1010+
ticks: { fontColor: textColor },
1011+
},
1012+
],
1013+
yAxes: [
1014+
{
1015+
stacked: true,
1016+
scaleLabel: {
1017+
display: true,
1018+
labelString: unit,
1019+
fontColor: textColor,
1020+
},
1021+
ticks: { beginAtZero: true, fontColor: textColor },
1022+
},
1023+
],
1024+
},
1025+
tooltips: {
1026+
mode: "index",
1027+
intersect: false,
1028+
callbacks: {
1029+
title: (tooltipItems) => {
1030+
if (tooltipItems.length > 0) {
1031+
const idx = tooltipItems[0].index;
1032+
const commitId = commitOrder[idx];
1033+
const commit = commitInfoMap.get(commitId);
1034+
return commit
1035+
? commitId.slice(0, 7) + " - " + commit.message
1036+
: commitId.slice(0, 7);
1037+
}
1038+
return "";
1039+
},
1040+
label: (item) => {
1041+
const dsLabel = datasets[item.datasetIndex].label;
1042+
return " " + dsLabel + ": " + item.value + " " + unit;
1043+
},
1044+
afterBody: (tooltipItems) => {
1045+
const total = tooltipItems.reduce(
1046+
(sum, item) => sum + parseFloat(item.value || 0),
1047+
0
1048+
);
1049+
return " Total: " + total.toFixed(1) + " " + unit;
1050+
},
1051+
},
1052+
},
1053+
onClick: (_mouseEvent, activeElems) => {
1054+
if (activeElems.length === 0) return;
1055+
const index = activeElems[0]._index;
1056+
const commitId = commitOrder[index];
1057+
const commit = commitInfoMap.get(commitId);
1058+
if (commit && commit.url) {
1059+
window.open(commit.url, "_blank");
1060+
}
1061+
},
1062+
},
1063+
});
1064+
1065+
window.chartInstances.push(chart);
1066+
if (fullKey) {
1067+
window.chartsByBenchName.set(fullKey, chart);
1068+
}
1069+
}
1070+
9071071
function renderBenchSet(name, benchSet, main) {
9081072
const setElem = document.createElement("div");
9091073
setElem.className = "benchmark-set";
@@ -929,14 +1093,23 @@
9291093
}
9301094

9311095
// Detect and consolidate stacked chart entries.
932-
// Entries with extra: "stacked:GROUP_NAME" are grouped into a single stacked chart.
1096+
// Entries with extra: "stacked:GROUP_NAME" are grouped into overlaid line charts.
1097+
// Entries with extra: "stacked-area:GROUP_NAME" are grouped into stacked area charts.
9331098
const stackedGroups = new Map();
1099+
const stackedAreaGroups = new Map();
9341100
const regularItems = [];
9351101
items.forEach((item) => {
9361102
const latestBench = item.benches[item.benches.length - 1]?.bench;
9371103
const extra = latestBench?.extra || "";
1104+
const stackedAreaMatch = extra.match(/^stacked-area:(.+)$/);
9381105
const stackedMatch = extra.match(/^stacked:(.+)$/);
939-
if (stackedMatch) {
1106+
if (stackedAreaMatch) {
1107+
const groupName = stackedAreaMatch[1];
1108+
if (!stackedAreaGroups.has(groupName)) {
1109+
stackedAreaGroups.set(groupName, []);
1110+
}
1111+
stackedAreaGroups.get(groupName).push(item);
1112+
} else if (stackedMatch) {
9401113
const groupName = stackedMatch[1];
9411114
if (!stackedGroups.has(groupName)) {
9421115
stackedGroups.set(groupName, []);
@@ -947,7 +1120,7 @@
9471120
}
9481121
});
9491122

950-
// Add consolidated stacked chart entries
1123+
// Add consolidated stacked line chart entries
9511124
stackedGroups.forEach((groupItems, groupName) => {
9521125
const group = groupItems[0].group;
9531126
const parts = groupName.split("/");
@@ -961,6 +1134,20 @@
9611134
});
9621135
});
9631136

1137+
// Add consolidated stacked area chart entries
1138+
stackedAreaGroups.forEach((groupItems, groupName) => {
1139+
const group = groupItems[0].group;
1140+
const parts = groupName.split("/");
1141+
const chartName = parts[parts.length - 1];
1142+
regularItems.push({
1143+
group,
1144+
chartName,
1145+
benches: null,
1146+
fullKey: groupName,
1147+
stackedAreaItems: groupItems,
1148+
});
1149+
});
1150+
9641151
// Build hierarchical tree from group paths.
9651152
const tree = new Map();
9661153
regularItems.forEach((item) => {
@@ -976,6 +1163,7 @@
9761163
benches: item.benches,
9771164
fullKey: item.fullKey,
9781165
stackedItems: item.stackedItems || null,
1166+
stackedAreaItems: item.stackedAreaItems || null,
9791167
});
9801168
}
9811169
current = current.get(part).children;
@@ -1078,7 +1266,9 @@
10781266

10791267
// Render charts for this node.
10801268
value.charts.forEach((item) => {
1081-
if (item.stackedItems) {
1269+
if (item.stackedAreaItems) {
1270+
renderStackedAreaGraph(groupContent, item.chartName, item.stackedAreaItems, item.fullKey);
1271+
} else if (item.stackedItems) {
10821272
renderStackedGraph(groupContent, item.chartName, item.stackedItems, item.fullKey);
10831273
} else {
10841274
renderGraph(groupContent, item.chartName, item.benches, item.fullKey);

0 commit comments

Comments
 (0)