Private
Public Access
0
0

more organization

This commit is contained in:
2026-06-06 11:08:07 -04:00
parent 7d555361f9
commit 339b062913
9 changed files with 471 additions and 544 deletions
+62 -70
View File
@@ -70,7 +70,7 @@ class PerformanceScope:
"""Helper class for PerformanceMonitor.scope() context manager."""
def __init__(self, monitor: PerformanceMonitor, name: str) -> None:
self.monitor = monitor
self.name = name
self.name = name
def __enter__(self) -> PerformanceScope:
self.monitor.start_component(self.name)
return self
@@ -89,35 +89,33 @@ def get_monitor() -> PerformanceMonitor:
class PerformanceMonitor:
"""
Tracks application performance metrics like FPS, frame time, and CPU usage.
Supports thread-safe tracking for individual components with efficient moving averages.
Tracks application performance metrics like FPS, frame time, and CPU usage.
Supports thread-safe tracking for individual components with efficient moving averages.
"""
def __init__(self, history_size: int = 300) -> None:
self.enabled: bool = False
self.history_size = history_size
self._lock = threading.Lock()
self.history_size = history_size
self._lock = threading.Lock()
self._start_time: Optional[float] = None
self._start_time: Optional[float] = None
self._last_frame_start_time: float = 0.0
self._last_frame_time: float = 0.0
self._fps: float = 0.0
self._last_calculated_fps: float = 0.0
self._frame_count: int = 0
self._fps_timer: float = 0.0
self._cpu_percent: float = 0.0
self._input_lag_ms: float = 0.0
self._last_frame_time: float = 0.0
self._fps: float = 0.0
self._last_calculated_fps: float = 0.0
self._frame_count: int = 0
self._fps_timer: float = 0.0
self._cpu_percent: float = 0.0
self._input_lag_ms: float = 0.0
self._component_starts: dict[str, float] = {}
self._component_starts: dict[str, float] = {}
self._component_timings: dict[str, float] = {}
self._component_counts: dict[str, int] = {}
self._component_max: dict[str, float] = {}
self._component_min: dict[str, float] = {}
self._component_counts: dict[str, int] = {}
self._component_max: dict[str, float] = {}
self._component_min: dict[str, float] = {}
# Rolling history and running sums for O(1) average calculation
# deques are thread-safe for appends and pops.
self._history: Dict[str, deque[float]] = {}
self._history: Dict[str, deque[float]] = {}
self._history_sums: Dict[str, float] = {}
# For slowing down graph updates
@@ -143,7 +141,7 @@ class PerformanceMonitor:
"""Thread-safe O(1) history update."""
with self._lock:
if key not in self._history:
self._history[key] = deque(maxlen=self.history_size)
self._history[key] = deque(maxlen=self.history_size)
self._history_sums[key] = 0.0
h = self._history[key]
@@ -158,13 +156,12 @@ class PerformanceMonitor:
"""Thread-safe O(1) average retrieval."""
with self._lock:
h = self._history.get(key)
if not h or len(h) == 0:
return 0.0
if not h or len(h) == 0: return 0.0
return self._history_sums[key] / len(h)
def start_frame(self) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
"""
now = time.perf_counter()
with self._lock:
@@ -173,24 +170,24 @@ class PerformanceMonitor:
if dt > 0:
self._fps = 1.0 / dt
self._last_frame_start_time = now
self._start_time = now
self._frame_count += 1
self._start_time = now
self._frame_count += 1
def end_frame(self) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing]
"""
if self._start_time is None:
return
now = time.perf_counter()
elapsed = now - self._start_time
now = time.perf_counter()
elapsed = now - self._start_time
frame_time_ms = elapsed * 1000
with self._lock:
self._last_frame_time = frame_time_ms
cpu = self._cpu_percent
cpu = self._cpu_percent
ilag = self._input_lag_ms
fps = self._fps
fps = self._fps
# Slow down history sampling for core metrics
if now - self._last_sample_time >= self._sample_interval:
@@ -205,11 +202,11 @@ class PerformanceMonitor:
with self._lock:
self._last_calculated_fps = self._frame_count / self._fps_timer
self._frame_count = 0
self._fps_timer = 0.0
self._fps_timer = 0.0
def start_component(self, name: str) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
"""
if not self.enabled: return
now = time.perf_counter()
@@ -218,7 +215,7 @@ class PerformanceMonitor:
def end_component(self, name: str) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
[C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics]
"""
if not self.enabled: return
now = time.perf_counter()
@@ -228,55 +225,51 @@ class PerformanceMonitor:
elapsed = (now - start) * 1000
with self._lock:
self._component_timings[name] = elapsed
self._component_counts[name] = self._component_counts.get(name, 0) + 1
if name not in self._component_max or elapsed > self._component_max[name]:
self._component_max[name] = elapsed
if name not in self._component_min or elapsed < self._component_min[name]:
self._component_min[name] = elapsed
self._component_counts[name] = self._component_counts.get(name, 0) + 1
if name not in self._component_max or elapsed > self._component_max[name]: self._component_max[name] = elapsed
if name not in self._component_min or elapsed < self._component_min[name]: self._component_min[name] = elapsed
self._add_to_history(f'comp_{name}', elapsed)
def get_metrics(self) -> dict[str, float]:
"""
Returns current metrics and their moving averages. Thread-safe.
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager]
Returns current metrics and their moving averages. Thread-safe.
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager]
"""
with self._lock:
fps = self._fps
last_ft = self._last_frame_time
cpu = self._cpu_percent
ilag = self._input_lag_ms
last_calc_fps = self._last_calculated_fps
total_frames = float(self._frame_count)
fps = self._fps
last_ft = self._last_frame_time
cpu = self._cpu_percent
ilag = self._input_lag_ms
last_calc_fps = self._last_calculated_fps
total_frames = float(self._frame_count)
timings_snapshot = dict(self._component_timings)
counts_snapshot = dict(self._component_counts)
max_snapshot = dict(self._component_max)
min_snapshot = dict(self._component_min)
counts_snapshot = dict(self._component_counts)
max_snapshot = dict(self._component_max)
min_snapshot = dict(self._component_min)
metrics = {
'fps': fps,
'fps_avg': self._get_avg('fps'),
'fps': fps,
'fps_avg': self._get_avg('fps'),
'last_frame_time_ms': last_ft,
'frame_time_ms_avg': self._get_avg('frame_time_ms'),
'cpu_percent': cpu,
'cpu_percent_avg': self._get_avg('cpu_percent'),
'input_lag_ms': ilag,
'input_lag_ms_avg': self._get_avg('input_lag_ms'),
'total_frames': total_frames
'frame_time_ms_avg': self._get_avg('frame_time_ms'),
'cpu_percent': cpu,
'cpu_percent_avg': self._get_avg('cpu_percent'),
'input_lag_ms': ilag,
'input_lag_ms_avg': self._get_avg('input_lag_ms'),
'total_frames': total_frames
}
for name, elapsed in timings_snapshot.items():
metrics[f'time_{name}_ms'] = elapsed
metrics[f'time_{name}_ms'] = elapsed
metrics[f'time_{name}_ms_avg'] = self._get_avg(f'comp_{name}')
metrics[f'count_{name}'] = float(counts_snapshot.get(name, 0))
metrics[f'max_{name}_ms'] = max_snapshot.get(name, 0.0)
metrics[f'min_{name}_ms'] = min_snapshot.get(name, 0.0)
metrics[f'count_{name}'] = float(counts_snapshot.get(name, 0))
metrics[f'max_{name}_ms'] = max_snapshot.get(name, 0.0)
metrics[f'min_{name}_ms'] = min_snapshot.get(name, 0.0)
return metrics
def get_history(self, key: str) -> List[float]:
"""
Returns a snapshot of the full history buffer for a specific metric key.
[C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions]
Returns a snapshot of the full history buffer for a specific metric key.
[C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state, tests/test_history_manager.py:TestHistoryManager.test_get_history_returns_descriptions]
"""
with self._lock:
if key in self._history:
@@ -287,15 +280,14 @@ class PerformanceMonitor:
def scope(self, name: str) -> PerformanceScope:
"""
Returns a context manager for timing a component.
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager]
Returns a context manager for timing a component.
[C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager]
"""
return PerformanceScope(self, name)
def stop(self) -> None:
"""
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
[C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast]
"""
self._stop_event.set()
if self._cpu_thread.is_alive():