3. CUDA核心与Tensor Core:计算单元的家族图谱
说到GPU里的计算单元,很多人第一反应就是「CUDA核心」。嗯,这个说法没错,但不够准确。我刚开始接触CUDA时也以为GPU里全是清一色的计算单元,后来在调优一个深度学习模型时才发现——事情没那么简单。
GPU的计算单元,其实是一个「家族图谱」。里面有负责通用计算的CUDA核心,有专门干矩阵乘法的Tensor Core,还有处理特殊函数的SFU等等。今天我就带你捋一捋这个家族里的各个成员。
3.1 CUDA核心:最通用的打工人
CUDA核心,说白了就是GPU里最基础的算术逻辑单元。每个CUDA核心都能做整数运算和浮点运算。你写的任何CUDA代码,最终都会落到这些核心上执行。
但这里有个坑——NVIDIA官方文档里其实没有「CUDA核心」这个术语。它只是我们工程师约定俗成的叫法。真正的硬件术语叫「CUDA Core」或「Shader Core」。
关键数据:
- 每个CUDA核心包含:整数ALU + 浮点FPU
- 支持FP32、INT32、FP64(部分型号)
- 每个SM(流多处理器)包含几十到上百个CUDA核心
我曾经在优化一个科学计算程序时,发现FP64性能远低于预期。查了半天才发现——消费级显卡的FP64单元被砍掉了大半。嗯,这是个血泪教训。
3.2 Tensor Core:矩阵计算的加速器
Tensor Core是NVIDIA从Volta架构开始引入的专用单元。它专门干一件事——矩阵乘加运算。你想想看,深度学习里的卷积、全连接层,本质上全是矩阵运算。
为什么需要Tensor Core?因为CUDA核心做矩阵乘法太慢了。一个4x4的矩阵乘法,CUDA核心需要16次乘法和12次加法。而Tensor Core一次就能搞定。
| 特性 | CUDA Core | Tensor Core |
|---|---|---|
| 精度支持 | FP32/FP64/INT32 | FP16/BF16/INT8/INT4 |
| 单次运算 | 1个FMA | 4x4矩阵乘加 |
| 适用场景 | 通用计算 | 深度学习/矩阵运算 |
| 吞吐量 | 基准 | 8-16倍于CUDA Core |
我的建议:如果你的程序涉及大量矩阵运算,优先考虑用Tensor Core。但要注意数据精度——Tensor Core默认用FP16,需要做好精度验证。
3.3 SFU与特殊函数单元
除了CUDA核心和Tensor Core,GPU里还有一类特殊单元——SFU(Special Function Unit)。它负责计算sin、cos、log、exp这些超越函数。
为什么需要单独的SFU?因为这些函数用CUDA核心算太慢了。一个sin函数可能需要几十条指令。而SFU用硬件查表+多项式逼近,几个周期就能出结果。
我记得有一次做音频处理,大量用了sin和cos。一开始用CUDA核心硬算,性能惨不忍睹。后来改成调用内置的__sinf()函数,速度直接翻了5倍。这就是SFU的威力。
3.4 计算单元的层级关系
这些计算单元不是散落在芯片上的。它们有严格的层级结构。我画了一张图,帮你理清关系:
从这张图你能看到:
- GPU芯片包含多个GPC(图形处理簇)
- 每个GPC包含多个SM(流多处理器)
- 每个SM内部包含CUDA核心、Tensor Core、SFU
- SM之间通过共享内存和L2缓存通信
3.5 如何选择计算单元?
写CUDA代码时,你不需要手动指定用哪个单元。编译器会自动调度。但了解它们的特性,能帮你写出更高效的代码。
避坑指南:
- 不要假设所有GPU都有Tensor Core——老架构就没有
- Tensor Core对数据对齐要求严格,不对齐会触发回退到CUDA Core
- SFU的精度比CUDA Core低——对精度敏感的场景慎用
我曾经在部署一个推理模型时,发现性能比预期差很多。排查后发现——数据是INT8格式,但没走Tensor Core路径。原因是数据布局不对,编译器自动回退到了CUDA Core。调整数据布局后,性能提升了6倍。
3.6 实战:查看你的GPU计算单元
想知道你的GPU有哪些计算单元?用这个代码就能查:
// 查询GPU计算单元信息
#include <cuda_runtime.h>
#include <stdio.h>
int main() {
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, 0);
printf("GPU型号: %s\n", prop.name);
printf("SM数量: %d\n", prop.multiProcessorCount);
printf("每个SM的CUDA核心数: %d\n",
prop.maxThreadsPerMultiProcessor / 32);
printf("Tensor Core支持: %s\n",
prop.major >= 7 ? "是" : "否");
printf("共享内存大小: %.2f KB\n",
prop.sharedMemPerMultiprocessor / 1024.0);
return 0;
}
运行这个程序,你就能看到自己GPU的家底了。我建议你动手试试——纸上得来终觉浅。
小技巧:用nvidia-smi -q -d COMPUTE也能看到计算单元信息。但代码方式更灵活,可以集成到你的性能分析工具里。
好了,这一章我们聊了GPU计算单元的家族图谱。CUDA核心是通用打工人,Tensor Core是矩阵运算专家,SFU是特殊函数快手。理解它们的特性和适用场景,能帮你写出更高效的CUDA程序。
记住一点:没有最好的计算单元,只有最适合的计算单元。选对了,性能翻倍;选错了,事倍功半。