#!/usr/bin/env python3 """ Benchmark Comparison Script for Bioheat PINN Compares RustyTorch++ (Rust) vs PyTorch (Python) performance for solving the Pennes Bioheat Equation using PINNs. Usage: python run_comparison.py [--steps 1000] [--output comparison_report.html] """ import argparse import json import os import subprocess import sys from datetime import datetime from pathlib import Path from typing import Dict, Any, Optional # Import the PyTorch benchmark from pytorch_bioheat_reference import benchmark_pytorch def run_rustytorch_benchmark( num_steps: int = 1000, binary_path: Optional[str] = None, ) -> Optional[Dict[str, Any]]: """ Run RustyTorch++ benchmark via CLI. Note: This requires a compiled benchmark binary. If not available, returns None. """ # Look for benchmark binary if binary_path is None: possible_paths = [ "../../target/release/bioheat_benchmark", "../../target/debug/bioheat_benchmark", "../target/release/bioheat_benchmark", ] for path in possible_paths: if os.path.exists(path): binary_path = path break if binary_path is None or not os.path.exists(binary_path): print("RustyTorch++ benchmark binary not found.") print("To compile: cargo build --release -p rtx-bioheat --bin bioheat_benchmark") return None try: result = subprocess.run( [binary_path, "--steps", str(num_steps), "--json"], capture_output=True, text=True, timeout=600, ) if result.returncode != 0: print(f"RustyTorch++ benchmark failed: {result.stderr}") return None return json.loads(result.stdout) except Exception as e: print(f"Error running RustyTorch++ benchmark: {e}") return None def generate_html_report( pytorch_results: Dict[str, Any], rustytorch_results: Optional[Dict[str, Any]], output_path: str, ): """Generate HTML comparison report.""" # Calculate speedups if RustyTorch results available if rustytorch_results: training_speedup = pytorch_results['training_time_sec'] / rustytorch_results['training_time_sec'] inference_speedup = pytorch_results['inference_mean_ms'] / rustytorch_results['inference_mean_ms'] else: training_speedup = None inference_speedup = None html = f""" Bioheat PINN Benchmark Comparison

Bioheat PINN Benchmark Comparison

RustyTorch++ vs PyTorch - Pennes Bioheat Equation

Physics: Pennes Bioheat Equation

ρc(∂T/∂t) = k∇²T + ωbρbcb(Ta - T) + Qm + Qs

The Pennes bioheat equation models heat transfer in biological tissue for thermal ablation planning.

Summary Metrics

{pytorch_results['steps_per_second']:.0f}
PyTorch Steps/sec
{"
" + f"{rustytorch_results['steps_per_second']:.0f}" + "
RustyTorch++ Steps/sec
" if rustytorch_results else ""} {f"
{training_speedup:.1f}x
Training Speedup
" if training_speedup else ""} {f"
{inference_speedup:.1f}x
Inference Speedup
" if inference_speedup else ""}

Detailed Comparison

{"" if rustytorch_results else ""} {"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if training_speedup else ""} {f"" if rustytorch_results else ""} {f"" if training_speedup else ""} {f"" if rustytorch_results else ""} {f"" if inference_speedup else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""} {f"" if rustytorch_results else ""}
Metric PyTorchRustyTorch++Speedup
Device {pytorch_results['device']}{rustytorch_results['device']}-
Model Parameters {pytorch_results['num_parameters']:,}{rustytorch_results['num_parameters']:,}-
Training Steps {pytorch_results['num_steps']}{rustytorch_results['num_steps']}-
Training Time (sec) {pytorch_results['training_time_sec']:.2f}{rustytorch_results['training_time_sec']:.2f}{training_speedup:.2f}x
Steps/Second {pytorch_results['steps_per_second']:.1f}{rustytorch_results['steps_per_second']:.1f}{training_speedup:.2f}x
Inference Time (32^3 grid) {pytorch_results['inference_mean_ms']:.2f} ms{rustytorch_results['inference_mean_ms']:.2f} ms{inference_speedup:.2f}x
Peak GPU Memory {pytorch_results['peak_memory_mb']:.1f} MB{rustytorch_results['peak_memory_mb']:.1f} MB-
Final Loss {pytorch_results['final_loss']:.4e}{rustytorch_results['final_loss']:.4e}-

Configuration

""" with open(output_path, 'w') as f: f.write(html) print(f"\nReport generated: {output_path}") def main(): parser = argparse.ArgumentParser( description='Benchmark Comparison: RustyTorch++ vs PyTorch' ) parser.add_argument( '--steps', type=int, default=1000, help='Number of training steps (default: 1000)' ) parser.add_argument( '--output', type=str, default='comparison_report.html', help='Output HTML report path (default: comparison_report.html)' ) parser.add_argument( '--pytorch-only', action='store_true', help='Only run PyTorch benchmark' ) parser.add_argument( '--device', type=str, default='cuda', choices=['cuda', 'cpu'], help='Device for PyTorch (default: cuda)' ) args = parser.parse_args() print("=" * 60) print("Bioheat PINN Benchmark Comparison") print("RustyTorch++ vs PyTorch") print("=" * 60) # Run PyTorch benchmark print("\n[1/2] Running PyTorch benchmark...") pytorch_results = benchmark_pytorch( device=args.device, num_steps=args.steps, ) # Run RustyTorch++ benchmark (if available) rustytorch_results = None if not args.pytorch_only: print("\n[2/2] Running RustyTorch++ benchmark...") rustytorch_results = run_rustytorch_benchmark(num_steps=args.steps) # Generate report print("\nGenerating comparison report...") generate_html_report(pytorch_results, rustytorch_results, args.output) # Print summary print("\n" + "=" * 60) print("Summary") print("=" * 60) print(f"PyTorch training: {pytorch_results['steps_per_second']:.1f} steps/sec") if rustytorch_results: print(f"RustyTorch++ training: {rustytorch_results['steps_per_second']:.1f} steps/sec") speedup = pytorch_results['training_time_sec'] / rustytorch_results['training_time_sec'] print(f"Training speedup: {speedup:.2f}x") print("=" * 60) if __name__ == '__main__': main()