This content is for v0.12.0. Switch to the latest version for up-to-date documentation.
When a Model Does Not Fit
tigris analyze either says the model fits or names the stages that are too big.
This page is about the second case: what the report is telling you, and which levers
move the number.
Every figure below is from MobileNetV2 as published in the ONNX model zoo, at its 224x224 input, measured with the CLI.
Start from the report
Section titled “Start from the report”tigris analyze mobilenetv2.onnx -m 256Kwarning: input axis 0 (batch_size) is unset; using 1 (--input-shape overrides)mobilenetv2.onnx float32, 65 operators input input 1x3x224x224 float32 output output 1x1000 float32fits 256.00 KiB fast memory, 58 stages, 53 tiled
memory unscheduled 5.74 MiB largest tensor 4.59 MiB 1x96x112x112 this plan 252.00 KiB 4.00 KiB headroom slow memory 5.17 MiB slow traffic 20.25 MiB per inference: 7.21 MiB written, 13.04 MiB read also fits at 128.00 KiB 64 stages, 62 tiled also fits at 64.00 KiB 64 stages, 63 tiled does not fit at 32.00 KiB
flash plan 13.31 MiB weights 13.30 MiB, overhead 8.22 KiB as int8 3.33 MiB estimateUnscheduled is the high-water mark of simultaneously live activations with no partitioning, so it is the arena a single-stage execution would need. It is a property of the model.
This plan is what the plan uses after temporal partitioning and tiling, and it is a property of the budget you gave it. The solver takes the largest tile that fits, because fewer tiles mean less halo recompute and fewer stage boundaries, so raising the budget raises the figure and lowers the stage count:
-m |
This plan | Stages | Tiled |
|---|---|---|---|
| 64K | 63.56 KiB | 64 | 63 |
| 256K | 252.00 KiB | 58 | 53 |
| 512K | 504.88 KiB | 43 | 38 |
| 1M | 1008.00 KiB | 17 | 15 |
So this figure does not tell you whether a smaller part would work. The also fits at
rows do, and the floor appears once the budget is too small.
The three ways a compile fails
Section titled “The three ways a compile fails”A stage does not fit the fast arena
Section titled “A stage does not fit the fast arena”Same model at 32 KiB:
does not fit 32.00 KiB fast memory: 3 stages exceed it stage 51 37.50 KiB smallest 2D tile stage 55 37.50 KiB smallest 2D tile stage 59 37.50 KiB smallest 2D tileEach line is a stage the solver has already shrunk to its smallest tile, with the fast
memory that tile still needs. The first line is the floor: no budget below 37.50 KiB
compiles, and the fits at row below the verdict names the next budget that does.
compile prints the same summary and ends with Error: no plan written.
Stages 51, 55 and 59 are 960-channel depthwise convolutions, which tile in both
height and width; their smallest tile reads a 3x3x960 window and writes 960 outputs,
37.50 KiB in float32. The GlobalAveragePool over the 1x1280x7x7 activation does not
appear: below one row it sums groups of channels over runs of positions.
Spills do not fit the slow pool
Section titled “Spills do not fit the slow pool”fits 256.00 KiB fast memory, 58 stages, 53 tiled; slow memory 5.17 MiB needed, 4.00 MiB givenTensors that cross a stage boundary are written to the slow pool and read back. A
second -m sizes that pool: -m 256K+8M gives 256 KiB of fast arena and 8 MiB of
slow. Without a second -m, slow memory reports what the plan needs and the compile
does not check it; the application has to provide that much.
The plan does not fit flash
Section titled “The plan does not fit flash”Error: no plan written: the plan is 13.31 MiB, the flash budget is 4.00 MiB-f is a check, not a constraint the compiler can solve. Weights dominate the plan,
so this is a quantization question rather than a scheduling one.
Levers that move the floor
Section titled “Levers that move the floor”Quantization moves the floor a lot; resolution moves it less than you might expect. Everything below is measured on the same model.
| Variant | Unscheduled | Floor |
|---|---|---|
| float32, 224x224 | 5.74 MiB | 37.50 KiB |
| float32, 160x160 | 2.93 MiB | 37.50 KiB |
| float32, 128x128 | 1.88 MiB | 37.50 KiB |
| int8, 224x224 | 1.44 MiB | 9.38 KiB |
| int8, 128x128 | 480.00 KiB | 9.38 KiB |
Each variant compiles at its floor and fails 1 KiB below it.
Quantize to int8. Every activation becomes a quarter of its size, so the floor falls by about that much, and the plan goes from 13.31 MiB to 3.39 MiB. This is the lever with the best ratio of effort to result, and it is usually the only one that does not change what the model computes. TiGrIS reads ONNX QDQ models directly.
Lower the input resolution. This shrinks every activation and the unscheduled
peak with it, but the floor is set by the smallest tile of the widest layers, and a
2D tile does not get smaller when the image does. For this model the floor stays at
37.50 KiB at 160x160 and 128x128, and at 9.38 KiB for int8. analyze and compile
take --input-shape input:1x3x160x160, so you can measure before deciding whether to
retrain or re-export.
Levers that do not move it
Section titled “Levers that do not move it”--xip does not change the plan’s size or the floor. It marks an
execute-in-place deployment; the runtime reads uncompressed weights from the
loaded plan buffer with or without the flag. The application must place that
buffer in memory-mapped flash to keep weights out of RAM.
-c lz4 rarely pays on a model of this shape. Compressed weights are
decompressed one stage at a time into a reservation carved out of the fast
arena, so the largest stage’s weight block has to fit there alongside the
activations. For MobileNetV2 that reservation is 4.89 MiB, far past any arena the
model is aimed at, and the compile fails:
Error: no plan written: decompressed weights need 4.89 MiB of fast memory, the budget is 256.00 KiBOn a model whose per-stage weights are small it compiles, but the saving is
thin. DS-CNN at -m 128K goes from 89.64 KiB to 88.44 KiB of plan, about one
percent, in exchange for 66.50 KiB of the fast memory. Reach for it when weights are
compressible and stages are small, not as a general fix.
What it looks like when it fits
Section titled “What it looks like when it fits”The int8 model at a 64 KiB budget:
tigris analyze mobilenetv2-int8.onnx -m 64Kwarning: input axis 0 (batch_size) is unset; using 1 (--input-shape overrides)mobilenetv2-int8.onnx int8, 67 operators input input 1x3x224x224 float32, stored as int8 at scale 0.0186584, zero point -14 output output 1x1000 float32fits 64.00 KiB fast memory, 58 stages, 53 tiled
memory unscheduled 1.44 MiB largest tensor 1.15 MiB 1x96x112x112 this plan 63.00 KiB 1.00 KiB headroom slow memory 1.29 MiB slow traffic 5.07 MiB per inference: 1.80 MiB written, 3.26 MiB read also fits at 32.00 KiB 64 stages, 62 tiled also fits at 16.00 KiB 64 stages, 63 tiled does not fit at 8.00 KiB
flash plan 3.39 MiB weights 3.38 MiB, overhead 10.25 KiBThe input and output rows are the plan’s contract. The model declares float32 at both ends. Its input is stored as int8 and converted at the boundary; its output is produced by a float32 section. An application hands over and reads back float32.
Read the trace before calling it done. tigris analyze mobilenetv2-int8.onnx -m 64K --trace runs the plan on the host runtime and reports, per stage, the bytes it reads
from the slow pool and writes back. All of that is traffic per inference, and on a part where the slow pool is external
PSRAM it is what sets the latency. A larger fast arena buys fewer stages and less of it.
When nothing works
Section titled “When nothing works”If the floor is still above the arena after quantizing and reducing resolution, the stages the report names have to change. A 2D tile’s floor follows the channel count of its layer, so fewer channels in the widest layers lower it; a row tile’s floor follows the width of one input row. That is a change to the model, so it belongs with training rather than with the compiler.
For the report’s fields, see analyze. For how the
compiler arrives at those numbers, see
Tiling.