import json
from pathlib import Path
from typing import Optional, Dict
import statistics
import os
from datetime import datetime, timedelta
class CriterionExtractor:
def __init__(self, project_root: Optional[Path] = None):
if project_root is None:
project_root = Path(__file__).parent.parent
self.project_root = project_root
self.criterion_dir = project_root / "target" / "criterion"
def get_benchmark_result(self, bench_name: str) -> Optional[float]:
estimates_file = self.criterion_dir / bench_name / "base" / "estimates.json"
if not estimates_file.exists():
return None
try:
with open(estimates_file) as f:
data = json.load(f)
mean_ns = data.get("mean", {}).get("point_estimate")
if mean_ns is None:
return None
return mean_ns / 1_000_000_000
except (json.JSONDecodeError, KeyError, TypeError):
return None
def get_all_benchmarks(self) -> Dict[str, float]:
results = {}
if not self.criterion_dir.exists():
return results
for bench_dir in self.criterion_dir.iterdir():
if not bench_dir.is_dir():
continue
bench_name = bench_dir.name
mean_time = self.get_benchmark_result(bench_name)
if mean_time is not None:
results[bench_name] = mean_time
return results
def get_available_benchmarks(self) -> list:
return sorted(self.get_all_benchmarks().keys())
def is_cached(self, bench_name: str) -> bool:
return self.get_benchmark_result(bench_name) is not None
def is_stale(self, max_age_hours: int = 24) -> bool:
if not self.criterion_dir.exists():
return True
newest_mtime = 0
for bench_dir in self.criterion_dir.iterdir():
if bench_dir.is_dir():
estimates_file = bench_dir / "base" / "estimates.json"
if estimates_file.exists():
mtime = estimates_file.stat().st_mtime
newest_mtime = max(newest_mtime, mtime)
if newest_mtime == 0:
return True
src_dir = self.project_root / "benches"
if src_dir.exists():
for rs_file in src_dir.glob("*.rs"):
if rs_file.stat().st_mtime > newest_mtime:
return True
cache_age = datetime.now().timestamp() - newest_mtime
max_age_seconds = max_age_hours * 3600
return cache_age > max_age_seconds
def cache_summary(self) -> Dict:
benchmarks = self.get_all_benchmarks()
stale = self.is_stale()
return {
'total_benchmarks': len(benchmarks),
'benchmarks': sorted(benchmarks.keys()),
'criterion_dir_exists': self.criterion_dir.exists(),
'is_stale': stale,
}
def extract_rust_benchmark_cached(bench_name: str, project_root: Optional[Path] = None) -> Optional[float]:
extractor = CriterionExtractor(project_root)
return extractor.get_benchmark_result(bench_name)
if __name__ == '__main__':
import sys
extractor = CriterionExtractor()
print("Cached Criterion.rs Benchmarks")
print("=" * 70)
summary = extractor.cache_summary()
if not summary['criterion_dir_exists']:
print("\nNo Criterion results found.")
print("Run: cargo bench --release")
sys.exit(1)
print(f"\nFound {summary['total_benchmarks']} benchmarks:\n")
benchmarks = extractor.get_all_benchmarks()
for bench_name in sorted(benchmarks.keys()):
mean_time = benchmarks[bench_name]
if mean_time < 0.001:
time_str = f"{mean_time * 1_000_000:.2f} µs"
elif mean_time < 1:
time_str = f"{mean_time * 1000:.2f} ms"
else:
time_str = f"{mean_time:.2f} s"
print(f" {bench_name:40s} {time_str:>12s}")