Spaces:
Running
Running
| """Performance logger for tracking resource usage across analysis phases.""" | |
| import time | |
| from contextlib import contextmanager | |
| from datetime import datetime | |
| import numpy as np | |
| import psutil | |
| class PerfLogger: | |
| def __init__(self, job_id): | |
| self.job_id = job_id | |
| self.process = psutil.Process() | |
| self.phases = {} | |
| self.loop_stats = [] | |
| def phase(self, name): | |
| """Track major phases: load, configure, loop, finalize, write""" | |
| start_time = time.perf_counter() | |
| start_mem = self.process.memory_info().rss / 1024**2 | |
| yield | |
| self.phases[name] = { | |
| "elapsed_sec": time.perf_counter() - start_time, | |
| "memory_start_mb": start_mem, | |
| "memory_end_mb": self.process.memory_info().rss / 1024**2, | |
| } | |
| self.phases[name]["memory_delta_mb"] = ( | |
| self.phases[name]["memory_end_mb"] - start_mem | |
| ) | |
| def loop_item(self, iteration, log_every=10): | |
| """Track individual loop iterations, conditionally""" | |
| if iteration % log_every != 0: | |
| yield | |
| return | |
| start_time = time.perf_counter() | |
| yield | |
| self.loop_stats.append( | |
| { | |
| "iteration": iteration, | |
| "elapsed_sec": time.perf_counter() - start_time, | |
| "memory_mb": self.process.memory_info().rss / 1024**2, | |
| } | |
| ) | |
| def log_report(self, phase=None, context=""): | |
| """Generate formatted report for specific phase or most recent""" | |
| if not self.phases: | |
| return "No phases completed yet" | |
| phase_name = phase or list(self.phases.keys())[-1] | |
| if phase_name not in self.phases: | |
| return f"Phase '{phase_name}' not found" | |
| stats = self.phases[phase_name] | |
| prefix = f"[{context}] " if context else "" | |
| return ( | |
| f"{prefix}{phase_name}: " | |
| f"{stats['elapsed_sec']:.2f}s, " | |
| f"Δmem: {stats['memory_delta_mb']:+.1f}MB, " | |
| f"mem: {stats['memory_end_mb']:.1f}MB" | |
| ) | |
| def to_metadata(self): | |
| """Export as dict for dataframe row""" | |
| meta = { | |
| "job_id": self.job_id, | |
| "timestamp": datetime.now().isoformat(), | |
| } | |
| # Flatten phases | |
| for phase_name, stats in self.phases.items(): | |
| for stat_name, value in stats.items(): | |
| meta[f"{phase_name}_{stat_name}"] = value | |
| # Loop summary stats | |
| if self.loop_stats: | |
| loop_times = [s["elapsed_sec"] for s in self.loop_stats] | |
| meta["loop_mean_sec"] = np.mean(loop_times) | |
| meta["loop_max_sec"] = np.max(loop_times) | |
| meta["loop_n_samples"] = len(self.loop_stats) | |
| return meta | |