| | """ |
| | Performance monitoring and optimization utilities. |
| | """ |
| |
|
| | import time |
| | import psutil |
| | import functools |
| | from typing import Dict, Any, Callable, Optional |
| | from contextlib import contextmanager |
| |
|
| | from app.core.logging import get_logger |
| |
|
| | logger = get_logger(__name__) |
| |
|
| |
|
| | class PerformanceMonitor: |
| | """Performance monitoring utility.""" |
| | |
| | def __init__(self): |
| | self.metrics = { |
| | "requests_total": 0, |
| | "requests_successful": 0, |
| | "requests_failed": 0, |
| | "total_processing_time": 0.0, |
| | "average_processing_time": 0.0, |
| | "min_processing_time": float('inf'), |
| | "max_processing_time": 0.0 |
| | } |
| | |
| | def record_request(self, processing_time: float, success: bool = True): |
| | """Record a request's performance metrics.""" |
| | self.metrics["requests_total"] += 1 |
| | |
| | if success: |
| | self.metrics["requests_successful"] += 1 |
| | else: |
| | self.metrics["requests_failed"] += 1 |
| | |
| | self.metrics["total_processing_time"] += processing_time |
| | self.metrics["average_processing_time"] = ( |
| | self.metrics["total_processing_time"] / self.metrics["requests_total"] |
| | ) |
| | |
| | if processing_time < self.metrics["min_processing_time"]: |
| | self.metrics["min_processing_time"] = processing_time |
| | |
| | if processing_time > self.metrics["max_processing_time"]: |
| | self.metrics["max_processing_time"] = processing_time |
| | |
| | def get_metrics(self) -> Dict[str, Any]: |
| | """Get current performance metrics.""" |
| | metrics = self.metrics.copy() |
| | |
| | |
| | try: |
| | metrics.update({ |
| | "cpu_percent": psutil.cpu_percent(), |
| | "memory_percent": psutil.virtual_memory().percent, |
| | "memory_available_mb": psutil.virtual_memory().available / (1024 * 1024) |
| | }) |
| | except Exception as e: |
| | logger.warning(f"Failed to get system metrics: {str(e)}") |
| | |
| | return metrics |
| | |
| | def reset_metrics(self): |
| | """Reset all metrics.""" |
| | self.metrics = { |
| | "requests_total": 0, |
| | "requests_successful": 0, |
| | "requests_failed": 0, |
| | "total_processing_time": 0.0, |
| | "average_processing_time": 0.0, |
| | "min_processing_time": float('inf'), |
| | "max_processing_time": 0.0 |
| | } |
| |
|
| |
|
| | |
| | performance_monitor = PerformanceMonitor() |
| |
|
| |
|
| | @contextmanager |
| | def measure_time(): |
| | """Context manager to measure execution time.""" |
| | start_time = time.time() |
| | try: |
| | yield |
| | finally: |
| | end_time = time.time() |
| | execution_time = end_time - start_time |
| | logger.debug(f"Execution time: {execution_time:.3f}s") |
| |
|
| |
|
| | def timed_function(func: Callable) -> Callable: |
| | """Decorator to measure function execution time.""" |
| | @functools.wraps(func) |
| | def wrapper(*args, **kwargs): |
| | start_time = time.time() |
| | try: |
| | result = func(*args, **kwargs) |
| | success = True |
| | return result |
| | except Exception as e: |
| | success = False |
| | raise |
| | finally: |
| | end_time = time.time() |
| | execution_time = end_time - start_time |
| | performance_monitor.record_request(execution_time, success) |
| | logger.debug(f"{func.__name__} execution time: {execution_time:.3f}s") |
| | |
| | return wrapper |
| |
|
| |
|
| | async def timed_async_function(func: Callable) -> Callable: |
| | """Decorator to measure async function execution time.""" |
| | @functools.wraps(func) |
| | async def wrapper(*args, **kwargs): |
| | start_time = time.time() |
| | try: |
| | result = await func(*args, **kwargs) |
| | success = True |
| | return result |
| | except Exception as e: |
| | success = False |
| | raise |
| | finally: |
| | end_time = time.time() |
| | execution_time = end_time - start_time |
| | performance_monitor.record_request(execution_time, success) |
| | logger.debug(f"{func.__name__} execution time: {execution_time:.3f}s") |
| | |
| | return wrapper |
| |
|
| |
|
| | class SimpleCache: |
| | """Simple in-memory cache for inference results.""" |
| | |
| | def __init__(self, max_size: int = 100, ttl: int = 3600): |
| | """ |
| | Initialize cache. |
| | |
| | Args: |
| | max_size: Maximum number of items to cache |
| | ttl: Time to live in seconds |
| | """ |
| | self.max_size = max_size |
| | self.ttl = ttl |
| | self.cache = {} |
| | self.access_times = {} |
| | |
| | def _is_expired(self, key: str) -> bool: |
| | """Check if a cache entry is expired.""" |
| | if key not in self.access_times: |
| | return True |
| | |
| | return time.time() - self.access_times[key] > self.ttl |
| | |
| | def _evict_expired(self): |
| | """Remove expired entries.""" |
| | current_time = time.time() |
| | expired_keys = [ |
| | key for key, access_time in self.access_times.items() |
| | if current_time - access_time > self.ttl |
| | ] |
| | |
| | for key in expired_keys: |
| | self.cache.pop(key, None) |
| | self.access_times.pop(key, None) |
| | |
| | def _evict_lru(self): |
| | """Remove least recently used entry.""" |
| | if not self.access_times: |
| | return |
| | |
| | lru_key = min(self.access_times.keys(), key=lambda k: self.access_times[k]) |
| | self.cache.pop(lru_key, None) |
| | self.access_times.pop(lru_key, None) |
| | |
| | def get(self, key: str) -> Optional[Any]: |
| | """Get item from cache.""" |
| | if key not in self.cache or self._is_expired(key): |
| | return None |
| | |
| | self.access_times[key] = time.time() |
| | return self.cache[key] |
| | |
| | def set(self, key: str, value: Any): |
| | """Set item in cache.""" |
| | |
| | self._evict_expired() |
| | |
| | |
| | while len(self.cache) >= self.max_size: |
| | self._evict_lru() |
| | |
| | self.cache[key] = value |
| | self.access_times[key] = time.time() |
| | |
| | def clear(self): |
| | """Clear all cache entries.""" |
| | self.cache.clear() |
| | self.access_times.clear() |
| | |
| | def size(self) -> int: |
| | """Get current cache size.""" |
| | return len(self.cache) |
| | |
| | def stats(self) -> Dict[str, Any]: |
| | """Get cache statistics.""" |
| | return { |
| | "size": len(self.cache), |
| | "max_size": self.max_size, |
| | "ttl": self.ttl, |
| | "hit_ratio": getattr(self, '_hits', 0) / max(getattr(self, '_requests', 1), 1) |
| | } |
| |
|
| |
|
| | |
| | inference_cache = SimpleCache(max_size=50, ttl=1800) |
| |
|
| |
|
| | def get_system_info() -> Dict[str, Any]: |
| | """Get system information.""" |
| | try: |
| | return { |
| | "cpu_count": psutil.cpu_count(), |
| | "cpu_percent": psutil.cpu_percent(interval=1), |
| | "memory_total_gb": psutil.virtual_memory().total / (1024**3), |
| | "memory_available_gb": psutil.virtual_memory().available / (1024**3), |
| | "memory_percent": psutil.virtual_memory().percent, |
| | "disk_usage_percent": psutil.disk_usage('/').percent |
| | } |
| | except Exception as e: |
| | logger.error(f"Failed to get system info: {str(e)}") |
| | return {"error": str(e)} |
| |
|