Skip to content
heliaPROFILER
User guide
HELIA

Model Explorer overlays

Look at where the cycles go on the graph rather than in a table. hpx writes one overlay JSON per captured counter that Model Explorer renders as a colour over each operator node.

A result bundle with model_explorer/ in it (the default; output.model_explorer is true), the .tflite model that produced it, and Model Explorer in a browser.

File Value on each node
model_explorer/me_overlay_<COUNTER>.json that counter’s per-layer aggregate, one file per counter captured, for example me_overlay_ARM_PMU_CPU_CYCLES.json, me_overlay_ARM_PMU_INST_RETIRED.json, me_overlay_ARM_PMU_MVE_INST_RETIRED.json

Each overlay maps graph node ids to that counter’s raw per-layer value and carries a three-stop gradient (GRADIENT_COST in report/model_explorer.py): Model Explorer interpolates it across the range of values in the file, green at the minimum, yellow at the midpoint and red at the maximum. The colour is relative to the range in that one overlay, so a node’s colour says nothing about absolute cost and cannot be compared between two overlays or two runs.

The file is Model Explorer’s node-data format: a top-level key per graph id (main for a single-subgraph model) holding a name shown in the overlay selector (the counter name), a results map from node key to {"value": <number>}, and the gradient list. It is plain JSON, so jq '.main.results' reads the per-node values when the graph view is not needed; Parsing outputs covers the CSV and summary that carry the same numbers with more context.

  1. Open Model Explorer.
  2. Load the .tflite model the run profiled (the same file; the overlay matches nodes by operator index).
  3. Choose Add Overlay and pick one of the me_overlay_*.json files.
  4. Read the colours: red is the highest value of that counter, green the lowest. Switch overlays to compare, for instance cycles against ARM_PMU_L1D_CACHE_MISS_RD to see whether a hot layer is memory-bound.
  5. Check the picture against the table: the reddest node of the cycles overlay is the first row of summary.top_layers, and its pct says how much of the instrumented total it holds. High cycle cost with many cache misses is a reason to investigate memory placement. Low cache misses alone do not prove a layer is compute-bound; compare stalls, instruction counts and the other available counters before attributing the cost.

A layer that ran in several PMU passes has one value per counter, taken from the pass that captured that counter; a counter the SoC could not capture has no overlay file rather than a file of zeros.

Nodes are matched by the original TFLite operator index, the same source_index that joins MACs on Output files. heliaRT and TFLM run operators in graph order, so their overlays use the sequential operator index (0, 1, 2, …). heliaAOT skips operators such as VAR_HANDLE without renumbering, so its overlays use the original index from the AOT operator manifest, or from the label suffix (the 3 in CONV_2D:3) when the run has no manifest. A layer whose original index cannot be resolved (a position the AOT manifest does not name, or any run whose manifest extraction failed) is left out of the overlay rather than painted onto an unrelated node. ExecuTorch layers name plan instructions, not graph nodes, so an ExecuTorch run writes no overlay files. Each run replaces the me_overlay_*.json files in model_explorer/.

Terminal window
hpx profile model.tflite --no-model-explorer

The model_explorer/ directory is then absent and the manifest lists no overlay export; nothing else in the bundle changes.

A graph whose hottest operators match top_layers in summary.json for the cycles overlay, and whose colour pattern shifts by counter.

PMU counters for what each counter measures; Memory placement when the cache-miss overlay points at weights or activations in a slow tier.