4. 硬件架构与INT8计算单元:CPU INT8指令集(AVX-512 VNNI)、GPU Tensor Core INT8、NPU/TPU专用加速器
各位同学,咱们今天聊点硬核的。INT8计算,说白了就是让硬件用更少的力气干更多的活。但不同硬件干活的姿势,那可差太多了。
我这些年调优下来,最大的感触就是:不了解硬件,就别谈优化。你写的代码再漂亮,硬件不买账,一切都是白搭。今天咱们就把CPU、GPU、NPU这三家怎么玩INT8,掰开揉碎了讲清楚。
核心观点:INT8加速的本质,是用更低的精度换取更高的吞吐。但不同硬件架构的“交易方式”完全不同——CPU靠指令集,GPU靠专用核心,NPU/TPU靠全定制设计。
4.1 CPU INT8指令集:AVX-512 VNNI
先说说CPU。很多人觉得CPU做INT8推理太慢,不如GPU。嗯,这话对了一半。如果你还在用老旧的SSE指令集,那确实慢。但如果你有支持AVX-512 VNNI的处理器,情况就完全不同了。
AVX-512 VNNI是Intel在Cascade Lake时代引入的。它最牛的地方在于——一条指令就能完成8个INT8乘加操作。你想想看,以前你得拆成好几条指令,现在一条搞定,吞吐直接翻倍。
我的经验:我在部署一个BERT模型时,从AVX2切换到AVX-512 VNNI,推理延迟直接降了40%。但要注意,AVX-512频率会降,你得在吞吐和单核性能之间找平衡。
来看个实际例子。假设我们要做两个INT8向量的点积:
// 传统方式(AVX2)
__m256i a = _mm256_loadu_si256((__m256i*)ptr_a);
__m256i b = _mm256_loadu_si256((__m256i*)ptr_b);
__m256i prod = _mm256_maddubs_epi16(a, b); // 先乘再加,但只到16位
// 还得额外累加...
// AVX-512 VNNI方式
__m512i a = _mm512_loadu_si512((__m512i*)ptr_a);
__m512i b = _mm512_loadu_si512((__m512i*)ptr_b);
__m512i sum = _mm512_dpbusd_epi32(zero, a, b); // 一条指令搞定!
看到了吗?VNNI把乘加和累加合并了。这不仅仅是代码变短,关键是减少了指令发射压力和寄存器压力。
注意:AVX-512 VNNI需要硬件支持。不是所有标着“支持AVX-512”的CPU都支持VNNI。我吃过这个亏——代码写好了,跑在至强金牌上没问题,换到至强银牌上直接非法指令异常。
另外,CPU上做INT8还有个好处:内存带宽利用率高。因为INT8数据量是FP32的1/4,同样的内存带宽能喂给计算单元更多数据。这对带宽敏感型模型(比如推荐系统)特别友好。
4.2 GPU Tensor Core INT8
接下来聊聊GPU。NVIDIA从Volta架构开始引入Tensor Core,专门做混合精度计算。到了Turing,Tensor Core开始原生支持INT8。
Tensor Core的设计思路和CPU完全不同。CPU是通用计算单元,靠指令集扩展来加速。而Tensor Core是专用硬件,它只做一件事——矩阵乘加。
一个Tensor Core每个时钟周期能做一个4×4矩阵乘加。在A100上,每个SM有4个Tensor Core,一共108个SM。你算算,这吞吐量有多恐怖?
| GPU架构 | Tensor Core版本 | INT8 TOPS | 关键特性 |
|---|---|---|---|
| Turing (T4) | 第2代 | 65 | 首次支持INT8 |
| Ampere (A100) | 第3代 | 624 | 支持稀疏化,性能翻倍 |
| Hopper (H100) | 第4代 | 1979 | Transformer引擎,FP8/INT8混合 |
用Tensor Core做INT8推理,我建议你直接用cuBLAS或cuDNN。别自己手写CUDA——除非你真的很闲。这些库已经针对Tensor Core做了深度优化,包括数据布局、warp调度、共享内存管理等等。
避坑指南:我曾经手写过一个INT8矩阵乘,自以为优化得很好了。结果一测,只有cuBLAS的60%性能。后来发现是数据对齐问题——Tensor Core要求数据按特定格式排列,我忽略了。
GPU上做INT8还有个关键点:量化校准。你喂给Tensor Core的数据必须是真正的INT8,而不是FP32转过去的。这涉及到calibration数据集的选择、量化参数的计算。我一般用1000张左右的图片做校准,效果比较稳定。
4.3 NPU/TPU专用加速器
最后说说专用加速器。NPU和TPU是真正的“专才”——从设计之初就是为了神经网络推理而生。
Google的TPU v1,2015年就出来了。它有一个巨大的脉动阵列(Systolic Array),128×128的MAC单元。每个周期能做16384个乘加操作。这规模,CPU和GPU看了都得喊大哥。
NPU这边,各家方案五花八门。华为的昇腾、寒武纪的思元、地平线的征程……但核心思路都一样:把计算和数据移动做到极致。
专用加速器有几个共同特点:
- 大尺寸脉动阵列:矩阵乘是核心操作,用脉动阵列做最合适
- 片上存储层次:多级SRAM,减少DRAM访问
- 数据流优化:权重固定、输入流式、输出累加
- 专用指令集:一条指令完成整个layer的计算
我调过一款NPU,它的INT8算力标称是32TOPS。但实际跑模型时,只能跑到18TOPS。为什么?因为数据搬运成了瓶颈。模型太大,权重放不进片上SRAM,频繁从DDR搬数据,计算单元一直在空等。
我的建议:用NPU/TPU时,一定要关注“计算效率”而不是“峰值算力”。峰值算力是理论值,实际能跑多少,取决于你的模型结构和数据流设计。
TPU还有一个杀手锏——Bfloat16。虽然它不是INT8,但它的动态范围和INT8差不多,而且不需要量化。TPU v2开始支持BF16,很多模型直接跑BF16,精度和FP32几乎一样,速度却快得多。
4.4 三种架构的对比与选择
说了这么多,咱们来总结一下。三种架构各有千秋,选哪个取决于你的场景:
| 维度 | CPU (AVX-512 VNNI) | GPU (Tensor Core) | NPU/TPU |
|---|---|---|---|
| 灵活性 | 最高 | 中等 | 最低 |
| INT8吞吐 | 中等 | 高 | 最高 |
| 延迟 | 低 | 中等 | 低 |
| 功耗 | 高 | 中等 | 低 |
| 开发难度 | 中等 | 低(用库) | 高 |
| 适用场景 | 小模型、低延迟 | 大模型、高吞吐 | 特定模型、量产 |
我个人习惯是:原型阶段用GPU,部署阶段看场景。如果模型小、延迟要求高,用CPU;如果模型大、吞吐要求高,用GPU;如果量很大、功耗敏感,考虑NPU。
下面这张图展示了三种架构在INT8计算时的核心差异:
从这张图能看出来,CPU是“小而精”,每个核心都能独立工作;GPU是“多而专”,大量SM配合Tensor Core;NPU/TPU则是“大而全”,整个芯片为一个目标服务。
好了,这一章的内容就到这里。记住一句话:没有最好的硬件,只有最合适的硬件。下一章咱们聊聊量化方法,看看怎么把FP32模型变成INT8,同时保证精度不掉。