:telemetry events emitted by Emily.
All span-style events use :telemetry.span/3 semantics, so attaching
to *:start, *:stop, and *:exception is sufficient for
histograms and error tracking.
Events
Evaluation boundaries
[:emily, :eval, :start | :stop | :exception] — Emily.eval/1.
The :stop event carries :duration (monotonic native units).
[:emily, :to_binary, :start | :stop | :exception] — fires for both
Emily.to_binary/1 and the Nx.to_binary/1 path on Emily.Backend.
Metadata: :byte_size, :shape, :dtype.
Fallback entry
[:emily, :fallback, :start | :stop | :exception] — fires whenever
an op routes through Nx.BinaryBackend because the MLX path is not
wired. Metadata: :op, :input_shapes, :input_dtypes.
Per-fallback behaviour is configurable via :fallback:
config :emily, fallback: :silent | :warn | :raise:silent(default) — span events still fire, no log, no raise. Library consumers and CI logs stay quiet.:warn— one-shotLogger.warningper{op, input_shapes}pair. A Bumblebee user sees"indexed_put on shape [...] fell back to Nx.BinaryBackend"once per shape, not every forward pass. Typically set inconfig/dev.exswhile chasing performance regressions.:raise— raises aRuntimeErrorcarrying the op, input shapes, and input dtypes. Use in CI to fail builds when a hot path unexpectedly routes throughNx.BinaryBackend.
In :raise mode the :start/:stop span events do not fire
because the raise happens on entry; :silent and :warn preserve
the full span.
The legacy :warn_on_fallback boolean is still honoured when
:fallback is unset (true → :warn, false → :silent).
Prefer :fallback in new code; if both are set, :fallback wins.
Memory stats (poll-driven)
[:emily, :memory, :stats] — discrete event, not a span. Call
Emily.Memory.stats/0 to sample; measurements:
:active— bytes currently held by MLX:peak— high-water mark since lastEmily.Memory.reset_peak/0:cache— bytes cached for reuse
Wire this into a periodic task (e.g. Process.send_after/3 loop) to
graph memory drift in a long-running serving.
Attaching a handler
:telemetry.attach(
"emily-fallback-log",
[:emily, :fallback, :stop],
fn _event, measurements, metadata, _config ->
IO.inspect({metadata.op, measurements.duration})
end,
nil
)
Summary
Functions
Sample the MLX allocator and emit [:emily, :memory, :stats].
Functions
@spec memory_stats() :: %{ active: non_neg_integer(), peak: non_neg_integer(), cache: non_neg_integer() }
Sample the MLX allocator and emit [:emily, :memory, :stats].
Prefer Emily.Memory.stats/0 in new code. This function remains as
the telemetry-oriented entry point for existing callers.
Returns the measurements map so callers can also log or plot inline.
Examples
iex> stats = Emily.Telemetry.memory_stats()
iex> Map.keys(stats) |> Enum.sort()
[:active, :cache, :peak]