Emily.Telemetry (emily v0.3.2)

Copy Markdown View Source

: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.

A one-shot Logger.warning per {op, input_shapes} pair is opt-in: the span event fires on every fallback, but the log line is off by default so library consumers don't get unsolicited warnings. Turn it on — typically in config/dev.exs — when chasing performance regressions:

config :emily, :warn_on_fallback, true

With it on, a Bumblebee user sees "indexed_put on shape [...] fell back to Nx.BinaryBackend" once per shape, not every forward pass.

Memory stats (poll-driven)

[:emily, :memory, :stats] — discrete event, not a span. Call Emily.Telemetry.memory_stats/0 to sample; measurements:

  • :active — bytes currently held by MLX
  • :peak — high-water mark since last Native.reset_peak_memory/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

memory_stats()

@spec memory_stats() :: %{
  active: non_neg_integer(),
  peak: non_neg_integer(),
  cache: non_neg_integer()
}

Sample the MLX allocator and emit [:emily, :memory, :stats].

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]