Size and latency of six configurations, one of them a fasterai Quantizer call, on a CPU and on a GPU, plus two Quantizer calls the library refuses on a convolutional model
Overview
This notebook times five configurations on a CPU model and five on a GPU model, and reports their size and latency. One of them comes from fasterai’s Quantizer (x86/static); the others are the untouched baseline, .half(), torchao’s quantize_ called directly, and torch.compile on the GPU FP16 model. Two further Quantizer configurations appear as refusals, called three times (twice on the CPU model, once on ResNet-50): torchao’s int8_weight_only and int8_dynamic recipes rewrite nn.Linear layers only, so on a convolutional model they would quantize the classifier head alone, which the library refuses. Quantizer backends and methods not run here (pt2e, qnnpack, fbgemm, onednn, method='dynamic', method='qat') are out of scope. The training table below is the baseline’s validation accuracy on the 1478 validation images, single run, no seed fixed; no quantized model’s accuracy is measured on this page.
CPU section: ResNet-18 fine-tuned for 3 epochs on Oxford Pets at 64x64, timed on one training batch (dls.one_batch(), at ImageDataLoaders.from_name_func’s default bs=64; the page does not print the batch shape).
GPU section: torchvision ResNet-50 with random weights (weights=None) on a random batch of 32 images at 224x224, on the GPU that the page names below.
Protocol for both sections: each configuration is timed in one block of 50 iterations after 10 warm-up iterations (3 for the torch.compile row), wall-clock time.time(), with torch.cuda.synchronize() around the GPU blocks; the blocks run once each, in the order shown, baseline first. No dispersion, no interleaved comparison and no output-parity check against the FP32 model is reported. Sizes are the state_dict files on disk, in MB of 10^6 bytes; the compiled model’s size is not measured.
import platform, torchaodef cpu_name():"The CPU model line Linux reports, or `platform.processor()` where there is none"try:for line inopen('/proc/cpuinfo'):if line.startswith('model name'): return line.split(':', 1)[1].strip()exceptOSError: passreturn platform.processor()print(f"CPU: {cpu_name()} | torch threads: {torch.get_num_threads()} | "f"torch {torch.__version__} | torchao {torchao.__version__}")print(f"validation set: {len(dls.valid_ds)} images")
.half() on the whole model. The file halves. The latency is 644 ms against 26.74 ms for FP32, so on this CPU this row is a size result, not a speed one. The GPU section times the same .half() model at 4.24 ms against 7.67 ms for its FP32 baseline, so the 644 ms is a property of this CPU path, not of half precision; the page does not measure why that path is slow.
Weights and activations in INT8, with a calibration pass over dls.valid. At 16.78 ms against the section-1 FP32 baseline’s 26.74 ms, on the same box and the same batch, it is the CPU row with the lowest latency in this run, at a quarter of the size. With one timing block per configuration and no dispersion reported, the page cannot say how much of that difference is run-to-run variation.
Only the weights are INT8; activations stay FP32; no calibration. torchao’s int8_weight_only recipe rewrites nn.Linear layers, not convolutions (see the Quantizer reference): on this ResNet it would quantize the classifier head and leave every convolution in floating point, so Quantizer refuses the model rather than handing back a float model carrying a quantized model’s provenance. torchao rewrote none of the 20 convolutions, and Quantizer inspects the model it got back rather than trusting the recipe.
refused: backend='torchao' int8_weight_only left 20 convolution(s) in floating point (0.0, 0.4.0.conv1, 0.4.0.conv2, 0.4.1.conv1, 0.4.1.conv2, 0.5.0.conv1, ...): for convolutions use backend='pt2e' or backend='x86'.
5. W8A8 dynamic (torchao)
INT8 weights, activations quantized on the fly at run time; no calibration. int8_dynamic rewrites nn.Linear layers too, so the same post-condition refuses the same model, and the message counts the convolutions the recipe would have left in floating point.
refused: backend='torchao' int8_dynamic left 20 convolution(s) in floating point (0.0, 0.4.0.conv1, 0.4.0.conv2, 0.4.1.conv1, 0.4.1.conv2, 0.5.0.conv1, ...): for convolutions use backend='pt2e' or backend='x86'.
6. W4A32 (INT4 weight-only)
Weights in 4 bits through torchao’s IntxWeightOnlyConfig, applied to Conv2d and Linear layers. The default intx_packing_format is UNPACKED_TO_INT8, so each 4-bit value occupies one byte on disk: the file is about four times smaller here, not eight. On this CPU it came out at 26.67 ms against the FP32 baseline’s 26.74 ms, a difference one timing block cannot resolve.
W4A32 (INT4 weight-only) size= 12.0 MB latency= 26.67 ms
7. W4A16 (INT4 weights, FP16 activations)
The INT4 model above with activations in half precision. Nearly the same size as W4A32, 11.9 MB against 12.0 MB; on CPU its latency is in the same range as the half-precision row of section 2, 657 ms against 644 ms, and on the GPU the same configuration runs at 5.09 ms against 7.67 ms.
W4A16 (INT4 + half) size= 11.9 MB latency=657.45 ms
GPU benchmarks
The same methods on ResNet-50 with random weights (weights=None) and a random batch of 32 images at 224x224, plus torch.compile on the FP16 model. The torchao weight-only call is here too, and Quantizer refuses ResNet-50 for the reason section 4 gives. Latency only: with random weights there is no accuracy to compare and no parity check against a trained reference. The compiled model’s size is not measured, and its row says so; the compiled row’s speedup is against the FP32 eager baseline and mixes two changes; the FP16 row isolates the precision one.
The figures are the ones printed above, rounded to the precision shown; this table restates them.
Configuration
CPU: ResNet-18, one training batch at 64x64
GPU: ResNet-50, batch of 32 at 224x224
FP32
46.9 MB, 26.74 ms
102.5 MB, 7.67 ms
FP16 (.half())
23.5 MB, 644 ms
51.3 MB, 4.24 ms
W8A8 static (x86)
11.9 MB, 16.78 ms
not run
W4A32 (INT4 weight-only)
12.0 MB, 26.67 ms
26.4 MB, 8.40 ms
W4A16 (INT4 + half)
11.9 MB, 657 ms
26.0 MB, 5.09 ms
FP16 + torch.compile
not run
size not measured, 2.22 ms
Measured on: CPU section, the Intel(R) Core(TM) i9-14900KS printed above, with 24 torch threads, torch 2.9.1+cu128 and torchao 0.16.0; ResNet-18 fine-tuned 3 epochs on Oxford Pets, one training batch at 64x64 (default bs=64, shape not printed). GPU section, NVIDIA GeForce RTX 5090; ResNet-50 with random weights, random 32x3x224x224 input. Each configuration: one block of 50 iterations after 10 warm-ups (3 for the compiled row), wall-clock time, run once, no dispersion, no interleaving, no parity check. Nothing here was measured on other hardware, batch sizes or models.