padne.context ============= .. py:module:: padne.context .. autoapi-nested-parse:: Process-wide context for cross-cutting concerns. Currently hosts the stage-timing API used to measure how long named stages take across the codebase. Usage: with context.timing_session() as session: with context.stage_timer("connectivity"): ... # -> "padne.solver.connectivity" @context.stage_timer def assemble_system(...): ... # -> "padne.solver.assemble_system" @context.stage_timer("custom_name") def f(...): ... # -> ".custom_name" with session.durations as d: ... # dict[str, Session.Duration] The module-level stage_timer is a no-op when called outside an active timing_session. `Session` exposes the same polymorphic `stage_timer` API directly, so once you have a session reference you can also do: @session.stage_timer # key = "." def f(): ... with session.stage_timer("padne.solver.something"): ... When invoked on a Session directly the string is the *full* key with no caller-module prefix. The module-level wrapper is what adds the prefix. Attributes ---------- .. autoapisummary:: padne.context.T Classes ------- .. autoapisummary:: padne.context.LockedObject padne.context.Session Functions --------- .. autoapisummary:: padne.context.timing_session padne.context.merge_durations padne.context.stage_timer Module Contents --------------- .. py:data:: T .. py:class:: LockedObject(obj: T) Bases: :py:obj:`Generic`\ [\ :py:obj:`T`\ ] Wraps a value so it cannot be accessed without holding the lock. owned = LockedObject([]) with owned as items: items.append(...) .. py:class:: Session A single bag of stage timings shared across all threads that run inside one timing_session() block. .. py:class:: Duration .. py:attribute:: perf_time :type: float :value: 0.0 .. py:attribute:: process_time :type: float :value: 0.0 .. py:attribute:: call_count :type: int :value: 0 .. py:attribute:: durations .. py:method:: stage_timer(arg) Polymorphic: with session.stage_timer("full.key"): ... # CM @session.stage_timer # bare decorator @session.stage_timer("full.key") # parameterized decorator .. py:method:: snapshot() -> dict[str, Session] Return an independent copy of the recorded durations. Used to ship timings across process boundaries (see padne.parallel). .. py:method:: merge(snapshot: dict[str, Session]) -> None Accumulate durations from another session's snapshot into this one. Times sum and call counts add, same as repeated stage entries. .. py:method:: format_summary() -> str Return a tree-shaped table of recorded stages. Keys are split on '.' to form a hierarchy; siblings are sorted by descending perf time (a branch's sort key is the sum of its descendants' perf time, with leaf keys contributing their own recorded perf time). .. py:function:: timing_session() Open a timing session. While active, module-level stage_timer entries record into it. TODO: support multiple concurrent independent sessions. Right now we only allow one active session per process and raise if a second is opened. The check-and-set below races for that case; it's only safe today because the single-session restriction means nobody opens two in parallel. .. py:function:: merge_durations(snapshot: dict[str, Session]) -> None Merge a durations snapshot into the active session. No-op when no session is active, mirroring the stage_timer behavior. .. py:function:: stage_timer(arg) Module-level wrapper that adds the caller-module prefix and defers session lookup to call/enter time. with stage_timer("name"): # key = ".name" ... @stage_timer # key = "." def f(): ... @stage_timer("name") # key = ".name" def g(): ...