70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
# /// script
|
||
# requires-python = ">=3.14"
|
||
# dependencies = ["matplotlib"]
|
||
# ///
|
||
"""Bar chart of cold-parse throughput for the README's Performance section.
|
||
|
||
Numbers are pasted from `uv run scripts/bench.py` (the cold 50/50
|
||
browser/crawler mix, µs per parse) and shown as parses per second.
|
||
Transparent SVG, text baked to paths, neutral grays: renders the same
|
||
on light and dark themes. All labels sit on the bars themselves.
|
||
Very wide aspect ratio: forges render images at full content width,
|
||
so height alone controls how tall it appears.
|
||
|
||
uv run scripts/speedplot.py
|
||
"""
|
||
|
||
from pathlib import Path
|
||
|
||
import matplotlib
|
||
|
||
matplotlib.use("Agg")
|
||
matplotlib.rcParams["svg.fonttype"] = "path" # text as paths: renders anywhere
|
||
import matplotlib.pyplot as plt
|
||
|
||
# µs per cold parse, from scripts/bench.py.
|
||
US = {
|
||
"user-agent-parser": 8.2,
|
||
"uarite": 17.7,
|
||
"ua-parser (Rust)": 46.6,
|
||
"ua-parser (RE2)": 77.1,
|
||
"ua-parser": 321.5,
|
||
"user-agents": 334.4,
|
||
}
|
||
|
||
#: Readable on both white and dark backgrounds.
|
||
OUTSIDE = "#767676"
|
||
|
||
data = sorted(((n, 1e6 / us) for n, us in US.items()), key=lambda t: -t[1])
|
||
names = [n for n, _ in data][::-1]
|
||
values = [v for _, v in data][::-1]
|
||
colors = ["#6e6e6e"] * len(data)
|
||
colors[names.index("uarite")] = "#2b6cb0"
|
||
|
||
fig, ax = plt.subplots(figsize=(12, 1.7), dpi=100)
|
||
bars = ax.barh(names, values, color=colors, height=0.82)
|
||
ax.set_xlim(0, max(values))
|
||
ax.axis("off")
|
||
|
||
for bar, name, v in zip(bars, names, values):
|
||
# Round to two significant digits: 121951 -> "120 000".
|
||
rounded = round(v, 1 - int(f"{v:.0e}".split("e")[1]))
|
||
label = f"{name} {rounded:,.0f}".replace(",", " ")
|
||
# va="center" centers the font bbox incl. descender space, which leaves
|
||
# the glyphs slightly high; nudge down to optically center on the bar.
|
||
y = bar.get_y() + bar.get_height() / 2 - 0.09
|
||
if bar.get_width() > max(values) * 0.28:
|
||
# Long bar: white text inside, right-aligned at the bar end.
|
||
ax.text(bar.get_width() - max(values) * 0.012, y, label,
|
||
va="center", ha="right", color="white", fontsize=11)
|
||
else:
|
||
# Short bar: theme-neutral gray text just past the bar end.
|
||
ax.text(bar.get_width() + max(values) * 0.012, y, label,
|
||
va="center", color=OUTSIDE, fontsize=11)
|
||
|
||
fig.tight_layout(pad=0.2)
|
||
out = Path("docs/bench-speed.svg")
|
||
out.parent.mkdir(exist_ok=True)
|
||
fig.savefig(out, transparent=True)
|
||
print("wrote", out)
|