25fb843c40
The prior commit switched the recipe to `[source] path = "source"`, but .gitignore:77 still listed `local/recipes/shells/brush/source` (a leftover from when brush was a transient upstream git fetch), so the vendored tree was not tracked — a fresh clone would have no brush source and the build would fail. Drop that ignore line and commit the vendored working tree (reubeno/brush @ 897b373e, with the Redox port patches pre-applied). brush is now a durable local fork like the other path=source recipes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
96 lines
3.5 KiB
Python
Executable File
96 lines
3.5 KiB
Python
Executable File
#!/usr/bin/python3
|
|
import argparse
|
|
import re
|
|
from dataclasses import dataclass
|
|
from typing import Dict
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-b", "--base-results", dest="base_results_file_path", type=str, help="Path to base results output file", required=True)
|
|
parser.add_argument("-t", "--test-results", dest="test_results_file_path", type=str, help="Path to test results output file", required=True)
|
|
|
|
args = parser.parse_args()
|
|
|
|
@dataclass
|
|
class Benchmark:
|
|
test_name: str
|
|
duration_in_ns: int
|
|
plus_or_minus_in_ns: int
|
|
|
|
def parse_benchmarks_results(file_path: str) -> Dict[str, Benchmark]:
|
|
benchmarks = {}
|
|
|
|
with open(file_path, "r") as file:
|
|
for line in file.readlines():
|
|
match = re.match(r"test (.*) \.\.\. bench: +([\d,]+) ns/iter \(\+/- ([\d,]+)\)", line.strip())
|
|
if match:
|
|
benchmark = Benchmark(
|
|
test_name=match.group(1),
|
|
duration_in_ns=int(match.group(2).replace(',', '')),
|
|
plus_or_minus_in_ns=int(match.group(3).replace(',', ''))
|
|
)
|
|
|
|
benchmarks[benchmark.test_name] = benchmark
|
|
|
|
return benchmarks
|
|
|
|
base_results = parse_benchmarks_results(args.base_results_file_path)
|
|
test_results = parse_benchmarks_results(args.test_results_file_path)
|
|
|
|
base_test_names = set(base_results.keys())
|
|
test_test_names = set(test_results.keys())
|
|
|
|
removed_from_base = base_test_names - test_test_names
|
|
added_by_test = test_test_names - base_test_names
|
|
common = base_test_names & test_test_names
|
|
|
|
print("# Performance Benchmark Report")
|
|
|
|
if common:
|
|
print(f"| {'Benchmark name':38} | {'Baseline (μs)':>13} | {'Test/PR (μs)':>13} | {'Delta (μs)':>13} | {'Delta %':15} |")
|
|
print(f"| {'-' * 38} | {'-' * 13} | {'-' * 13} | {'-' * 13} | {'-' * 15} |")
|
|
for name in sorted(common):
|
|
# Retrieve base data
|
|
base_duration = base_results[name].duration_in_ns / 1000.0
|
|
base_plus_or_minus = base_results[name].plus_or_minus_in_ns / 1000.0
|
|
base_plus_or_minus_percentage = (100.0 * base_plus_or_minus) / base_duration
|
|
|
|
# Retrieve test data
|
|
test_duration = test_results[name].duration_in_ns / 1000.0
|
|
test_plus_or_minus = test_results[name].plus_or_minus_in_ns / 1000.0
|
|
test_plus_or_minus_percentage = (100.0 * test_plus_or_minus) / test_duration
|
|
|
|
# Compute delta
|
|
delta_duration = test_duration - base_duration
|
|
delta_percentage = (100.0 * delta_duration) / base_duration
|
|
abs_delta_percentage = abs(delta_percentage)
|
|
max_plus_or_minus_percentage = max(base_plus_or_minus_percentage, test_plus_or_minus_percentage)
|
|
|
|
# Format
|
|
delta_str = f"{delta_duration:8.2f}"
|
|
|
|
if abs_delta_percentage > max_plus_or_minus_percentage:
|
|
if delta_percentage < 0:
|
|
delta_prefix = "🟢 "
|
|
elif delta_percentage > 0:
|
|
delta_prefix = "🟠 +"
|
|
else:
|
|
delta_prefix = "⚪ "
|
|
|
|
delta_percentage_str = f"{delta_prefix}{delta_percentage:.2f}%"
|
|
else:
|
|
delta_percentage_str = "⚪ Unchanged"
|
|
|
|
print(f"| `{name:36}` | `{base_duration:8.2f} μs` | `{test_duration:8.2f} μs` | `{delta_str:>8} μs` | `{delta_percentage_str:12}` |")
|
|
|
|
if removed_from_base:
|
|
print()
|
|
print("Benchmarks removed:")
|
|
for name in removed_from_base:
|
|
print(f" - {name}")
|
|
|
|
if added_by_test:
|
|
print()
|
|
print("Benchmarks added:")
|
|
for name in added_by_test:
|
|
print(f" - {name}")
|