#!/usr/bin/env python3 """ Analyze benchmark results from multiple Python versions Creates summary tables and comparisons """ import json import glob from datetime import datetime from typing import Dict, List import sys def load_results() -> Dict[str, dict]: """Load all JSON result files""" results = {} for filename in glob.glob("results_python_*.json"): with open(filename, "r") as f: data = json.load(f) version = data["python_version"] results[version] = data return results def calculate_relative_performance(results: Dict[str, dict]) -> Dict[str, dict]: """Calculate relative performance with dataclass as baseline (1.0x)""" relative = {} for version, data in results.items(): relative[version] = {} benchmarks = data["benchmarks"] # Use dataclass as baseline for each metric for metric in ["creation_per_object", "modification_per_operation", "serialization_per_object"]: dataclass_time = benchmarks["dataclass"][metric] relative[version][metric] = { "dataclass": 1.0, "pydantic": benchmarks["pydantic"][metric] / dataclass_time, "sqlalchemy": benchmarks["sqlalchemy"][metric] / dataclass_time } return relative def format_microseconds(seconds: float) -> str: """Convert seconds to microseconds with formatting""" return f"{seconds * 1_000_000:.2f}μs" def format_relative(value: float) -> str: """Format relative performance value""" return f"{value:.1f}x" def print_summary_table(results: Dict[str, dict]): """Print formatted summary table""" print("\n" + "=" * 80) print("BENCHMARK RESULTS SUMMARY") print("=" * 80) print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f"Python versions tested: {', '.join(sorted(results.keys()))}") # Get the first result to check iterations first_result = next(iter(results.values())) print(f"Iterations per test: {first_result['iterations']:,}") print("\n") # Print absolute times for each Python version for version in sorted(results.keys()): data = results[version] print(f"\nPython {version} - Absolute Times") print("-" * 60) print(f"{'Library':<12} {'Creation':<15} {'Modification':<15} {'Serialization':<15}") print("-" * 60) for lib in ["dataclass", "pydantic", "sqlalchemy"]: bench = data["benchmarks"][lib] creation = format_microseconds(bench["creation_per_object"]) modification = format_microseconds(bench["modification_per_operation"]) serialization = format_microseconds(bench["serialization_per_object"]) print(f"{lib:<12} {creation:<15} {modification:<15} {serialization:<15}") # Print relative performance relative = calculate_relative_performance(results) print("\n\nRelative Performance (dataclass = 1.0x)") print("=" * 80) for version in sorted(results.keys()): print(f"\nPython {version}") print("-" * 60) print(f"{'Library':<12} {'Creation':<15} {'Modification':<15} {'Serialization':<15}") print("-" * 60) for lib in ["dataclass", "pydantic", "sqlalchemy"]: creation = format_relative(relative[version]["creation_per_object"][lib]) modification = format_relative(relative[version]["modification_per_operation"][lib]) serialization = format_relative(relative[version]["serialization_per_object"][lib]) print(f"{lib:<12} {creation:<15} {modification:<15} {serialization:<15}") # Average across all Python versions print("\n\nAverage Relative Performance Across All Python Versions") print("=" * 80) print(f"{'Library':<12} {'Creation':<15} {'Modification':<15} {'Serialization':<15}") print("-" * 60) for lib in ["dataclass", "pydantic", "sqlalchemy"]: avg_creation = sum(relative[v]["creation_per_object"][lib] for v in relative) / len(relative) avg_modification = sum(relative[v]["modification_per_operation"][lib] for v in relative) / len(relative) avg_serialization = sum(relative[v]["serialization_per_object"][lib] for v in relative) / len(relative) print(f"{lib:<12} {format_relative(avg_creation):<15} " f"{format_relative(avg_modification):<15} {format_relative(avg_serialization):<15}") def save_summary(results: Dict[str, dict]): """Save summary to JSON and Markdown files""" summary = { "generated": datetime.now().isoformat(), "python_versions": sorted(results.keys()), "raw_results": results, "relative_performance": calculate_relative_performance(results) } # Save JSON summary with open("summary.json", "w") as f: json.dump(summary, f, indent=2) # Save Markdown summary with open("summary.md", "w") as f: f.write("# Python Data Model Benchmark Results\n\n") f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") # Average performance table f.write("## Average Relative Performance (dataclass = 1.0x)\n\n") f.write("| Library | Object Creation | Modification | Serialization |\n") f.write("|---------|----------------|--------------|---------------|\n") relative = calculate_relative_performance(results) for lib in ["dataclass", "pydantic", "sqlalchemy"]: avg_creation = sum(relative[v]["creation_per_object"][lib] for v in relative) / len(relative) avg_modification = sum(relative[v]["modification_per_operation"][lib] for v in relative) / len(relative) avg_serialization = sum(relative[v]["serialization_per_object"][lib] for v in relative) / len(relative) f.write(f"| {lib} | {avg_creation:.1f}x | {avg_modification:.1f}x | {avg_serialization:.1f}x |\n") # Get iterations from first result first_result = next(iter(results.values())) f.write("\n## Notes\n\n") f.write("- **dataclass**: Standard library, no validation\n") f.write("- **pydantic**: Includes type validation and coercion\n") f.write("- **sqlalchemy**: Includes database operations (in-memory SQLite)\n") f.write(f"- Each test used {first_result['iterations']:,} iterations\n") def main(): """Main analysis function""" results = load_results() if not results: print("No result files found. Run benchmark.py first.") sys.exit(1) print(f"Found results for Python versions: {', '.join(sorted(results.keys()))}") print_summary_table(results) save_summary(results) print("\n\nSummary saved to:") print(" - summary.json (detailed results)") print(" - summary.md (markdown table)") if __name__ == "__main__": main()