diff --git a/bench.sh b/bench.sh
index 0c6c012d3..614daa203 100755
--- a/bench.sh
+++ b/bench.sh
@@ -167,6 +167,7 @@ for i in "${!benchmark_names[@]}"; do
# Run multiple iterations and collect timing data
iteration_results=()
+ iteration_pass_data=()
for ((iter=1; iter<=ITERATIONS; iter++)); do
output_binary="$TEMP_DIR/bench_output_$$"
@@ -180,6 +181,8 @@ for i in "${!benchmark_names[@]}"; do
total_ms=$(echo "$timing_json" | grep -o '"total_ms":[0-9.]*' | head -1 | cut -d: -f2)
if [[ -n "$total_ms" ]]; then
iteration_results+=("$total_ms")
+ # Store full JSON for pass data extraction
+ iteration_pass_data+=("$timing_json")
fi
rm -f "$output_binary"
@@ -190,7 +193,7 @@ for i in "${!benchmark_names[@]}"; do
continue
fi
- # Calculate mean and stddev
+ # Calculate mean and stddev for total time
sum=0
for val in "${iteration_results[@]}"; do
sum=$(echo "$sum + $val" | bc -l)
@@ -210,8 +213,36 @@ for i in "${!benchmark_names[@]}"; do
log_info " $name: mean=${mean}ms, std=${stddev}ms (n=$count)"
- # Store result for later
- all_results+=("{\"name\":\"$name\",\"iterations\":$count,\"mean_ms\":$mean,\"std_ms\":$stddev}")
+ # Extract and aggregate per-pass timing data
+ # Use Python to parse JSON and compute per-pass means
+ pass_json=$(python3 -c "
+import json
+import sys
+
+pass_data = {}
+for json_str in sys.argv[1:]:
+ try:
+ data = json.loads(json_str)
+ for p in data.get('passes', []):
+ name = p['name']
+ duration = p['duration_ms']
+ if name not in pass_data:
+ pass_data[name] = []
+ pass_data[name].append(duration)
+ except:
+ pass
+
+# Calculate means
+result = {}
+for name, durations in pass_data.items():
+ mean = sum(durations) / len(durations) if durations else 0
+ result[name] = {'mean_ms': round(mean, 3)}
+
+print(json.dumps(result))
+" "${iteration_pass_data[@]}" 2>/dev/null || echo "{}")
+
+ # Store result with pass data
+ all_results+=("{\"name\":\"$name\",\"iterations\":$count,\"mean_ms\":$mean,\"std_ms\":$stddev,\"passes\":$pass_json}")
done
# Get metadata
diff --git a/scripts/generate-charts.py b/scripts/generate-charts.py
index e5a8dc41d..08abb77a2 100755
--- a/scripts/generate-charts.py
+++ b/scripts/generate-charts.py
@@ -209,8 +209,34 @@ def scale_y(v: float) -> float:
return "\n".join(svg_parts)
-def generate_breakdown_chart(runs: list[dict]) -> str:
- """Generate stacked bar chart showing time per compiler pass."""
+def get_benchmark_names(runs: list[dict]) -> list[str]:
+ """Get list of all benchmark names from runs."""
+ names = set()
+ for run in runs:
+ for bench in run.get("benchmarks", []):
+ if "name" in bench:
+ names.add(bench["name"])
+ return sorted(names)
+
+
+def get_pass_times_for_benchmark(run: dict, benchmark_name: str) -> dict[str, float]:
+ """Extract pass timing for a specific benchmark from a run."""
+ for bench in run.get("benchmarks", []):
+ if bench.get("name") == benchmark_name and "passes" in bench:
+ passes = bench["passes"]
+ return {
+ name: passes.get(name, {}).get("mean_ms", 0)
+ for name in PASS_ORDER
+ }
+ return {}
+
+
+def generate_breakdown_chart(runs: list[dict], benchmark_name: Optional[str] = None) -> str:
+ """Generate stacked bar chart showing time per compiler pass.
+
+ If benchmark_name is provided, shows data for that specific benchmark.
+ Otherwise, shows aggregate data across all benchmarks.
+ """
if not runs:
return generate_empty_chart(BREAKDOWN_WIDTH, BREAKDOWN_HEIGHT, "No benchmark data available yet")
@@ -218,7 +244,10 @@ def generate_breakdown_chart(runs: list[dict]) -> str:
pass_times: Optional[dict[str, float]] = None
commit = ""
for run in reversed(runs):
- pt = get_pass_times(run)
+ if benchmark_name:
+ pt = get_pass_times_for_benchmark(run, benchmark_name)
+ else:
+ pt = get_pass_times(run)
if pt and any(v > 0 for v in pt.values()):
pass_times = pt
commit = short_commit(run.get("commit", ""))
@@ -254,7 +283,7 @@ def generate_breakdown_chart(runs: list[dict]) -> str:
}
''',
f'
Breakdown of where compilation time is spent in the most recent benchmark run.
-