Files
andros fb45f6a582 README: replace mermaid xychart with grouped-bar images
Mermaid's xychart-beta cannot draw grouped bars or a legend: two bar
series overlap on the same axis position, so the paired GPU-vs-cairo
comparison was unreadable. Render the charts as images with real
grouped bars, value labels and a legend (bench/make-charts.py), and
embed them from .github/assets/.
2026-07-24 11:38:01 +02:00

95 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""Render the benchmark charts as grouped-bar images.
Mermaid's xychart-beta cannot draw grouped (side-by-side) bars or a
legend, so the README embeds these generated images instead. Run this
after updating the numbers in the README tables:
python3 bench/make-charts.py
Output PNGs go to .github/assets/.
"""
import os
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
GPU = "#43b97f"
CAIRO = "#4f81e5"
COCOA = "#4f81e5"
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "..", ".github", "assets")
WORKLOADS = ["Line scroll", "Page scroll", "Full redraw", "Typing", "Image scroll"]
# (filename, title, gpu values, cairo values, x-axis label)
PAIRED = [
("bench-linux-1616.png",
"1616x912 frame: redisplays per second (higher is better)",
[487, 296, 294, 1311, 1239],
[530, 297, 247, 1857, 1359]),
("bench-linux-4k.png",
"4K frame: redisplays per second (higher is better)",
[240, 124, 121, 1766, 1328],
[117, 102, 66, 238, 115]),
]
def paired_chart(fname, title, gpu, cairo):
fig, ax = plt.subplots(figsize=(9, 4.5), dpi=140)
y = range(len(WORKLOADS))
h = 0.38
# GPU on top of each pair, cairo just below.
ax.barh([i + h / 2 for i in y], gpu, height=h, color=GPU, label="GPU (OpenGL)")
ax.barh([i - h / 2 for i in y], cairo, height=h, color=CAIRO, label="Stock (cairo)")
ax.set_yticks(list(y))
ax.set_yticklabels(WORKLOADS)
ax.invert_yaxis()
ax.set_xlabel("redisplays per second")
ax.set_title(title)
ax.legend(loc="lower right", frameon=False)
ax.spines[["top", "right"]].set_visible(False)
top = max(max(gpu), max(cairo))
ax.set_xlim(0, top * 1.12)
for i in y:
ax.text(gpu[i] + top * 0.01, i + h / 2, str(gpu[i]),
va="center", ha="left", fontsize=8, color="#2a2a2a")
ax.text(cairo[i] + top * 0.01, i - h / 2, str(cairo[i]),
va="center", ha="left", fontsize=8, color="#2a2a2a")
fig.tight_layout()
fig.savefig(os.path.join(OUT, fname), facecolor="white")
plt.close(fig)
print("wrote", fname)
def macos_chart():
fname = "bench-macos-cpu.png"
labels = ["Stock (Cocoa)", "GPU vsync on", "GPU vsync off"]
vals = [16.0, 10.5, 15.5]
colors = [COCOA, GPU, GPU]
fig, ax = plt.subplots(figsize=(9, 3.0), dpi=140)
y = range(len(labels))
ax.barh(list(y), vals, height=0.5, color=colors)
ax.set_yticks(list(y))
ax.set_yticklabels(labels)
ax.invert_yaxis()
ax.set_xlabel("CPU seconds")
ax.set_title("M1 Pro: CPU to render 15 s of flat-out scroll (lower is better)")
ax.spines[["top", "right"]].set_visible(False)
ax.set_xlim(0, max(vals) * 1.12)
for i in y:
ax.text(vals[i] + max(vals) * 0.01, i, f"{vals[i]:.1f} s",
va="center", ha="left", fontsize=8, color="#2a2a2a")
fig.tight_layout()
fig.savefig(os.path.join(OUT, fname), facecolor="white")
plt.close(fig)
print("wrote", fname)
if __name__ == "__main__":
os.makedirs(OUT, exist_ok=True)
for args in PAIRED:
paired_chart(*args)
macos_chart()