ExSystolic.Trace (ex_systolic v0.2.0)

Copy Markdown View Source

Execution trace recording for systolic array runs.

Every tick optionally emits a Trace.Event per PE, recording inputs, outputs, and state transitions. The trace is:

  • optional -- disabled by default to avoid overhead
  • complete -- when enabled, every PE at every tick is recorded
  • deterministic -- same inputs always produce the same trace

Storage and ordering

Internally, events are stored in reverse-chronological order (newest event first) so that recording is O(1) amortized. The query helpers at/2 and for_coord/2 return their results in chronological order (oldest first), matching the order in which events were recorded. If you access trace.events directly, call Enum.reverse/1 to obtain chronological order.

Use cases

  • Debugging PE behaviour
  • Visualising data flow (e.g. in a livebook)
  • Regression testing (compare traces across runs)

Summary

Functions

Filters trace events by tick number, returned in chronological order.

Filters trace events by PE coordinate, returned in chronological order.

Creates a new trace from a list of events.

Records a single PE step as a trace event.

Types

event()

@type event() :: ExSystolic.Trace.Event.t()

t()

@type t() :: %ExSystolic.Trace{events: [event()]}

Functions

at(trace, tick)

@spec at(t(), non_neg_integer()) :: [event()]

Filters trace events by tick number, returned in chronological order.

Examples

iex> e0 = %ExSystolic.Trace.Event{tick: 0, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> e1 = %ExSystolic.Trace.Event{tick: 1, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> trace = ExSystolic.Trace.new() |> ExSystolic.Trace.record(e0) |> ExSystolic.Trace.record(e1)
iex> [event] = ExSystolic.Trace.at(trace, 0)
iex> event.tick
0

for_coord(trace, coord)

@spec for_coord(t(), ExSystolic.Grid.coord()) :: [event()]

Filters trace events by PE coordinate, returned in chronological order.

Examples

iex> e0 = %ExSystolic.Trace.Event{tick: 0, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> e1 = %ExSystolic.Trace.Event{tick: 1, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 0}
iex> trace = ExSystolic.Trace.new() |> ExSystolic.Trace.record(e0) |> ExSystolic.Trace.record(e1)
iex> ticks = ExSystolic.Trace.for_coord(trace, {0,0}) |> Enum.map(& &1.tick)
iex> ticks
[0, 1]

new(events \\ [])

@spec new([event()]) :: t()

Creates a new trace from a list of events.

Examples

iex> trace = ExSystolic.Trace.new([])
iex> trace.events
[]

record(trace, event)

@spec record(t(), event()) :: t()

Records a single PE step as a trace event.

Events are prepended for O(1) amortized recording. Use at/2 or for_coord/2 to retrieve in chronological order.

Examples

iex> trace = ExSystolic.Trace.new()
iex> event = %ExSystolic.Trace.Event{tick: 0, coord: {0,0}, inputs: %{}, outputs: %{}, state_before: 0, state_after: 7}
iex> trace2 = ExSystolic.Trace.record(trace, event)
iex> hd(trace2.events).state_after
7
iex> hd(trace2.events).tick
0