现代GPU和CPU在设计哲学上的根本差异,源于它们各自面对的计算任务特性。CPU作为通用处理器,需要应对各种不可预测的控制流和复杂逻辑判断,而GPU则专为数据并行计算优化。
CPU采用延迟优化(Latency-Optimized)设计:
GPU采用吞吐优化(Throughput-Optimized)设计:
实际案例:NVIDIA A100 GPU包含6912个CUDA Core,而同期Intel Xeon Platinum 8380 CPU仅有40个物理核心。
CPU的SIMD(单指令多数据):
GPU的SIMT(单指令多线程):
这种差异使得GPU在规则并行计算(如矩阵运算)上具有数量级优势,但在复杂控制流任务上表现不佳。
以NVIDIA Ampere架构为例,其层级结构如下:
code复制GPU芯片
├── GPC(Graphics Processing Cluster)
│ ├── TPC(Texture Processing Cluster)
│ │ ├── SM(Streaming Multiprocessor)
│ │ │ ├── CUDA Core
│ │ │ ├── Tensor Core
│ │ │ ├── 寄存器文件
│ │ │ └── 共享内存/L1缓存
│ │ └── 光栅引擎
│ └── 光栅化引擎
└── L2缓存/显存控制器
NVIDIA A100的关键参数:
技术细节:A100的80GB版本通过堆叠式HBM2e实现更高带宽,采用TSMC的CoWoS(Chip on Wafer on Substrate)封装技术。
单个SM包含以下关键组件:
计算核心
调度资源
存储体系
特殊功能单元
Occupancy(占用率)是衡量SM资源利用率的关键指标:
code复制Occupancy = 活跃Warp数 / 最大支持Warp数(通常64)
影响Occupancy的三大因素:
寄存器限制
共享内存限制
线程块限制
优化技巧:使用CUDA Occupancy Calculator API可精确计算最佳配置。
Warp是GPU调度的最小单位,包含32个线程:
Warp调度特点:
当Warp内线程执行不同分支时:
c++复制if (threadIdx.x % 2 == 0) {
// 分支A
} else {
// 分支B
}
硬件会:
优化建议:
| 架构 | 年份 | 改进点 |
|---|---|---|
| Tesla | 2006 | 首个统一着色器架构 |
| Fermi | 2010 | 引入真正的CUDA Core |
| Kepler | 2012 | 支持动态并行 |
| Maxwell | 2014 | 能效比提升 |
| Pascal | 2016 | 支持FP16 |
| Volta | 2017 | 独立INT核心 |
| Turing | 2018 | 并发FP/INT |
| Ampere | 2020 | 第三代Tensor Core |
| Hopper | 2022 | 第四代Tensor Core |
Tensor Core是专为矩阵运算设计的执行单元:
Ampere架构Tensor Core改进:
| 内存类型 | 延迟(周期) | 带宽 | 容量 | 作用域 |
|---|---|---|---|---|
| 寄存器 | 1 | 最高 | 256KB/SM | 线程私有 |
| 共享内存 | 20-30 | 高 | 164KB/SM | Block共享 |
| L1缓存 | 20-30 | 高 | 128KB/SM | SM内共享 |
| L2缓存 | 200+ | 中 | 40MB | 全芯片共享 |
| 全局内存 | 400+ | 低 | 40-80GB | 全设备可见 |
合并内存访问
共享内存Bank冲突避免
常量内存优化
异步内存操作
CUDA线程层次与硬件映射关系:
code复制Grid → 分配到整个GPU
Block → 分配到SM
Thread → 组成Warp在CUDA Core上执行
关键限制:
假设一个核函数配置:
在A100上的资源分配:
| 型号 | 架构 | FP32 | TF32 | FP16 | INT8 | FP64 | 显存带宽 |
|---|---|---|---|---|---|---|---|
| V100 | Volta | 15.7 | - | 125 | - | 7.8 | 900GB/s |
| A100 | Ampere | 19.5 | 156 | 312 | 624 | 9.7 | 2039GB/s |
| H100 | Hopper | 67 | 989 | 1979 | 3958 | 67 | 3350GB/s |
科学计算:
AI训练:
边缘推理:
图形渲染:
cpp复制#include <cuda_runtime.h>
#include <stdio.h>
void printArchName(int major, int minor) {
printf(" 架构名称: ");
if (major == 9 && minor == 0) printf("Hopper");
else if (major == 8 && minor == 0) printf("Ampere");
else if (major == 7 && minor == 5) printf("Turing");
else if (major == 7 && minor == 0) printf("Volta");
else if (major == 6 && minor == 1) printf("Pascal");
else if (major == 5 && minor == 3) printf("Maxwell");
else printf("Unknown");
printf(" (SM%d%d)\n", major, minor);
}
int main() {
int deviceCount;
cudaGetDeviceCount(&deviceCount);
printf("发现 %d 个CUDA设备:\n", deviceCount);
for (int i = 0; i < deviceCount; i++) {
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, i);
printf("\n设备 %d: \"%s\"\n", i, prop.name);
printArchName(prop.major, prop.minor);
printf(" SM数量: %d\n", prop.multiProcessorCount);
printf(" CUDA核心数: %d\n",
_ConvertSMVer2Cores(prop.major, prop.minor) * prop.multiProcessorCount);
printf(" 时钟频率: %.2f GHz\n", prop.clockRate * 1e-6);
printf(" 内存时钟: %.2f GHz\n", prop.memoryClockRate * 1e-6);
printf(" 内存总线宽度: %d-bit\n", prop.memoryBusWidth);
printf(" 总全局内存: %.2f GB\n",
prop.totalGlobalMem / (1024.0 * 1024 * 1024));
printf(" L2缓存大小: %.2f MB\n", prop.l2CacheSize / (1024.0 * 1024));
printf(" 每个SM最大线程数: %d\n", prop.maxThreadsPerMultiProcessor);
printf(" 每个Block最大线程数: %d\n", prop.maxThreadsPerBlock);
printf(" 每个SM最大寄存器数: %d\n", prop.regsPerMultiprocessor);
printf(" 每个Block最大共享内存: %.2f KB\n",
prop.sharedMemPerBlock / 1024.0);
printf(" 计算能力: %d.%d\n", prop.major, prop.minor);
printf(" 支持并发内核执行: %s\n",
prop.concurrentKernels ? "是" : "否");
printf(" 支持统一内存: %s\n",
prop.unifiedAddressing ? "是" : "否");
}
return 0;
}
bash复制nvcc -arch=sm_80 device_query.cu -o device_query
nvidia-smi -q获取更多信息最大化并行度
隐藏内存延迟
利用特殊指令
__shfl_sync进行Warp内通信__ldg指令读取只读数据__reduce_add_sync进行Warp内归约全局内存访问
cudaMallocPitch处理2D数组共享内存使用
__syncthreads()正确同步寄存器优化
--ptxas-options=-v检查)计算瓶颈特征
内存瓶颈特征
延迟瓶颈特征
Nsight工具套件
CUDA-MEMCHECK
cuda-memcheck ./your_programprintf调试
printf在内核中输出调试信息-arch=sm_XX支持多芯片模块(MCM)
光追加速
AI专用加速
3D堆叠技术
近内存计算
异构计算
原始实现:
cpp复制__global__ void matmul_naive(float *A, float *B, float *C, int N) {
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < N && col < N) {
float sum = 0.0f;
for (int k = 0; k < N; k++) {
sum += A[row * N + k] * B[k * N + col];
}
C[row * N + col] = sum;
}
}
优化版本:
典型优化步骤:
协同组(CG)
异步操作
多设备编程
SYCL/DPC++
OpenMP Offload
高级库
了解目标架构
渐进式优化
性能可移植性
资源平衡