""" EU-Utility - Startup Profiler Profiles application startup time and identifies bottlenecks. """ import time import sys from functools import wraps from typing import Dict, List, Callable, Optional from dataclasses import dataclass, field from collections import defaultdict @dataclass class ProfileResult: """Result of profiling a component.""" name: str start_time: float end_time: float duration_ms: float parent: Optional[str] = None children: List[str] = field(default_factory=list) class StartupProfiler: """ Profiles application startup time. Usage: profiler = StartupProfiler() with profiler.profile("database_init"): init_database() profiler.print_report() """ _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): if self._initialized: return self.results: Dict[str, ProfileResult] = {} self._stack: List[str] = [] self._start_times: Dict[str, float] = {} self.enabled = True self.app_start_time = time.perf_counter() self._initialized = True def profile(self, name: str): """Context manager for profiling a code block.""" return self._ProfileContext(self, name) class _ProfileContext: def __init__(self, profiler, name): self.profiler = profiler self.name = name def __enter__(self): if self.profiler.enabled: self.profiler._start(self.name) return self def __exit__(self, exc_type, exc_val, exc_tb): if self.profiler.enabled: self.profiler._end(self.name) return False def _start(self, name: str): """Start profiling a component.""" self._start_times[name] = time.perf_counter() parent = self._stack[-1] if self._stack else None self._stack.append(name) if parent: if parent not in self.results: self.results[parent] = ProfileResult( name=parent, start_time=0, end_time=0, duration_ms=0, children=[] ) self.results[parent].children.append(name) def _end(self, name: str): """End profiling a component.""" end_time = time.perf_counter() start_time = self._start_times.pop(name, end_time) duration = (end_time - start_time) * 1000 if name in self._stack: self._stack.remove(name) self.results[name] = ProfileResult( name=name, start_time=start_time - self.app_start_time, end_time=end_time - self.app_start_time, duration_ms=duration, parent=self._stack[-1] if self._stack else None ) def print_report(self): """Print startup profiling report.""" total_time = (time.perf_counter() - self.app_start_time) * 1000 print("\n" + "=" * 80) print("šŸš€ STARTUP PERFORMANCE REPORT") print("=" * 80) print(f"{'Component':<40} {'Time (ms)':<12} {'% of Total':<12} {'Status'}") print("-" * 80) # Sort by duration sorted_results = sorted( self.results.items(), key=lambda x: x[1].duration_ms, reverse=True ) for name, result in sorted_results: pct = (result.duration_ms / total_time * 100) if total_time > 0 else 0 status = "āš ļø SLOW" if result.duration_ms > 1000 else "āœ“ OK" if result.duration_ms > 100 else "āœ“ FAST" indent = " " * (len(name.split(".")) - 1) print(f"{indent}{name:<38} {result.duration_ms:>10.2f} {pct:>10.1f}% {status}") print("-" * 80) print(f"{'TOTAL STARTUP TIME':<40} {total_time:>10.2f} ms") print("=" * 80) # Identify bottlenecks slow_components = [r for r in self.results.values() if r.duration_ms > 500] if slow_components: print("\nāš ļø BOTTLENECKS DETECTED:") for r in sorted(slow_components, key=lambda x: x.duration_ms, reverse=True): print(f" • {r.name}: {r.duration_ms:.2f} ms") print() def get_bottlenecks(self, threshold_ms: float = 500) -> List[ProfileResult]: """Get components that took longer than threshold.""" return [ r for r in self.results.values() if r.duration_ms > threshold_ms ] def reset(self): """Reset profiler.""" self.results.clear() self._stack.clear() self._start_times.clear() self.app_start_time = time.perf_counter() def profile_startup(name: str): """Decorator for profiling function startup time.""" def decorator(func): @wraps(func) def wrapper(*args, **kwargs): profiler = StartupProfiler() with profiler.profile(name): return func(*args, **kwargs) return wrapper return decorator # Convenience function def get_startup_profiler() -> StartupProfiler: """Get global StartupProfiler instance.""" return StartupProfiler() if __name__ == "__main__": # Test profiler profiler = StartupProfiler() with profiler.profile("test_component"): time.sleep(0.1) with profiler.profile("nested_component"): time.sleep(0.05) with profiler.profile("another_component"): time.sleep(0.2) profiler.print_report()