new diagrams in benchmarks

This commit is contained in:
2026-02-13 14:42:59 +03:00
parent 1023d05419
commit b2f5a1b163
4 changed files with 250 additions and 125 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ def main() -> None:
lambda command,
description: f"[bold cyan]▸[/bold cyan] [bold white]{command}[/bold white] [dim]│[/dim] [yellow italic]{description}[/yellow italic]"
)
# orchestrator.run_repl(app)
orchestrator.run_repl(app)
if __name__ == "__main__":
+55 -124
View File
@@ -2,8 +2,9 @@ __all__ = ["DiagramGenerator"]
from pathlib import Path
import matplotlib
import matplotlib.pyplot as plt
import cairosvg
import pygal
from pygal.style import Style
from ..benchmarks.core.models import BenchmarkGroupResult
@@ -12,8 +13,26 @@ class DiagramGenerator:
def __init__(self, output_dir: Path | str) -> None:
self.output_dir: Path = Path(output_dir) if isinstance(output_dir, str) else output_dir
matplotlib.use("Agg")
plt.style.use("seaborn-v0_8-whitegrid")
self._style = Style(
background="white",
plot_background="white",
foreground="#2c3e50",
foreground_strong="#000000",
foreground_subtle="#7f8c8d",
opacity=".9",
opacity_hover=".95",
transition="150ms ease-in",
colors=("#2ecc71", "#3498db", "#e74c3c"),
title_font_size=40,
legend_font_size=34,
label_font_size=32, #
major_label_font_size=32,
value_font_size=28,
value_label_font_size=28,
tooltip_font_size=24,
no_data_font_size=28,
font_family="Consolas, 'Courier New', monospace",
)
def generate_comparison_diagram(self, benchmark_group: BenchmarkGroupResult) -> Path:
results = benchmark_group.benchmark_results
@@ -31,132 +50,44 @@ class DiagramGenerator:
)
y_limit = max_value / 0.85 if max_value > 0 else 1.0
items_count = len(descriptions)
x_positions: list[int] = list(range(items_count))
bar_width = 0.25
x_std_dev = [x - bar_width for x in x_positions]
x_avg = [x for x in x_positions]
x_median = [x + bar_width for x in x_positions]
fig, ax = plt.subplots(figsize=(16, 8))
fig.patch.set_facecolor("white")
bars_std = ax.bar(
x_std_dev,
std_devs,
bar_width,
label="Std Deviation",
color="#2ecc71",
alpha=0.9,
edgecolor="#27ae60",
linewidth=1.5,
)
bars_avg = ax.bar(
x_avg,
avg_times,
bar_width,
label="Average Time",
color="#3498db",
alpha=0.9,
edgecolor="#2980b9",
linewidth=1.5,
)
bars_median = ax.bar(
x_median,
median_times,
bar_width,
label="Median Time",
color="#e74c3c",
alpha=0.9,
edgecolor="#c0392b",
linewidth=1.5,
)
for bar_group in [bars_std, bars_avg, bars_median]:
for bar in bar_group:
height = bar.get_height()
ax.text(
bar.get_x() + bar.get_width() / 2.0,
height,
f"{height:.3f}",
ha="center",
va="bottom",
fontsize=9,
fontweight="bold",
)
ax.set_ylabel("Time (ms)", fontsize=14, fontweight="bold", labelpad=10)
title_text = f"{benchmark_group.type_.replace('_', ' ').title()}"
metadata_text = f"Iterations: {benchmark_group.iterations} | GC: {'Disabled' if benchmark_group.is_gc_disabled else 'Enabled'}"
ax.text(
0.5,
1.08,
title_text,
transform=ax.transAxes,
fontsize=18,
fontweight="bold",
ha="center",
color="#2c3e50",
metadata_text = (
f"Iterations: {benchmark_group.iterations} | GC: "
f"{'Disabled' if benchmark_group.is_gc_disabled else 'Enabled'}"
)
ax.text(
0.5,
1.03,
metadata_text,
transform=ax.transAxes,
fontsize=12,
ha="center",
color="#7f8c8d",
style="italic",
)
ax.set_xticks(x_positions)
ax.set_xticklabels([])
for i, (pos, desc) in enumerate(zip(x_positions, descriptions)):
text_x_pos = pos - bar_width - (bar_width / 2)
ax.text(
text_x_pos,
y_limit * 0.02,
desc,
rotation=90,
va="bottom",
ha="right",
fontsize=10,
color="#2c3e50",
)
ax.set_ylim(0, y_limit)
legend = ax.legend(
loc="upper left",
fontsize=12,
framealpha=0.95,
edgecolor="#34495e",
fancybox=True,
shadow=True,
)
legend.get_frame().set_facecolor("#ecf0f1")
ax.grid(axis="y", alpha=0.4, linestyle="--", linewidth=0.8)
ax.set_axisbelow(True)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_color("#7f8c8d")
ax.spines["bottom"].set_color("#7f8c8d")
plt.tight_layout()
filename = f"{benchmark_group.type_}_comparison.png"
output_path = self.output_dir / filename
self.output_dir.mkdir(parents=True, exist_ok=True)
plt.savefig(output_path, dpi=200, bbox_inches="tight", facecolor="white")
plt.close(fig)
dynamic_height = 600 + (len(descriptions) * 150)
chart = pygal.HorizontalBar(
style=self._style,
width=3100,
height=dynamic_height,
explicit_size=True,
show_legend=True,
legend_at_bottom=True,
print_values=True,
print_values_position="top",
legend_at_bottom_columns=3,
range=(0, y_limit),
zero=0,
)
chart.title = f"{title_text}\n{metadata_text}"
chart.x_title = "Time (ms)"
chart.no_data_text = "No data"
chart.value_formatter = lambda x: f"{x:.3f}"
chart.x_labels = descriptions
chart.add("Std Deviation", std_devs)
chart.add("Average Time", avg_times)
chart.add("Median Time", median_times)
svg_bytes = chart.render()
cairosvg.svg2png(bytestring=svg_bytes, write_to=str(output_path))
return output_path