
beefed.aiPractical guide to quantization, pruning, and TensorRT/Triton deployment to cut inference latency and cost on GPUs and edge devices.
Real production pain looks like: good numbers on a researcher’s workstation, but p95 latency spikes and cost balloons when the model lands in a multi-tenant cluster or on an edge device; post-deploy surprises (preproc CPU stalls, dynamic shapes, inappropriate batch sizing) break your SLO even before you start pruning weights. You need a repeatable baseline, an optimization plan that preserves your key slice metrics, and a deployment story that includes compiled engines and validated runtime configs.
Contents
Start by measuring the problem on the hardware and workload you actually care about. Capture:
trtexec for low-level engine benchmarking and perf_analyzer for server-level workloads when you plan to use Triton.
Define concrete success criteria before changing the model. Examples you can adopt immediately:
Make the baseline artifact reproducible: version the raw model, export a canonical ONNX or model file, capture the exact pre/post-processing code as preprocess.py/postprocess.py, and store a short perf script that reproduces the numbers (use the same client workload and flags). This “artifact + perf script” is the golden baseline you’ll compare optimizations against.
Quantization and pruning are powerful, but they behave differently and demand different validation.
Quantization (PTQ vs QAT)
FP16 first (FP16 almost always reduces memory and speeds up TensorCore-backed GPUs) and then try INT8 for extra gains. TensorRT supports FP16/INT8 and uses per-channel weight scales for conv/FC weights—this reduces per-layer quantization error for convolution layers.
fake-quantize operations so the model learns to be robust to quantization noise; PyTorch’s QAT flows have shown strong recovery relative to PTQ, especially on harder models. QAT is more engineering work but often required for sub-1% accuracy loss targets. Pruning (structured vs unstructured)
Pitfalls to watch for
A practical compile-tune pipeline (repeatable, automated) looks like:
torch.onnx.export() is the recommended path for PyTorch exports). Make the export deterministic: fixed opsets, explicit batch dims, and known input shapes where possible. onnx-simplifier or use Polygraphy to compare backends and isolate mismatches before compilation. Polygraphy can run onnxruntime vs TensorRT and highlight per-layer differences. # Python / TensorRT (conceptual)
profile = builder.create_optimization_profile()
profile.set_shape("input", (1,3,224,224), (8,3,224,224), (32,3,224,224))
config.add_optimization_profile(profile)
TensorRT chooses kernels per-profile; build engines for the shape ranges that reflect production traffic.
trtexec to benchmark and to serialize engines; use a timing cache to reduce rebuild time. trtexec doubles as a fast profiler and engine generator. Example trtexec usage to build FP16 or INT8 engines:
# FP16 engine
trtexec --onnx=model.onnx --saveEngine=model_fp16.plan --fp16 --workspace=4096
# INT8 engine (requires calibration cache or calibrator)
trtexec --onnx=model.onnx \
--minShapes=input:1x3x224x224 --optShapes=input:8x3x224x224 --maxShapes=input:32x3x224x224 \
--int8 --calib=/path/to/calib_cache \
--saveEngine=model_int8.plan --workspace=4096
TensorRT exposes timing caches and serialized engines; reusing them saves minutes of build time and avoids long, noisy autotuning steps during CI. ONNX Runtime’s TensorRT execution provider also highlights the benefit of caching (timing cache, engine cache) to reduce session startup time dramatically.
Calibration notes
Validation during compile
polygraphy run to compare the compiled engine against ONNX/float32 outputs on a handful of tricky inputs (corner cases, low-light images, occlusions). Run regression tests at p95 and mAP for the target slices. When you need production-grade serving across many models or versions, the Triton Inference Server is the pragmatic choice: it natively hosts TensorRT engines, ONNX models, TorchScript, TensorFlow graphs, and more from a model repository layout, and exposes an HTTP/gRPC API plus Prometheus metrics for autoscaling.
Practical deployment patterns
*.plan files in a Triton model repository with a config.pbtxt to control instance_group, max_batch_size, and dynamic_batching. Example minimal config.pbtxt:
name: "resnet50"
platform: "tensorrt_plan"
max_batch_size: 32
input [
{ name: "input_0" data_type: TYPE_FP32 dims: [3,224,224] }
]
output [
{ name: "output" data_type: TYPE_FP32 dims: }
]
instance_group [
{ count: 2 kind: KIND_GPU }
]
dynamic_batching {
preferred_batch_size: [4,8,16]
max_queue_delay_microseconds: 1000
}
perf_analyzer for load-testing server-level behavior (batching effects, concurrency trade-offs, and network overhead). perf_analyzer reproduces client-side behavior and reports p50/p90/p95/p99 and throughput under realistic loads. Autoscaling and metrics
/metrics Prometheus endpoint and drive HPA/KEDA with custom metrics such as in_flight_requests, avg_queue_delay, or gpu_utilization. Triton provides these metrics natively on the metrics endpoint. Autoscale on the metric that best predicts SLO breaches (often request queue length or p95 latency) rather than raw GPU utilization alone.
Packing and sharing GPUs
instance_group.count to trade latency for throughput. Prefer colocating models that share pre/post-processing CPU patterns to reduce host-side overhead. Test with perf_analyzer and watch server-side metrics (queue_time, compute_input, compute_infer, compute_output) to find hotspots.
Below is a compact, actionable checklist and a few snippets you can run now.
1) Baseline & gating
model.onnx, preprocess.py, postprocess.py, perf_script.sh.2) Quick wins (order matters)
trtexec --fp16. 3) Quantization protocol
calib_cache and version it. fake-quantize ops. Track validation metrics per epoch. 4) Pruning protocol
5) Compile & tune
torch.onnx.export() with dynamo=True or the recommended exporter) and run Polygraphy to check parity.
trtexec to iterate quickly. --useCudaGraph in trtexec/runtime if you have stable input shapes and need ultra-low latency.6) Serve & autoscale
config.pbtxt assigning proper instance_group and dynamic_batching. perf_analyzer and collect metrics from Triton /metrics. Create HPA/KEDA rule(s) on a chosen metric (queue size or p95 latency).
7) Validation & rollback
config.pbtxt in the model registry; tag with the exact TensorRT/Triton/container versions so the artifact is reproducible.Helpful formulas and snippets
cost_per_inference = (instance_hourly_cost / 3600) / throughput_per_sec
import numpy as np
lat_ms = np.array([...]) # list of per-request latencies in ms
p95 = np.percentile(lat_ms, 95)
Quick pointers for edge deployment
Important: Always tie an optimization to a measurable, versioned artifact (model.plan / calib_cache / config.pbtxt) and an automated perf test. That combination is what makes model optimization safe and repeatable.
Measure, validate, and write down the trade-off you are willing to accept between accuracy and latency. Apply the smallest change that meets the SLO (FP16 → INT8 → structured sparsity → QAT) and keep the full experimental record in version control so you can reproduce the wins on new hardware generations.
Sources:
NVIDIA TensorRT Developer Guide - Core TensorRT concepts: precision modes (FP32/FP16/INT8), optimization profiles, trtexec usage and performance benchmarking; guidance on engine building and runtime tuning.
Performing Inference In INT8 Precision (TensorRT docs) - Details on INT8 calibration, calibrator APIs, calibration cache portability, and practical notes (recommended calibration sample sizes).
Triton Model Repository (NVIDIA Triton docs) - Model repository layout, config.pbtxt fields, platform-specific model files, and version policies.
Triton Performance Analyzer (perf_analyzer) guide - How to benchmark Triton-served models, options for realistic input data, and comparing batching/concurrency trade-offs.
Quantization-Aware Training for Large Language Models (PyTorch blog) - Practical QAT workflows, reasons to prefer QAT over PTQ in some cases, and PyTorch QAT tooling notes.
ONNX Runtime — TensorRT Execution Provider - Details on using TensorRT as an ONNX Runtime EP, engine/timing caches, and the speedups from caches.
Accelerating Inference with Sparsity Using the NVIDIA Ampere Architecture and NVIDIA TensorRT - Explanation of 2:4 structured sparsity, sparse Tensor Cores and practical sparse retraining workflow and speedups.
Learning both Weights and Connections for Efficient Neural Network (Han et al., 2015) - Foundational pruning methodology and empirical results showing large parameter reductions with retraining.
Polygraphy documentation (NVIDIA) - Tooling to compare backends, sanitize ONNX, and debug TensorRT/ONNX numeric mismatches.
Exporting a PyTorch model to ONNX (PyTorch docs) - Recommended ONNX export practices and the torch.onnx.export() API for stable ONNX artifacts.
Triton Metrics (Prometheus) — Triton docs - Available Triton Prometheus metrics, endpoint details, and configuration options.
Exploiting Ampere Structured Sparsity with cuSPARSELt (NVIDIA blog) - cuSPARSELt library overview for sparse GEMM and integration points for sparse acceleration on Ampere GPUs.