checkpoint: massive refactor
This commit is contained in:
@@ -3,132 +3,117 @@ import psutil
|
||||
import threading
|
||||
|
||||
class PerformanceMonitor:
|
||||
def __init__(self):
|
||||
self._start_time = None
|
||||
self._last_frame_time = 0.0
|
||||
self._fps = 0.0
|
||||
self._frame_count = 0
|
||||
self._fps_last_time = time.time()
|
||||
self._process = psutil.Process()
|
||||
self._cpu_usage = 0.0
|
||||
self._cpu_lock = threading.Lock()
|
||||
|
||||
# Input lag tracking
|
||||
self._last_input_time = None
|
||||
self._input_lag_ms = 0.0
|
||||
|
||||
# Alerts
|
||||
self.alert_callback = None
|
||||
self.thresholds = {
|
||||
'frame_time_ms': 33.3, # < 30 FPS
|
||||
'cpu_percent': 80.0,
|
||||
'input_lag_ms': 100.0
|
||||
}
|
||||
self._last_alert_time = 0
|
||||
self._alert_cooldown = 30 # seconds
|
||||
def __init__(self):
|
||||
self._start_time = None
|
||||
self._last_frame_time = 0.0
|
||||
self._fps = 0.0
|
||||
self._frame_count = 0
|
||||
self._fps_last_time = time.time()
|
||||
self._process = psutil.Process()
|
||||
self._cpu_usage = 0.0
|
||||
self._cpu_lock = threading.Lock()
|
||||
# Input lag tracking
|
||||
self._last_input_time = None
|
||||
self._input_lag_ms = 0.0
|
||||
# Alerts
|
||||
self.alert_callback = None
|
||||
self.thresholds = {
|
||||
'frame_time_ms': 33.3, # < 30 FPS
|
||||
'cpu_percent': 80.0,
|
||||
'input_lag_ms': 100.0
|
||||
}
|
||||
self._last_alert_time = 0
|
||||
self._alert_cooldown = 30 # seconds
|
||||
# Detailed profiling
|
||||
self._component_timings = {}
|
||||
self._comp_start = {}
|
||||
# Start CPU usage monitoring thread
|
||||
self._stop_event = threading.Event()
|
||||
self._cpu_thread = threading.Thread(target=self._monitor_cpu, daemon=True)
|
||||
self._cpu_thread.start()
|
||||
|
||||
# Detailed profiling
|
||||
self._component_timings = {}
|
||||
self._comp_start = {}
|
||||
|
||||
# Start CPU usage monitoring thread
|
||||
self._stop_event = threading.Event()
|
||||
self._cpu_thread = threading.Thread(target=self._monitor_cpu, daemon=True)
|
||||
self._cpu_thread.start()
|
||||
def _monitor_cpu(self):
|
||||
while not self._stop_event.is_set():
|
||||
# psutil.cpu_percent with interval=1.0 is blocking for 1 second.
|
||||
# To be responsive to stop_event, we use a smaller interval or no interval
|
||||
# and handle the timing ourselves.
|
||||
try:
|
||||
usage = self._process.cpu_percent()
|
||||
with self._cpu_lock:
|
||||
self._cpu_usage = usage
|
||||
except Exception:
|
||||
pass
|
||||
# Sleep in small increments to stay responsive to stop_event
|
||||
for _ in range(10):
|
||||
if self._stop_event.is_set():
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
def _monitor_cpu(self):
|
||||
while not self._stop_event.is_set():
|
||||
# psutil.cpu_percent with interval=1.0 is blocking for 1 second.
|
||||
# To be responsive to stop_event, we use a smaller interval or no interval
|
||||
# and handle the timing ourselves.
|
||||
try:
|
||||
usage = self._process.cpu_percent()
|
||||
with self._cpu_lock:
|
||||
self._cpu_usage = usage
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Sleep in small increments to stay responsive to stop_event
|
||||
for _ in range(10):
|
||||
if self._stop_event.is_set():
|
||||
break
|
||||
time.sleep(0.1)
|
||||
def start_frame(self):
|
||||
self._start_time = time.time()
|
||||
|
||||
def start_frame(self):
|
||||
self._start_time = time.time()
|
||||
def record_input_event(self):
|
||||
self._last_input_time = time.time()
|
||||
|
||||
def record_input_event(self):
|
||||
self._last_input_time = time.time()
|
||||
def start_component(self, name: str):
|
||||
self._comp_start[name] = time.time()
|
||||
|
||||
def start_component(self, name: str):
|
||||
self._comp_start[name] = time.time()
|
||||
def end_component(self, name: str):
|
||||
if name in self._comp_start:
|
||||
elapsed = (time.time() - self._comp_start[name]) * 1000.0
|
||||
self._component_timings[name] = elapsed
|
||||
|
||||
def end_component(self, name: str):
|
||||
if name in self._comp_start:
|
||||
elapsed = (time.time() - self._comp_start[name]) * 1000.0
|
||||
self._component_timings[name] = elapsed
|
||||
def end_frame(self):
|
||||
if self._start_time is None:
|
||||
return
|
||||
end_time = time.time()
|
||||
self._last_frame_time = (end_time - self._start_time) * 1000.0
|
||||
self._frame_count += 1
|
||||
# Calculate input lag if an input occurred during this frame
|
||||
if self._last_input_time is not None:
|
||||
self._input_lag_ms = (end_time - self._last_input_time) * 1000.0
|
||||
self._last_input_time = None
|
||||
self._check_alerts()
|
||||
elapsed_since_fps = end_time - self._fps_last_time
|
||||
if elapsed_since_fps >= 1.0:
|
||||
self._fps = self._frame_count / elapsed_since_fps
|
||||
self._frame_count = 0
|
||||
self._fps_last_time = end_time
|
||||
|
||||
def end_frame(self):
|
||||
if self._start_time is None:
|
||||
return
|
||||
|
||||
end_time = time.time()
|
||||
self._last_frame_time = (end_time - self._start_time) * 1000.0
|
||||
self._frame_count += 1
|
||||
|
||||
# Calculate input lag if an input occurred during this frame
|
||||
if self._last_input_time is not None:
|
||||
self._input_lag_ms = (end_time - self._last_input_time) * 1000.0
|
||||
self._last_input_time = None
|
||||
def _check_alerts(self):
|
||||
if not self.alert_callback:
|
||||
return
|
||||
now = time.time()
|
||||
if now - self._last_alert_time < self._alert_cooldown:
|
||||
return
|
||||
metrics = self.get_metrics()
|
||||
alerts = []
|
||||
if metrics['last_frame_time_ms'] > self.thresholds['frame_time_ms']:
|
||||
alerts.append(f"Frame time high: {metrics['last_frame_time_ms']:.1f}ms")
|
||||
if metrics['cpu_percent'] > self.thresholds['cpu_percent']:
|
||||
alerts.append(f"CPU usage high: {metrics['cpu_percent']:.1f}%")
|
||||
if metrics['input_lag_ms'] > self.thresholds['input_lag_ms']:
|
||||
alerts.append(f"Input lag high: {metrics['input_lag_ms']:.1f}ms")
|
||||
if alerts:
|
||||
self._last_alert_time = now
|
||||
self.alert_callback("; ".join(alerts))
|
||||
|
||||
self._check_alerts()
|
||||
|
||||
elapsed_since_fps = end_time - self._fps_last_time
|
||||
if elapsed_since_fps >= 1.0:
|
||||
self._fps = self._frame_count / elapsed_since_fps
|
||||
self._frame_count = 0
|
||||
self._fps_last_time = end_time
|
||||
def get_metrics(self):
|
||||
with self._cpu_lock:
|
||||
cpu_usage = self._cpu_usage
|
||||
metrics = {
|
||||
'last_frame_time_ms': self._last_frame_time,
|
||||
'fps': self._fps,
|
||||
'cpu_percent': cpu_usage,
|
||||
'input_lag_ms': self._last_input_time if self._last_input_time else 0.0 # Wait, this should be the calculated lag
|
||||
}
|
||||
# Oops, fixed the input lag logic in previous turn, let's keep it consistent
|
||||
metrics['input_lag_ms'] = self._input_lag_ms
|
||||
# Add detailed timings
|
||||
for name, elapsed in self._component_timings.items():
|
||||
metrics[f'time_{name}_ms'] = elapsed
|
||||
return metrics
|
||||
|
||||
def _check_alerts(self):
|
||||
if not self.alert_callback:
|
||||
return
|
||||
|
||||
now = time.time()
|
||||
if now - self._last_alert_time < self._alert_cooldown:
|
||||
return
|
||||
|
||||
metrics = self.get_metrics()
|
||||
alerts = []
|
||||
if metrics['last_frame_time_ms'] > self.thresholds['frame_time_ms']:
|
||||
alerts.append(f"Frame time high: {metrics['last_frame_time_ms']:.1f}ms")
|
||||
if metrics['cpu_percent'] > self.thresholds['cpu_percent']:
|
||||
alerts.append(f"CPU usage high: {metrics['cpu_percent']:.1f}%")
|
||||
if metrics['input_lag_ms'] > self.thresholds['input_lag_ms']:
|
||||
alerts.append(f"Input lag high: {metrics['input_lag_ms']:.1f}ms")
|
||||
|
||||
if alerts:
|
||||
self._last_alert_time = now
|
||||
self.alert_callback("; ".join(alerts))
|
||||
|
||||
def get_metrics(self):
|
||||
with self._cpu_lock:
|
||||
cpu_usage = self._cpu_usage
|
||||
|
||||
metrics = {
|
||||
'last_frame_time_ms': self._last_frame_time,
|
||||
'fps': self._fps,
|
||||
'cpu_percent': cpu_usage,
|
||||
'input_lag_ms': self._last_input_time if self._last_input_time else 0.0 # Wait, this should be the calculated lag
|
||||
}
|
||||
# Oops, fixed the input lag logic in previous turn, let's keep it consistent
|
||||
metrics['input_lag_ms'] = self._input_lag_ms
|
||||
|
||||
# Add detailed timings
|
||||
for name, elapsed in self._component_timings.items():
|
||||
metrics[f'time_{name}_ms'] = elapsed
|
||||
|
||||
return metrics
|
||||
|
||||
def stop(self):
|
||||
self._stop_event.set()
|
||||
self._cpu_thread.join(timeout=2.0)
|
||||
def stop(self):
|
||||
self._stop_event.set()
|
||||
self._cpu_thread.join(timeout=2.0)
|
||||
|
||||
Reference in New Issue
Block a user