CUDA Quantization Implementation Guide
December 25, 2025 13 min By RightNow AI Team
Introduction
Quantization reduces model size and increases inference speed by using lower-precision arithmetic. INT8 provides 4x memory reduction with ~1% accuracy loss. INT4 enables 8x reduction for LLMs. Efficient CUDA implementation requires understanding packed data formats and fused dequantization.
This guide covers quantization schemes, packed kernels, and performance optimization.
Common Performance Issues
- Quantization overhead negates speedup for small models
- Accuracy degradation from poor calibration
- Unpacked INT8 wastes memory bandwidth
- Missing fusion with dequantization
Optimization Techniques
1. Packed INT8/INT4
Pack multiple values per register for SIMD.
2. Fused Dequantization
Dequantize in GEMM epilogue, not separate kernel.
3. Per-channel Scaling
Different scales per output channel for accuracy.
Implementation Comparison
Before (Naive Implementation)
Separate dequantization doubles memory traffic.
// Anti-pattern: separate dequantize kernel
__global__ void dequantize(int8_t* input, float* output, float scale, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
output[i] = input[i] * scale;
}
}
void quantized_inference() {
// Dequantize weights
dequantize<<<grid, block>>>(q_weights, f_weights, scale, n);
// Regular GEMM
cublasSgemm(..., f_weights, ...);
}
After (Optimized Implementation)
Fused quantized GEMM eliminates dequantization overhead.
// INT8 GEMM with fused dequantization
// Using INT8 Tensor Cores (Turing+)
#include <cublas_v2.h>
#include <cublasLt.h>
void int8_gemm_fused(cublasLtHandle_t handle,
int8_t* A, int8_t* B, int8_t* C,
float* scale_A, float* scale_B, float* scale_C,
int M, int N, int K) {
cublasLtMatmulDesc_t matmulDesc;
cublasLtMatmulDescCreate(&matmulDesc, CUBLAS_COMPUTE_32I, CUDA_R_32I);
// Set scaling factors (fused in epilogue)
cublasLtMatmulDescSetAttribute(matmulDesc,
CUBLASLT_MATMUL_DESC_A_SCALE_POINTER, &scale_A, sizeof(scale_A));
cublasLtMatmulDescSetAttribute(matmulDesc,
CUBLASLT_MATMUL_DESC_B_SCALE_POINTER, &scale_B, sizeof(scale_B));
cublasLtMatmulDescSetAttribute(matmulDesc,
CUBLASLT_MATMUL_DESC_D_SCALE_POINTER, &scale_C, sizeof(scale_C));
// Execute with fused scaling
cublasLtMatmul(handle, matmulDesc, ...);
}
// INT4 packed format (2 values per byte)
__device__ int8_t unpack_int4(uint8_t packed, int idx) {
if (idx == 0) return (packed & 0x0F) - 8; // Lower 4 bits
else return (packed >> 4) - 8; // Upper 4 bits
}
// INT4 GEMM kernel
__global__ void gemm_int4(uint8_t* A_packed, uint8_t* B_packed,
float* C, float* scale, int M, int N, int K) {
// Each thread unpacks and processes multiple INT4 values
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < M && col < N) {
int32_t sum = 0;
for (int k = 0; k < K / 2; k++) {
uint8_t a_packed = A_packed[row * (K/2) + k];
uint8_t b_packed = B_packed[k * (N) + col];
int8_t a0 = unpack_int4(a_packed, 0);
int8_t a1 = unpack_int4(a_packed, 1);
int8_t b0 = unpack_int4(b_packed, 0);
int8_t b1 = unpack_int4(b_packed, 1);
sum += a0 * b0 + a1 * b1;
}
C[row * N + col] = sum * scale[col];
}
}
Performance Results
| Metric | Naive | Optimized | Improvement |
|---|---|---|---|
| INT8 vs FP16 | 1x | 2x | Tensor Core INT8 |
| INT4 vs FP16 | 1x | 3-4x | Memory bandwidth |
Frequently Asked Questions
How much accuracy loss from INT8?
Typically <1% for vision models, 1-2% for language models with proper calibration. Per-channel quantization preserves more accuracy than per-tensor.