GitHub

Docs / Concepts / Profiling

Profilingv0.2.0

You cannot optimize an inference you cannot measure. syn_prof gives every inference a per-stage timing breakdown, a peak-memory figure, and an NPU utilization percentage — captured with the hardware cycle counter, printable from the shell, and free when you turn it off.

What gets measured

One inference produces one syn_prof_result_t:

c
typedef struct {
    uint32_t total_us;            /* Total inference time             */
    uint32_t preprocess_us;       /* Pre-processing time              */
    uint32_t npu_us;              /* NPU execution time               */
    uint32_t postprocess_us;      /* Post-processing time             */
    uint32_t ipc_overhead_us;     /* IPC latency (if cross-core)      */
    uint32_t mem_peak_bytes;      /* Peak SRAM usage during inference */
    uint32_t npu_utilization_pct; /* NPU busy time as % of total      */
} syn_prof_result_t;

Timing is cycle-accurate: the profiler marks stage boundaries with k_cycle_get_32() and converts the deltas with k_cyc_to_us_ceil32(). Peak memory is read from the tensor arena's own statistics (syn_mem_get_stats(), the arena_peak field) at the end of the run, and NPU utilization is computed as NPU time as a percentage of total time. The ipc_overhead_us field is reserved for cross-core inference (see Dual-core design & IPC).

The C API

c
int  syn_prof_enable(void);
int  syn_prof_disable(void);
int  syn_prof_get_last(syn_prof_result_t *result);
void syn_prof_print_summary(void);

/* Per-layer tracing */
int  syn_prof_enable_layer_trace(void);
int  syn_prof_get_layer_time(uint32_t layer_index, uint32_t *us);

syn_prof_get_last() returns the most recent result, or -ENOENT when no inference has been profiled yet. syn_prof_print_summary() dumps the same data to the log. Internally, the inference path calls a set of markers — syn_prof_mark_start(), syn_prof_mark_preprocess_done(), syn_prof_mark_npu_done(), syn_prof_mark_end() — that record the cycle counter at each stage boundary.

Note · Per-layer tracing

The layer-trace entry points exist in the frozen API, but both syn_prof_enable_layer_trace() and syn_prof_get_layer_time() still return -ENOTSUP as of Phase 2. Layer-granularity work is scheduled alongside the later layer-boundary preemption features, which need real per-layer NPU callbacks.

Zero overhead when disabled

Profiling costs nothing when you are not using it, at two levels. At build time, CONFIG_SYNAPTIC_PROFILING (default y if DEBUG) gates the feature entirely — zero overhead when disabled. At runtime, every marker checks the enabled flag first and returns immediately when profiling is off, so syn_prof_disable() reduces a marker to a single branch.

Shell commands

The syn prof command group exposes the profiler over the syn shell:

CommandEffect
syn prof lastPrint the last profiling result (total, preprocess, NPU, postprocess times)
syn prof enableEnable profiling
syn prof disableDisable profiling

After a syn infer run test_classify on the FRDM-MCXN947 (v0.2.0 verification, 2026-07-12; stub NPU backend):

shell
uart:~$ syn prof last
Last inference:
  Total:       1069 us
  Preprocess:  1 us
  NPU:         1068 us
  Postprocess: 1 us
  Memory peak: 1792 bytes

The corresponding NPU utilization is 99% — expected for a bare single-model pipeline where the (stub-simulated) accelerator dominates and the pre/post stages are trivial. On QEMU (icount timing) the hello_inference boot inference profiles at 1010 µs total / 1003 µs NPU.

Status in Phase 2

Phase 2 · Markers wired into the live path

In v0.1.0 the profiler core, the syn_prof_mark_* internal API, and the shell commands were implemented, but no inference path called the markers — syn prof last always answered No profiling data available. As of v0.2.0 the pipeline executor fires the marks at every stage boundary and hello_inference runs through syn_infer_run_sync(), so syn prof last returns real data — verified live on the FRDM-MCXN947 on 2026-07-12 (the capture above), closing the Phase 1 known limitation on hardware. NPU times are the deterministic stub backend. The Phase 1 hardware figures (1038 µs end-to-end on the FRDM board versus 781 µs on QEMU, direct-HAL path) stand unchanged.