Tracing
Tracing
Tutorial: TracingSee also: API: language
Motivation
Writing config files as explicit L.call trees is safe and composable, but
it means maintaining two parallel representations: the training code and the
config. Tracing lets you write the training code directly and derive the config
from it automatically.
@L.configurable
Mark a function or class as traceable:
@L.configurable
def build_model(in_features: int, out_features: int) -> nn.Module:
return nn.Linear(in_features, out_features)
Outside a trace, build_model(784, 10) behaves normally: it builds and
returns an nn.Module.
Inside a trace, the call is recorded rather than executed. The function body
does not run; instead a L.call(build_model)(in_features=784, out_features=10)
node is produced.
L.trace
cfg = L.trace(lambda: build_model(in_features=784, out_features=10))
# cfg is a DictConfig, not an nn.Module
L.trace(thunk) activates the _TRACING ContextVar for the duration of
the thunk, then deactivates it via try/finally (so exceptions don't leave
tracing active). The return value is the root DictConfig of the recorded
call graph.
Argument Tiers
Arguments passed to a @L.configurable function inside a trace are classified:
| Tier | Type | Behavior |
|---|---|---|
| 1 | DictConfig | Spliced in as a sub-node |
| 2 | Primitive (int, float, str, bool, None) | Kept as-is |
| 3 | Other | TypeError: non-serializable argument inside trace |
Positional → Named Binding
Positional arguments are converted to named arguments using inspect.Signature:
@L.configurable
def build(in_features: int, out_features: int) -> nn.Module: ...
cfg = L.trace(lambda: build(784, 10))
# equivalent to: L.call(build)(in_features=784, out_features=10)
*args (VAR_POSITIONAL) raises TypeError inside a trace: variadic
positionals cannot be represented in a keyed config node.
Strict Mode
cfg = L.trace(thunk, strict=True)
strict=True (default) raises TypeError if the root of the trace does not
produce a DictConfig. Use strict=False to fall back to the executed value
when the thunk's root call is not @L.configurable.
Purity Requirement
The thunk body should be pure (no side effects): it will run only once and its side effects will be skipped inside the trace. In particular, avoid logging, file I/O, or random number generation inside a thunk.
When to Use Tracing vs Explicit L.call
| Situation | Recommendation |
|---|---|
| Config file with fixed targets | L.call, explicit, zero overhead |
| Code-first workflow; config derived from existing functions | L.trace |
Need typed groups or L.Defaults | L.call + L.Group |
| Wrapping a third-party function you can't decorate | L.call with the function as target |