222 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| """
 | |
| Parse Criterion benchmark results and export to CSV/JSON formats.
 | |
| """
 | |
| 
 | |
| import json
 | |
| import csv
 | |
| import sys
 | |
| import os
 | |
| from pathlib import Path
 | |
| from typing import Dict, List, Any
 | |
| 
 | |
| def parse_criterion_json(criterion_dir: str) -> List[Dict[str, Any]]:
 | |
|     """Parse Criterion benchmark results from the target directory."""
 | |
|     results = []
 | |
|     criterion_path = Path(criterion_dir)
 | |
|     
 | |
|     if not criterion_path.exists():
 | |
|         print(f"Error: Criterion directory not found: {criterion_dir}", file=sys.stderr)
 | |
|         return results
 | |
|     
 | |
|     # Find all benchmark.json files
 | |
|     for benchmark_file in criterion_path.rglob("new/benchmark.json"):
 | |
|         try:
 | |
|             with open(benchmark_file, 'r') as f:
 | |
|                 data = json.load(f)
 | |
|             
 | |
|             # Extract benchmark name from path
 | |
|             bench_name = str(benchmark_file.parent.parent.name)
 | |
|             
 | |
|             # Extract metrics
 | |
|             result = {
 | |
|                 'name': bench_name,
 | |
|                 'mean_ns': data.get('mean', {}).get('point_estimate', 0),
 | |
|                 'median_ns': data.get('median', {}).get('point_estimate', 0),
 | |
|                 'std_dev_ns': data.get('std_dev', {}).get('point_estimate', 0),
 | |
|             }
 | |
|             
 | |
|             # Calculate throughput
 | |
|             if result['mean_ns'] > 0:
 | |
|                 result['throughput_ops_sec'] = 1_000_000_000 / result['mean_ns']
 | |
|             else:
 | |
|                 result['throughput_ops_sec'] = 0
 | |
|             
 | |
|             results.append(result)
 | |
|         except Exception as e:
 | |
|             print(f"Warning: Failed to parse {benchmark_file}: {e}", file=sys.stderr)
 | |
|     
 | |
|     return results
 | |
| 
 | |
| def parse_benchmark_name(name: str) -> Dict[str, str]:
 | |
|     """Parse benchmark name into components."""
 | |
|     parts = name.split('/')
 | |
|     
 | |
|     result = {
 | |
|         'suite': parts[0] if len(parts) > 0 else '',
 | |
|         'category': parts[1] if len(parts) > 1 else '',
 | |
|         'operation': parts[2] if len(parts) > 2 else '',
 | |
|         'backend': '',
 | |
|         'parameter': ''
 | |
|     }
 | |
|     
 | |
|     # Try to extract backend name
 | |
|     for part in parts:
 | |
|         if 'redb' in part.lower():
 | |
|             result['backend'] = 'redb'
 | |
|             break
 | |
|         elif 'sled' in part.lower():
 | |
|             result['backend'] = 'sled'
 | |
|             break
 | |
|     
 | |
|     # Extract parameter (size, clients, etc.)
 | |
|     if len(parts) > 3:
 | |
|         result['parameter'] = parts[3]
 | |
|     
 | |
|     return result
 | |
| 
 | |
| def export_to_csv(results: List[Dict[str, Any]], output_file: str):
 | |
|     """Export results to CSV format."""
 | |
|     if not results:
 | |
|         print("No results to export", file=sys.stderr)
 | |
|         return
 | |
|     
 | |
|     fieldnames = ['name', 'backend', 'operation', 'mean_ns', 'median_ns', 
 | |
|                   'std_dev_ns', 'throughput_ops_sec']
 | |
|     
 | |
|     with open(output_file, 'w', newline='') as f:
 | |
|         writer = csv.DictWriter(f, fieldnames=fieldnames)
 | |
|         writer.writeheader()
 | |
|         
 | |
|         for result in results:
 | |
|             parsed = parse_benchmark_name(result['name'])
 | |
|             row = {
 | |
|                 'name': result['name'],
 | |
|                 'backend': parsed['backend'],
 | |
|                 'operation': parsed['operation'],
 | |
|                 'mean_ns': int(result['mean_ns']),
 | |
|                 'median_ns': int(result['median_ns']),
 | |
|                 'std_dev_ns': int(result['std_dev_ns']),
 | |
|                 'throughput_ops_sec': f"{result['throughput_ops_sec']:.2f}"
 | |
|             }
 | |
|             writer.writerow(row)
 | |
|     
 | |
|     print(f"Exported {len(results)} results to {output_file}")
 | |
| 
 | |
| def export_to_json(results: List[Dict[str, Any]], output_file: str):
 | |
|     """Export results to JSON format."""
 | |
|     if not results:
 | |
|         print("No results to export", file=sys.stderr)
 | |
|         return
 | |
|     
 | |
|     # Enhance results with parsed information
 | |
|     enhanced_results = []
 | |
|     for result in results:
 | |
|         parsed = parse_benchmark_name(result['name'])
 | |
|         enhanced = {**result, **parsed}
 | |
|         enhanced_results.append(enhanced)
 | |
|     
 | |
|     output = {
 | |
|         'benchmarks': enhanced_results,
 | |
|         'summary': {
 | |
|             'total_benchmarks': len(results),
 | |
|             'backends': list(set(r.get('backend', '') for r in enhanced_results if r.get('backend')))
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     with open(output_file, 'w') as f:
 | |
|         json.dump(output, f, indent=2)
 | |
|     
 | |
|     print(f"Exported {len(results)} results to {output_file}")
 | |
| 
 | |
| def print_summary(results: List[Dict[str, Any]]):
 | |
|     """Print a summary of benchmark results."""
 | |
|     if not results:
 | |
|         print("No results to summarize")
 | |
|         return
 | |
|     
 | |
|     print("\n=== Benchmark Summary ===\n")
 | |
|     print(f"Total benchmarks: {len(results)}")
 | |
|     
 | |
|     # Group by backend
 | |
|     backends = {}
 | |
|     for result in results:
 | |
|         parsed = parse_benchmark_name(result['name'])
 | |
|         backend = parsed['backend']
 | |
|         if backend:
 | |
|             if backend not in backends:
 | |
|                 backends[backend] = []
 | |
|             backends[backend].append(result)
 | |
|     
 | |
|     for backend, bench_results in backends.items():
 | |
|         print(f"\n{backend.upper()}:")
 | |
|         print(f"  Benchmarks: {len(bench_results)}")
 | |
|         
 | |
|         if bench_results:
 | |
|             mean_throughput = sum(r['throughput_ops_sec'] for r in bench_results) / len(bench_results)
 | |
|             print(f"  Avg throughput: {mean_throughput:.2f} ops/sec")
 | |
|             
 | |
|             fastest = max(bench_results, key=lambda x: x['throughput_ops_sec'])
 | |
|             print(f"  Fastest: {fastest['name']} ({fastest['throughput_ops_sec']:.2f} ops/sec)")
 | |
| 
 | |
| def main():
 | |
|     if len(sys.argv) < 2:
 | |
|         print("Usage: python parse_results.py <criterion_dir> [--csv output.csv] [--json output.json]")
 | |
|         print("\nExample:")
 | |
|         print("  python parse_results.py target/criterion --csv results.csv --json results.json")
 | |
|         sys.exit(1)
 | |
|     
 | |
|     criterion_dir = sys.argv[1]
 | |
|     
 | |
|     # Parse command line arguments
 | |
|     csv_output = None
 | |
|     json_output = None
 | |
|     
 | |
|     i = 2
 | |
|     while i < len(sys.argv):
 | |
|         if sys.argv[i] == '--csv' and i + 1 < len(sys.argv):
 | |
|             csv_output = sys.argv[i + 1]
 | |
|             i += 2
 | |
|         elif sys.argv[i] == '--json' and i + 1 < len(sys.argv):
 | |
|             json_output = sys.argv[i + 1]
 | |
|             i += 2
 | |
|         else:
 | |
|             i += 1
 | |
|     
 | |
|     # Parse results
 | |
|     print(f"Parsing benchmark results from {criterion_dir}...")
 | |
|     results = parse_criterion_json(criterion_dir)
 | |
|     
 | |
|     if not results:
 | |
|         print("No benchmark results found!")
 | |
|         sys.exit(1)
 | |
|     
 | |
|     # Export results
 | |
|     if csv_output:
 | |
|         export_to_csv(results, csv_output)
 | |
|     
 | |
|     if json_output:
 | |
|         export_to_json(results, json_output)
 | |
|     
 | |
|     # Print summary
 | |
|     print_summary(results)
 | |
|     
 | |
|     # If no output specified, print to stdout
 | |
|     if not csv_output and not json_output:
 | |
|         print("\n=== CSV Output ===\n")
 | |
|         import io
 | |
|         output = io.StringIO()
 | |
|         fieldnames = ['name', 'mean_ns', 'median_ns', 'throughput_ops_sec']
 | |
|         writer = csv.DictWriter(output, fieldnames=fieldnames)
 | |
|         writer.writeheader()
 | |
|         for result in results:
 | |
|             writer.writerow({
 | |
|                 'name': result['name'],
 | |
|                 'mean_ns': int(result['mean_ns']),
 | |
|                 'median_ns': int(result['median_ns']),
 | |
|                 'throughput_ops_sec': f"{result['throughput_ops_sec']:.2f}"
 | |
|             })
 | |
|         print(output.getvalue())
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main() |