STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
calibration_performance_monitor.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""
3Performance Monitor for Multi-Threaded Calibration System
4
5Tracks:
6- Thread CPU usage
7- Consensus computation time
8- Calibration update time
9- Memory usage
10- Queue depths
11
12Usage:
13 python calibration_performance_monitor.py
14"""
15
16import time
17import threading
18import psutil
19import logging
20from dataclasses import dataclass
21from typing import Dict, List
22
23logging.basicConfig(level=logging.INFO)
24logger = logging.getLogger(__name__)
25
26
27@dataclass
29 timestamp: float
30 main_thread_cpu: float
31 consensus_thread_cpu: float
32 calibration_thread_cpu: float
33 total_memory_mb: float
34 consensus_time_ms: float
35 calibration_time_ms: float
36 consensus_queue_depth: int
37 calibration_queue_depth: int
38
39
41 def __init__(self):
42 self.metrics_history: List[PerformanceMetrics] = []
43 self.consensus_times: List[float] = []
44 self.calibration_times: List[float] = []
45 self._running = False
46 self._thread = None
47
48 # Process handle for CPU monitoring
49 self.process = psutil.Process()
50
51 def start(self):
52 """Start monitoring in background thread"""
53 self._running = True
54 self._thread = threading.Thread(target=self._monitor_loop, daemon=True)
55 self._thread.start()
56 logger.info("Performance monitor started")
57
58 def stop(self):
59 """Stop monitoring"""
60 self._running = False
61 if self._thread:
62 self._thread.join(timeout=1.0)
63 logger.info("Performance monitor stopped")
64
65 def record_consensus_time(self, time_ms: float):
66 """Record time taken for consensus computation"""
67 self.consensus_times.append(time_ms)
68 if len(self.consensus_times) > 100:
69 self.consensus_times = self.consensus_times[-100:]
70
71 def record_calibration_time(self, time_ms: float):
72 """Record time taken for calibration update"""
73 self.calibration_times.append(time_ms)
74 if len(self.calibration_times) > 100:
75 self.calibration_times = self.calibration_times[-100:]
76
77 def _monitor_loop(self):
78 """Background monitoring loop"""
79 while self._running:
80 try:
81 # Get per-thread CPU usage
82 threads = self.process.threads()
83
84 # Memory usage
85 memory_mb = self.process.memory_info().rss / 1024 / 1024
86
87 # Average times
88 avg_consensus_ms = (
89 sum(self.consensus_times) / len(self.consensus_times)
90 if self.consensus_times
91 else 0
92 )
93 avg_calibration_ms = (
94 sum(self.calibration_times) / len(self.calibration_times)
95 if self.calibration_times
96 else 0
97 )
98
99 # Create metrics record
100 metrics = PerformanceMetrics(
101 timestamp=time.time(),
102 main_thread_cpu=0.0, # Would need thread-specific monitoring
103 consensus_thread_cpu=0.0,
104 calibration_thread_cpu=0.0,
105 total_memory_mb=memory_mb,
106 consensus_time_ms=avg_consensus_ms,
107 calibration_time_ms=avg_calibration_ms,
108 consensus_queue_depth=0, # Would need access to queue objects
109 calibration_queue_depth=0,
110 )
111
112 self.metrics_history.append(metrics)
113 if len(self.metrics_history) > 1000:
114 self.metrics_history = self.metrics_history[-1000:]
115
116 time.sleep(1.0) # Monitor every second
117
118 except Exception as e:
119 logger.error(f"Performance monitoring error: {e}")
120
121 def get_summary(self) -> Dict:
122 """Get performance summary"""
123 if not self.metrics_history:
124 return {}
125
126 recent = self.metrics_history[-60:] # Last minute
127
128 return {
129 "avg_memory_mb": sum(m.total_memory_mb for m in recent) / len(recent),
130 "max_memory_mb": max(m.total_memory_mb for m in recent),
131 "avg_consensus_ms": sum(m.consensus_time_ms for m in recent) / len(recent),
132 "max_consensus_ms": max(m.consensus_time_ms for m in recent),
133 "avg_calibration_ms": sum(m.calibration_time_ms for m in recent)
134 / len(recent),
135 "max_calibration_ms": max(m.calibration_time_ms for m in recent),
136 "samples": len(recent),
137 }
138
139 def print_summary(self):
140 """Print performance summary to console"""
141 summary = self.get_summary()
142 if not summary:
143 logger.info("No performance data yet")
144 return
145
146 logger.info("=== Performance Summary (Last 60s) ===")
147 logger.info(
148 f"Memory: {summary['avg_memory_mb']:.1f} MB avg, {summary['max_memory_mb']:.1f} MB max"
149 )
150 logger.info(
151 f"Consensus: {summary['avg_consensus_ms']:.1f} ms avg, {summary['max_consensus_ms']:.1f} ms max"
152 )
153 logger.info(
154 f"Calibration: {summary['avg_calibration_ms']:.1f} ms avg, {summary['max_calibration_ms']:.1f} ms max"
155 )
156 logger.info(f"Samples: {summary['samples']}")
157
158
159# Example usage in channel_plotter.py:
160#
161# from calibration_performance_monitor import PerformanceMonitor
162#
163# class App(QtWidgets.QMainWindow):
164# def __init__(self):
165# ...
166# self.perf_monitor = PerformanceMonitor()
167# self.perf_monitor.start()
168#
169# # In ConsensusWorkerThread.run():
170# start = time.time()
171# consensus = self.global_system.compute_consensus_pressure(...)
172# elapsed_ms = (time.time() - start) * 1000
173# if hasattr(self, 'perf_monitor'):
174# self.perf_monitor.record_consensus_time(elapsed_ms)
175#
176# # Similar for calibration worker
177
178if __name__ == "__main__":
179 # Test the monitor
181 monitor.start()
182
183 try:
184 while True:
185 time.sleep(5)
186 monitor.print_summary()
187 except KeyboardInterrupt:
188 monitor.stop()
#define max(a, b)
Definition Arduino.h:43
TOML_NODISCARD_CTOR time() noexcept=default