Source code for ralph.phases.timing
"""Phase timing utilities.
Provides monotonic-clock helpers and two dataclasses for measuring how long
each pipeline phase takes:
- ``PhaseTimer`` - start timing a phase with ``PhaseTimer.start(phase)`` and
stop it with ``timer.finish()`` to get a ``PhaseTimingRecord``.
- ``PhaseTimingRecord`` - frozen record holding the phase name, iteration
number, start timestamp, and elapsed ``timedelta`` / whole-second count.
All time values use ``time.monotonic`` so they are safe across system-clock
adjustments. Elapsed seconds are truncated (not rounded) to whole integers.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
from time import monotonic
from ralph.phases.phase_timing_record import PhaseTimingRecord
[docs]
def capture_time() -> float:
"""Return a monotonic timestamp suitable for elapsed-time calculations."""
return monotonic()
[docs]
def elapsed(start: float) -> timedelta:
"""Return the duration since ``start`` using a monotonic clock."""
return timedelta(seconds=max(0.0, monotonic() - start))
[docs]
def elapsed_seconds(start: float) -> int:
"""Return whole elapsed seconds since ``start``."""
return int(elapsed(start).total_seconds())
[docs]
@dataclass(frozen=True)
class PhaseTimer:
"""Simple helper for measuring phase execution durations."""
phase: str
iteration: int
started_at: float
[docs]
@classmethod
def start(cls, phase: str, *, iteration: int = 0) -> PhaseTimer:
"""Start timing a phase."""
return cls(phase=phase, iteration=iteration, started_at=capture_time())
[docs]
def finish(self) -> PhaseTimingRecord:
"""Return a completed timing record for the phase."""
duration = elapsed(self.started_at)
return PhaseTimingRecord(
phase=self.phase,
iteration=self.iteration,
started_at=self.started_at,
elapsed=duration,
elapsed_seconds=int(duration.total_seconds()),
)
__all__ = [
"PhaseTimer",
"PhaseTimingRecord",
"capture_time",
"elapsed",
"elapsed_seconds",
]