在大语言模型(LLM)的推理过程中,INT8和FP16是两种最常用的低精度数据类型。它们都能带来显著的速度提升,但背后的原理、加速效果和适用场景各不相同。本文从硬件原理、速度对比、精度影响、实践选择四个维度,系统讲解 INT8 与 FP16 的差异。
一、为什么低精度能加速推理?
1. 核心原理
深度学习的推理过程,本质是大量的矩阵乘法和加法。数据的位宽越小:
内存占用越少→ 相同显存能装更多数据
带宽压力越小→ 数据搬运更快
计算单元吞吐越高→ 相同时间内算更多
2. 三种精度的对比
| 类型 | 位宽 | 数值范围 | 内存占用 | 相对速度 |
|---|---|---|---|---|
| FP32 | 32 位 | ±3.4×10³⁸ | 基准 | 1× |
| FP16 | 16 位 | ±65504 | 1/2 | 2~4× |
| INT8 | 8 位 | -128~127 | 1/4 | 4~8× |
结论:位宽减半,内存和带宽减半,速度提升显著。
二、FP16 的加速原理
1. 什么是 FP16?
FP16(半精度浮点)用 16 位表示一个浮点数:
1 位符号
5 位指数
10 位尾数
2. 为什么 FP16 快?
| 维度 | FP32 | FP16 |
|---|---|---|
| 内存占用 | 4 字节 | 2 字节 |
| 显存带宽 | 基准 | 减半 |
| Tensor Core | 支持 | 原生支持 |
| 计算吞吐 | 基准 | 2~8× |
关键:现代 GPU(如 NVIDIA V100、A100、H100)都有专门的 Tensor Core,为 FP16 做了硬件级优化。用 FP16 能直接吃到 Tensor Core 的加速红利。
3. FP16 的精度问题
数值范围小(最大 65504)
容易出现上溢/下溢
需要损失缩放(Loss Scaling)技巧
三、INT8 的加速原理
1. 什么是 INT8?
INT8(8 位整数)用 8 位表示一个整数:
范围:-128 ~ 127
需要量化(Quantization):把 FP32 的浮点数映射到 INT8
2. 量化的两种方式
| 方式 | 说明 |
|---|---|
| 对称量化 | INT8 = round(FP32 / scale) |
| 非对称量化 | INT8 = round(FP32 / scale) + zero_point |
核心:用缩放因子 scale和零点 zero_point,把浮点数压缩成整数。
3. 为什么 INT8 更快?
| 维度 | FP16 | INT8 |
|---|---|---|
| 内存占用 | 2 字节 | 1 字节 |
| 显存带宽 | 基准 | 再减半 |
| 计算吞吐 | 基准 | 2× |
| 整数运算 | — | 硬件原生 |
关键:
内存再减半→ 显存能装更大的模型
整数运算更快→ GPU 的整数单元吞吐高
Tensor Core 支持 INT8→ A100/H100 上有专门的 INT8 加速
四、INT8 vs FP16:速度对比
1. 理论对比
| 维度 | FP16 | INT8 |
|---|---|---|
| 内存占用 | 2 字节 | 1 字节 |
| 相对 FP32 速度 | 2~4× | 4~8× |
| 相对 FP16 速度 | 1× | 1.5~2× |
| 精度损失 | 小 | 中 |
| 硬件支持 | 广泛 | 较新 GPU |
2. 实际测试数据
在 NVIDIA A100 上(以 BERT 推理为例):
| 精度 | 延迟 | 吞吐 |
|---|---|---|
| FP32 | 100% | 1× |
| FP16 | 40% | 2.5× |
| INT8 | 25% | 4× |
在 NVIDIA H100 上:
| 精度 | 相对速度 |
|---|---|
| FP32 | 1× |
| FP16 | 3× |
| INT8 | 6× |
结论:
INT8 比 FP16 快约 1.5~2 倍,但精度损失更大。
五、精度对比
1. FP16 的精度
相对 FP32 精度损失:< 1%
适用场景:几乎所有任务
是否需要微调:不需要
2. INT8 的精度
相对 FP32 精度损失:1~3%
适用场景:对精度要求不极端的任务
是否需要微调:推荐做量化感知训练(QAT)
3. 精度损失对比
| 精度 | 相对 FP32 损失 | 适用 |
|---|---|---|
| FP16 | < 1% | 通用 |
| INT8 | 1~3% | 推理优先 |
| INT4 | 5~10% | 极致压缩 |
六、硬件支持对比
| 硬件 | FP16 | INT8 |
|---|---|---|
| NVIDIA V100 | ✅ | ⚠️ 有限 |
| NVIDIA T4 | ✅ | ✅ |
| NVIDIA A100 | ✅ | ✅ |
| NVIDIA H100 | ✅ | ✅ |
| 消费级 GPU(RTX) | ✅ | ⚠️ 部分 |
| CPU(Intel) | ⚠️ | ✅ |
结论:
FP16:几乎所有现代 GPU 都支持
INT8:新 GPU 支持好,老 GPU 可能不支持
七、实践选择建议
1. 什么时候用 FP16?
| 场景 | 推荐 |
|---|---|
| 通用推理 | ✅ FP16 |
| 精度要求高 | ✅ FP16 |
| 老 GPU | ✅ FP16 |
| 训练 + 推理 | ✅ FP16 |
| 不想调参 | ✅ FP16 |
优点:简单、通用、精度损失小。
2. 什么时候用 INT8?
| 场景 | 推荐 |
|---|---|
| 推理优先 | ✅ INT8 |
| 显存吃紧 | ✅ INT8 |
| 新 GPU(A100/H100) | ✅ INT8 |
| 允许 1~3% 精度损失 | ✅ INT8 |
| 批量推理 | ✅ INT8 |
优点:最快、最省显存。
3. 推荐组合
训练:FP32 或 FP16(混合精度) 推理:FP16(通用)或 INT8(极致性能)
八、代码示例
1. PyTorch 用 FP16
import torch model = MyModel().cuda().half() # 转 FP16 with torch.no_grad(): output = model(input.half())2. PyTorch 用 INT8(动态量化)
import torch # 动态量化 quantized_model = torch.quantization.quantize_dynamic( model, {torch.nn.Linear}, dtype=torch.qint8 ) # 推理 output = quantized_model(input)3. 用 ONNX Runtime 加速
import onnxruntime as ort # FP16 sess = ort.InferenceSession('model_fp16.onnx') # INT8 sess = ort.InferenceSession('model_int8.onnx')九、总结对比表
| 维度 | FP16 | INT8 |
|---|---|---|
| 位宽 | 16 位 | 8 位 |
| 内存占用 | 2 字节 | 1 字节 |
| 相对 FP32 速度 | 2~4× | 4~8× |
| 相对 FP16 速度 | 1× | 1.5~2× |
| 精度损失 | < 1% | 1~3% |
| 硬件支持 | 广泛 | 新 GPU |
| 是否需要微调 | 不需要 | 推荐 QAT |
| 适用场景 | 通用 | 推理优先 |
十、一句话总结
INT8 比 FP16 更快,但精度损失更大。
| 选择 | 场景 |
|---|---|
| FP16 | 通用、精度优先、老 GPU |
| INT8 | 推理优先、新 GPU、显存吃紧 |
核心:
「FP16 是通用加速,INT8 是极致加速——FP16 损失 < 1%,INT8 损失 1~3%,但 INT8 比 FP16 再快 1.5~2 倍。」
记忆:
「FP16 稳,INT8 快;精度换速度,按需选。」
一句话:
「如果精度允许,INT8 是推理的最优解;如果不想折腾,FP16 是通用选择。」