Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cirron.com/llms.txt

Use this file to discover all available pages before exploring further.

ci.trace() returns the current session’s scope tree without leaving the Python process. It is the on-demand companion to the continuous output= sinks: a notebook user can call it after a training cell to see the tree inline; a script can call it mid-run to inspect what has happened so far. The data source is a process-wide in-memory buffer that the flush thread populates each tick. ci.trace() performs a best-effort synchronous drain into the buffer before reading, so spans closed between the last tick and the call are visible immediately.

Signature

def trace(
    format: Literal["tree", "dict", "json", "df"] = "tree",
    name: str | None = None,
    last: int | None = None,
) -> _TraceTreeRepr | dict | str | pandas.DataFrame | None

Parameters

NameTypeDefaultPurpose
formatstr"tree"Output format. One of "tree", "dict", "json", "df".
namestr?NoneKeep only spans whose name matches and their descendants.
lastint?NoneKeep only the N most recently closed spans (ranked by end_ns).

Formats

FormatReturnsNotes
"tree"None (prints) or _TraceTreeReprIn a Jupyter kernel returns a notebook-renderable wrapper; otherwise prints to stdout.
"dict"{"roots": [...], "span_count": int}Nested dict form: each node has name, index, wall_us, attrs, marks, children.
"json"strSame shape as "dict", serialized with json.dumps.
"df"pandas.DataFrameFlat: one row per span. Columns: id, parent_id, name, index, wall_us, cpu_ns, gpu_ns, memory_peak_bytes, mark_count, depth. Requires cirron-sdk[pandas].
format="df" raises CirronDependencyError when pandas is not installed, with a pip install hint.

Examples

import cirron as ci

ci.profile(output="none")     # purely in-memory; no spool writes
for epoch in ci.epochs(range(3)):
    for batch in ci.batches(loader):
        loss = train_step(batch)
        ci.mark("loss", loss.item())

ci.trace()
# epoch[0] - 142us {epoch_loss=0.5234}
#   batch[0] - 12us {loss=0.6124}
#   batch[1] - 11us {loss=0.5410}
#   ...

Render in a Jupyter cell

ci.trace()       # last expression renders inline; nothing prints to stdout
The Jupyter detection probes IPython.get_ipython() and only treats the ZMQ kernel as a notebook. A plain ipython REPL still prints.

Custom analysis with a DataFrame

df = ci.trace(format="df")
df[df["name"] == "forward"]["wall_us"].quantile([0.5, 0.95, 0.99])
df.groupby("name")["wall_us"].agg(["count", "mean", "max"])

Filter to a single scope name

epochs = ci.trace(format="dict", name="epoch")
# Returns only the epoch subtrees and their children.

Recent spans only

ci.trace(format="dict", last=5)
# The 5 most recently closed spans, ranked by end_ns.

Behavior with no profiler attached

ci.trace() works without a prior ci.profile() call. In that mode it drains the in-memory buffer directly and never writes a spool file as a side effect, so it is safe on read-only filesystems and in notebook inspection contexts.
import cirron as ci

with ci.scope("standalone"):
    ci.mark("x", 1)

ci.trace()    # no profiler, no spool writes, just the tree
When a profiler is active, the synchronous drain routes through ci.flush() so the user’s configured output= sinks still fire (an output="spool" run continues to land batches on disk).

Buffer bounds

The in-memory buffer is bounded:
  • Spans: keeps the most recent trace_buffer_max_spans (default 100_000).
  • Marks per span: keeps every kind="summary" mark and the most recent 1024 kind="point" marks. This bound matters for long-lived open spans like cirron.session whose span_id never appears in the span eviction list.
Tune via Cirron:
from cirron import Cirron
c = Cirron(trace_buffer_max_spans=10_000)
c.profile()

output= sinks

Continuous spool / log / stdout streaming as scopes close.

Lifecycle

flush, health, shutdown.