The 8 GB Lab Book · Chapter 12
FP16 baseline, INT8 on trial
- Board
- Jetson Orin Nano Super
- Mode
- 15W
- Setup
- TensorRT 10.3
- Summary
- INT8 ran 1.38x faster than FP16 on resnet18 and agreed with FP32 on 82.4% of images.
What I expected
The poster gives 2x: INT8An 8-bit integer number format; it shrinks weights and activations relative to floating point but needs calibration or scaling to keep accuracy. tensor cores run at twice the FP1616-bit floating point (half precision): half the bytes of FP32, and tensor cores run it at much higher throughput than FP32. rate. INT8 should halve the FP16 time. For accuracy, I expected only a rounding effect, with 1 or 2% of answers lost.
I also expected that the FP32Standard 32-bit floating point (single precision), the usual training format and the accuracy reference for lower-precision runs. TensorRT engineThe compiled, GPU-specific plan TensorRT produces from a network; it has to be rebuilt for a different GPU or TensorRT version. would run on the CUDA cores at 1.25 TFLOP/s. That gives approximately 2.9 ms for each image at 224x224. I wrote that value down as a floor.
The theory
Three floors, then the bytes, then what a cosine is in degrees
FLOORS AT 224x224 (arithmetic)
3.6 GFLOP per image (1.81 GMAC, published) CUDA cores fp32: 3.6 G / 1.25 T = 2.9 ms tensor fp16: 3.6 G / 10 T (est.) = 0.36 ms tensor int8: 3.6 G / 20 T (est.) = 0.18 ms = board: 2.67 / 1.34 / 0.97 ms
Bytes per image, batch 1 (arithmetic)
weights 11.7 M x 4 / 2 / 1 B = 46.8 / 23.4 / 11.7 MB engine files 47.1 / 23.7 / 12.3 MB: the file is the weights weight floor at 68 GB/s: 0.69 / 0.34 / 0.17 ms activations not counted: est. 1 to 2x the weights = byte floors sit on the compute floors (0.36 / 0.18 est.): neither is shown to bind
If the advantage of INT8 is bytes, not TOPSTera-operations per second: a peak throughput figure, usually quoted for low-precision math under ideal conditions, that real models rarely approach., the limit is 2x from half the weights and half the activations. But the byte floors (0.34 and 0.17 ms) are at the level of the compute floors (0.36 and 0.18 ms, est.). The board runs about 4x to 6x above both, so this run does not show which floor binds. 1.38x means that something did not halve. Launches are the same in each engine, 28 x 9 us = 0.25 ms (Chapter 11), and some layers may stay in FP16 (estimate). Without the launches, 1.38x becomes (1.34 - 0.25) / (0.97 - 0.25) = 1.5x.
Why cosine 0.9884 and 82.4% agreement are the same fact
Cos = 0.9884 is an angle of 8.7 degrees between the INT8 and FP32 logit vectors (arithmetic). The 1.0000 of FP16 is less than half a degree. A tilt of 8.7 degrees flips the top-1 when the top two logits are nearer than the distance that the tilt moves them. On out-of-domain images (CIFAR upscaled to 224), the top two are frequently near, so 17.6% flip. The same tilt on confident in-domain frames would flip fewer.
Agreement is a property of the tilt and the data. Cosine is a property of the tilt alone. For this reason, 0.9884 looked like a pass, but it was not a pass.
The trial (measured, 300 runs, 500 eval images, 15W)
- INT8 over FP16
- 1.38x
- FP16 agreement
- 99.6%
- INT8 agreement
- 82.4%
- INT8 cosine
- 0.9884
The setup
python3 tools/day12_int8.py
cat ~/day12/results.json | head -40"trt_version": "10.3.0" "notes": "eval images are CIFAR10 upscaled to 224x224, not real ImageNet or campus frames -- ... "onnx_ok": true
- Script: tools/day12_int8.py
- Wrote: day12/results.json
- Engines: resnet18_fp32, _fp16, _int8 .engine, plus calib.cache
- The script exports resnet18 (ImageNet weights) to static ONNX, builds three engines with the TensorRT Python API, calibrates INT8 on 256 CIFAR10 images at 224, and scores agreement with FP32 on 500 more.
# from day12/results.json (this run)
"trt_version": "10.3.0"
# board state captured 2026-09-29 (days/environment/board-2026-09-29.txt), not the state of this day's run
L4T R36.4.2, kernel 5.15.148-tegra
NV Power Mode: 15W
CUDA 12.6 (V12.6.68), TensorRT 10.3.0.30, cuDNN 9.3.0.75
The measurement
| What | Expected | Measured | Note |
|---|---|---|---|
| FP32: median / p99 ms | - | 2.67 / 2.73 | engine 47.1 MB, build 24 s |
| FP16: median / p99 ms | - | 1.34 / 1.39 | engine 23.7 MB, build 35 s |
| INT8: median / p99 ms | - | 0.97 / 1.04 | engine 12.3 MB, build 77 s |
| FP16 top-1 agreement / cosine vs FP32 | - | 99.6 % / 1.0000 | 500 CIFAR test images upscaled |
| INT8 top-1 agreement / cosine vs FP32 | - | 82.4 % / 0.9884 | 500 CIFAR test images upscaled |
| INT8 speedup over FP16 | 2x poster | 1.38x | resnet18 |

Three engines on one ruler, and what each one costs in answers
- 256 calibrationimages (CIFAR)
- Entropy scalesper tensor
- INT8 engine12.3 MB
- 500 eval images:82.4% agree
| what | my prior | the board |
|---|---|---|
| FP32 median / p99 | 2.9 ms floor | 2.67 / 2.73 ms, 47.1 MB, 24 s |
| FP16 median / p99 | - | 1.34 / 1.39 ms, 23.7 MB, 35 s |
| INT8 median / p99 | - | 0.97 / 1.04 ms, 12.3 MB, 77 s |
| FP16 agreement / cosine vs FP32 | rounding | 99.6% / 1.0000 |
| INT8 agreement / cosine vs FP32 | 1 to 2% lost | 82.4% / 0.9884 |
| INT8 speedup over FP16 | 2x poster | 1.38x |
Where it broke
FP32 was below its CUDA-core floor, with 2.67 ms against 2.9. At the 612 MHz of Chapter 4, that cannot occur on the CUDA cores. The FP32 build passed no precision flag, and TensorRTNVIDIA's inference compiler and runtime, which turns a trained network into an execution plan tuned for one specific GPU. turns TF32TensorFloat-32: an Ampere tensor-core mode that multiplies FP32 inputs with a shortened mantissa for speed and accumulates the result in FP32. on by default for FP32 networks (spec). This puts the convolutions on the tensor cores with a 10-bit mantissa. My 'FP32 baseline' is a TF32 baseline.
Also, INT8 lost 17.6% of the answers, not 2%. The calibrator saw 256 CIFAR images. 256 images from the incorrect domain give 8.7 degrees of tilt on the logits.
I used a poster number as a prior for a batch-1 network. Its byte floor and its compute floor are at the same level. Bytes alone limit the gain to 2x, and launches used a third of that. A TOPS number needs a batch size with it.
I called the FP32 engine a baseline, but I did not compare it with its own floor. It was faster than the floor. It is not the thing that I called it. Label the arithmetic that ran, not the flag that I passed.
0.9884 looked like a pass. The 8.7 degrees and the 17.6% flips are two different measures. Cosine gives the angle between the logit vectors. Agreement counts the answers that changed. Only agreement showed the loss.
My notes from Day 12
Written by hand on 2026-09-13, before this chapter was typed. These are my original notes from that day. Where the chapter corrects them, the chapter is current.


Summary
- The calibrator saw 256 CIFAR images. INT8 then agreed with FP32 on 82.4% of 500 images.
- Cosine 0.9884 looked like a pass, but 17.6% of the top-1 answers flipped. When there are no labels, I report agreement with FP32 first and cosine second.
- The FP32 engine ran in 2.67 ms, below its 2.9 ms CUDA-core floor. TensorRT kept TF32 on by default, so this baseline is TF32.
Sources
These sources are not mine. Measured numbers came off my own board; specs, estimates and paper figures are labeled as such.
- TensorRT quantization / calibrator docs
- TensorRT Model Optimizer notes
- NVIDIA Developer Forum thread on INT8 regression for ViT-S+DPT on Orin Nano
- Ampere tensor-core precision list