01-CUDA编程模型与内存层次
概述
CUDA(Compute Unified Device Architecture)是 NVIDIA 推出的并行计算平台和编程模型。深入理解 CUDA 编程模型和内存层次结构,是开发高性能 AI 算子的基础。
GPU 架构基础
硬件架构层次
┌─────────────────────────────────────────────────────────────────────────┐
│ NVIDIA GPU 架构 (A100) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ GPU Device │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ GPC (8个) │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │ │
│ │ │ │ TPC (2个/GPC) │ │ │ │
│ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ SM (2个/TPC, 共108个) │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ │ │
│ │ │ │ │ │ CUDA Cores: 64 FP32 + 64 INT32 │ │ │ │ │ │
│ │ │ │ │ │ Tensor Cores: 4 (第三代) │ │ │ │ │ │
│ │ │ │ │ │ Register File: 256KB │ │ │ │ │ │
│ │ │ │ │ │ Shared Memory: 164KB (可配置) │ │ │ │ │ │
│ │ │ │ │ │ L1 Cache: 与 Shared Memory 共享 │ │ │ │ │ │
│ │ │ │ │ └──────────────────────────────────────┘ │ │ │ │ │
│ │ │ │ └────────────────────────────────────────────┘ │ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ L2 Cache: 40MB │ │
│ │ HBM2e Memory: 80GB, 2TB/s bandwidth │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
SM (Streaming Multiprocessor) 详解
┌─────────────────────────────────────────────────────────────────────────┐
│ SM 内部结构 (A100) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Instruction Cache │ │
│ └─────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────┴───────────────────────────────────┐ │
│ │ Warp Scheduler (4个) │ │
│ │ 每个调度器管理一个 Warp (32线程),每周期可发射 1 条指令 │ │
│ └─────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────┴───────────────────────────────────┐ │
│ │ Processing Blocks (4个) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ FP32 Units │ │ INT32 Units │ │ Tensor Core │ │ │
│ │ │ (16个/Block) │ │ (16个/Block) │ │ (1个/Block) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ LD/ST Units │ │ SFU (特殊函数)│ │ │
│ │ │ (8个/Block) │ │ (4个/Block) │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Register File (256KB) │ │
│ │ 每个线程最多 255 个 32-bit 寄存器 │ │
│ │ 每个 SM 最多 65536 个 32-bit 寄存器 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Shared Memory / L1 Cache (164KB) │ │
│ │ 可配置:Shared Memory 优先 或 L1 Cache 优先 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
CUDA 编程模型
线程层次结构
┌─────────────────────────────────────────────────────────────────────────┐
│ CUDA 线程层次 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Grid (网格) │
│ ├── 一个 Kernel 启动一个 Grid │
│ ├── Grid 包含多个 Block │
│ └── Grid 可以是 1D/2D/3D │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Grid │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Block │ │ Block │ │ Block │ │ Block │ ... │ │
│ │ │ (0,0) │ │ (1,0) │ │ (2,0) │ │ (3,0) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Block │ │ Block │ │ Block │ │ Block │ ... │ │
│ │ │ (0,1) │ │ (1,1) │ │ (2,1) │ │ (3,1) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Block (线程块) │
│ ├── 在单个 SM 上执行 │
│ ├── Block 内线程可以同步和共享数据 │
│ ├── 最大 1024 线程/Block │
│ └── Block 可以是 1D/2D/3D │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Block │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ... │ │
│ │ │Warp │ │Warp │ │Warp │ │Warp │ │Warp │ │Warp │ │Warp │ │ │
│ │ │ 0 │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ 5 │ │ 6 │ │ │
│ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Warp (线程束) │
│ ├── 32 个线程组成 │
│ ├── SIMT 执行模型:同一指令,不同数据 │
│ └── Warp 是 GPU 调度的基本单位 │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Warp (32 threads) │ │
│ │ [T0][T1][T2][T3][T4][T5][T6][T7]...[T28][T29][T30][T31] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
基本 Kernel 编写
// vector_add.cu - 向量加法示例
#include <cuda_runtime.h>
#include <stdio.h>
// CUDA 错误检查宏
#define CUDA_CHECK(call) \
do { \
cudaError_t err = call; \
if (err != cudaSuccess) { \
fprintf(stderr, "CUDA Error at %s:%d - %s\n", \
__FILE__, __LINE__, cudaGetErrorString(err)); \
exit(EXIT_FAILURE); \
} \
} while(0)
// Kernel 函数:每个线程处理一个元素
__global__ void vector_add_kernel(
const float* __restrict__ a,
const float* __restrict__ b,
float* __restrict__ c,
int n
) {
// 计算全局线程索引
int idx = blockIdx.x * blockDim.x + threadIdx.x;
// 边界检查
if (idx < n) {
c[idx] = a[idx] + b[idx];
}
}
// 优化版本:每个线程处理多个元素 (Grid-Stride Loop)
__global__ void vector_add_kernel_v2(
const float* __restrict__ a,
const float* __restrict__ b,
float* __restrict__ c,
int n
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int stride = blockDim.x * gridDim.x;
// Grid-Stride Loop:处理比线程数更多的元素
for (int i = idx; i < n; i += stride) {
c[i] = a[i] + b[i];
}
}
// 向量化访问版本:使用 float4 一次加载 4 个元素
__global__ void vector_add_kernel_v3(
const float4* __restrict__ a,
const float4* __restrict__ b,
float4* __restrict__ c,
int n // n 是 float4 的数量
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int stride = blockDim.x * gridDim.x;
for (int i = idx; i < n; i += stride) {
float4 a_val = a[i];
float4 b_val = b[i];
float4 c_val;
c_val.x = a_val.x + b_val.x;
c_val.y = a_val.y + b_val.y;
c_val.z = a_val.z + b_val.z;
c_val.w = a_val.w + b_val.w;
c[i] = c_val;
}
}
void vector_add(const float* a, const float* b, float* c, int n) {
float *d_a, *d_b, *d_c;
size_t size = n * sizeof(float);
// 分配设备内存
CUDA_CHECK(cudaMalloc(&d_a, size));
CUDA_CHECK(cudaMalloc(&d_b, size));
CUDA_CHECK(cudaMalloc(&d_c, size));
// 拷贝数据到设备
CUDA_CHECK(cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice));
// 配置执行参数
int block_size = 256;
int grid_size = (n + block_size - 1) / block_size;
// 启动 Kernel
vector_add_kernel<<<grid_size, block_size>>>(d_a, d_b, d_c, n);
CUDA_CHECK(cudaGetLastError());
// 拷贝结果回主机
CUDA_CHECK(cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost));
// 释放设备内存
CUDA_CHECK(cudaFree(d_a));
CUDA_CHECK(cudaFree(d_b));
CUDA_CHECK(cudaFree(d_c));
}
线程索引计算
// 1D Grid, 1D Block
__global__ void kernel_1d() {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
}
// 2D Grid, 2D Block
__global__ void kernel_2d() {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// 线性索引 (row-major)
int idx = y * (gridDim.x * blockDim.x) + x;
}
// 3D Grid, 3D Block
__global__ void kernel_3d() {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int z = blockIdx.z * blockDim.z + threadIdx.z;
int width = gridDim.x * blockDim.x;
int height = gridDim.y * blockDim.y;
int idx = z * (width * height) + y * width + x;
}
// 矩阵操作常用索引
__global__ void matrix_kernel(float* mat, int rows, int cols) {
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < rows && col < cols) {
int idx = row * cols + col; // row-major
mat[idx] = ...;
}
}
内存层次详解
内存类型与特性
┌─────────────────────────────────────────────────────────────────────────┐
│ CUDA 内存层次 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 寄存器 (Registers) │ │
│ │ ├── 速度:最快 (~0 周期延迟) │ │
│ │ ├── 容量:每 SM 256KB,每线程最多 255 个 │ │
│ │ ├── 作用域:单个线程私有 │ │
│ │ └── 声明:自动变量 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 共享内存 (Shared Memory) │ │
│ │ ├── 速度:~5-10 周期延迟 │ │
│ │ ├── 容量:每 SM 最大 164KB (A100) │ │
│ │ ├── 作用域:Block 内所有线程共享 │ │
│ │ ├── 声明:__shared__ │ │
│ │ └── 特点:需要显式同步 __syncthreads() │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ L1 Cache / 纹理缓存 │ │
│ │ ├── 速度:~28-32 周期延迟 │ │
│ │ ├── 容量:与 Shared Memory 共享配置 │ │
│ │ ├── 作用域:SM 级别 │ │
│ │ └── 特点:硬件管理,对程序员透明 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ L2 Cache │ │
│ │ ├── 速度:~200 周期延迟 │ │
│ │ ├── 容量:40MB (A100) │ │
│ │ ├── 作用域:设备级别,所有 SM 共享 │ │
│ │ └── 特点:硬件管理 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 全局内存 (Global Memory / HBM) │ │
│ │ ├── 速度:~400-600 周期延迟 │ │
│ │ ├── 容量:80GB (A100) │ │
│ │ ├── 带宽:2TB/s (A100 HBM2e) │ │
│ │ ├── 作用域:设备级别,所有线程可访问 │ │
│ │ └── 声明:cudaMalloc / __device__ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 常量内存 (Constant Memory) │ │
│ │ ├── 容量:64KB │ │
│ │ ├── 特点:只读,有专用缓存 │ │
│ │ └── 声明:__constant__ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
共享内存使用
// 矩阵乘法 - 使用共享内存优化
#define TILE_SIZE 32
__global__ void matmul_shared(
const float* __restrict__ A, // [M, K]
const float* __restrict__ B, // [K, N]
float* __restrict__ C, // [M, N]
int M, int N, int K
) {
// 声明共享内存
__shared__ float As[TILE_SIZE][TILE_SIZE];
__shared__ float Bs[TILE_SIZE][TILE_SIZE];
int bx = blockIdx.x;
int by = blockIdx.y;
int tx = threadIdx.x;
int ty = threadIdx.y;
// 计算该线程负责的 C 矩阵位置
int row = by * TILE_SIZE + ty;
int col = bx * TILE_SIZE + tx;
float sum = 0.0f;
// 分块计算
for (int t = 0; t < (K + TILE_SIZE - 1) / TILE_SIZE; t++) {
// 协作加载 A 的一个 tile 到共享内存
if (row < M && t * TILE_SIZE + tx < K) {
As[ty][tx] = A[row * K + t * TILE_SIZE + tx];
} else {
As[ty][tx] = 0.0f;
}
// 协作加载 B 的一个 tile 到共享内存
if (t * TILE_SIZE + ty < K && col < N) {
Bs[ty][tx] = B[(t * TILE_SIZE + ty) * N + col];
} else {
Bs[ty][tx] = 0.0f;
}
// 同步,确保 tile 加载完成
__syncthreads();
// 计算部分和
#pragma unroll
for (int k = 0; k < TILE_SIZE; k++) {
sum += As[ty][k] * Bs[k][tx];
}
// 同步,确保计算完成后再加载下一个 tile
__syncthreads();
}
// 写回结果
if (row < M && col < N) {
C[row * N + col] = sum;
}
}
共享内存 Bank Conflict
/*
* 共享内存被分成 32 个 Bank (每个 Bank 4 字节宽)
* 同一 Warp 中的线程访问同一 Bank 的不同地址会产生 Bank Conflict
*
* 地址到 Bank 的映射:bank_id = (address / 4) % 32
*/
// Bank Conflict 示例
__shared__ float smem[32][32];
// 无冲突:每个线程访问不同的 Bank
float val = smem[threadIdx.x][0]; // 所有线程访问第 0 列,不同行
// 有冲突:所有线程访问同一 Bank
float val = smem[0][threadIdx.x]; // 当 threadIdx.x 是 32 的倍数时冲突
// 解决方案:Padding
__shared__ float smem_padded[32][32 + 1]; // 每行加 1 个元素的 padding
// 使用 padding 后无冲突
float val = smem_padded[0][threadIdx.x];
// 示例:转置矩阵时的 Bank Conflict 处理
__global__ void transpose_naive(float* out, const float* in, int N) {
__shared__ float tile[32][32];
int x = blockIdx.x * 32 + threadIdx.x;
int y = blockIdx.y * 32 + threadIdx.y;
// 读取 (coalesced)
if (x < N && y < N) {
tile[threadIdx.y][threadIdx.x] = in[y * N + x];
}
__syncthreads();
// 写入 (有 bank conflict)
x = blockIdx.y * 32 + threadIdx.x;
y = blockIdx.x * 32 + threadIdx.y;
if (x < N && y < N) {
out[y * N + x] = tile[threadIdx.x][threadIdx.y]; // Bank conflict!
}
}
__global__ void transpose_optimized(float* out, const float* in, int N) {
__shared__ float tile[32][32 + 1]; // Padding 消除 bank conflict
int x = blockIdx.x * 32 + threadIdx.x;
int y = blockIdx.y * 32 + threadIdx.y;
if (x < N && y < N) {
tile[threadIdx.y][threadIdx.x] = in[y * N + x];
}
__syncthreads();
x = blockIdx.y * 32 + threadIdx.x;
y = blockIdx.x * 32 + threadIdx.y;
if (x < N && y < N) {
out[y * N + x] = tile[threadIdx.x][threadIdx.y]; // 无 bank conflict
}
}
全局内存合并访问
/*
* 全局内存访问以 32/64/128 字节的事务为单位
* Warp 中连续线程访问连续内存地址时可以合并为一次事务
*/
// 合并访问 (Coalesced Access) - 高效
__global__ void coalesced_access(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
// 线程 0 访问 data[0],线程 1 访问 data[1]...
// 连续线程访问连续地址,可合并
float val = data[idx];
}
}
// 跨步访问 (Strided Access) - 低效
__global__ void strided_access(float* data, int n, int stride) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx * stride < n) {
// 线程 0 访问 data[0],线程 1 访问 data[stride]...
// 不连续,需要多次内存事务
float val = data[idx * stride];
}
}
// 随机访问 - 最低效
__global__ void random_access(float* data, int* indices, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
// 随机索引,几乎无法合并
float val = data[indices[idx]];
}
}
// 优化跨步访问:使用共享内存
__global__ void strided_access_optimized(
float* data,
float* output,
int n,
int stride
) {
__shared__ float smem[256];
int tid = threadIdx.x;
int block_start = blockIdx.x * blockDim.x * stride;
// 合并读取到共享内存
if (block_start + tid < n) {
smem[tid] = data[block_start + tid];
}
__syncthreads();
// 从共享内存跨步读取(共享内存访问无合并要求)
int local_idx = tid * stride % blockDim.x;
float val = smem[local_idx];
// 合并写回
int out_idx = blockIdx.x * blockDim.x + tid;
if (out_idx < n / stride) {
output[out_idx] = val;
}
}
内存管理 API
基本内存操作
#include <cuda_runtime.h>
// 1. 设备内存分配
float* d_data;
size_t size = N * sizeof(float);
cudaMalloc(&d_data, size); // 分配设备内存
cudaMallocManaged(&d_data, size); // 分配统一内存
cudaMallocHost(&h_data, size); // 分配页锁定主机内存
cudaMallocPitch(&d_data, &pitch, width, height); // 分配 2D 内存(带对齐)
// 2. 内存拷贝
cudaMemcpy(dst, src, size, cudaMemcpyHostToDevice);
cudaMemcpy(dst, src, size, cudaMemcpyDeviceToHost);
cudaMemcpy(dst, src, size, cudaMemcpyDeviceToDevice);
// 异步拷贝
cudaMemcpyAsync(dst, src, size, kind, stream);
// 2D 拷贝
cudaMemcpy2D(dst, dpitch, src, spitch, width, height, kind);
// 3. 内存设置
cudaMemset(d_data, 0, size); // 按字节设置
cudaMemsetAsync(d_data, 0, size, stream); // 异步设置
// 4. 内存释放
cudaFree(d_data);
cudaFreeHost(h_data);
// 5. 查询设备内存
size_t free_mem, total_mem;
cudaMemGetInfo(&free_mem, &total_mem);
统一内存 (Unified Memory)
// 统一内存示例
__global__ void kernel(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
data[idx] *= 2.0f;
}
}
void unified_memory_example() {
float* data;
int n = 1000000;
// 分配统一内存
cudaMallocManaged(&data, n * sizeof(float));
// CPU 初始化(自动在 CPU 端分页)
for (int i = 0; i < n; i++) {
data[i] = i;
}
// GPU 计算(自动迁移到 GPU)
int block_size = 256;
int grid_size = (n + block_size - 1) / block_size;
kernel<<<grid_size, block_size>>>(data, n);
// 等待完成
cudaDeviceSynchronize();
// CPU 访问结果(自动迁移回 CPU)
printf("data[0] = %f\n", data[0]);
cudaFree(data);
}
// 预取优化
void prefetch_example() {
float* data;
cudaMallocManaged(&data, n * sizeof(float));
int device;
cudaGetDevice(&device);
// 预取到 GPU
cudaMemPrefetchAsync(data, n * sizeof(float), device, 0);
kernel<<<grid_size, block_size>>>(data, n);
// 预取回 CPU
cudaMemPrefetchAsync(data, n * sizeof(float), cudaCpuDeviceId, 0);
cudaDeviceSynchronize();
}
// 内存建议
void memory_advise_example() {
float* data;
cudaMallocManaged(&data, n * sizeof(float));
int device;
cudaGetDevice(&device);
// 设置为只读(优化访问)
cudaMemAdvise(data, n * sizeof(float), cudaMemAdviseSetReadMostly, device);
// 设置首选位置
cudaMemAdvise(data, n * sizeof(float), cudaMemAdviseSetPreferredLocation, device);
// 设置访问者
cudaMemAdvise(data, n * sizeof(float), cudaMemAdviseSetAccessedBy, device);
}
页锁定内存与异步传输
// 使用页锁定内存实现 CPU-GPU 重叠
void async_transfer_example() {
const int N = 1 << 20;
const int num_streams = 4;
const int chunk_size = N / num_streams;
// 分配页锁定主机内存
float *h_data, *h_result;
cudaMallocHost(&h_data, N * sizeof(float));
cudaMallocHost(&h_result, N * sizeof(float));
// 分配设备内存
float *d_data, *d_result;
cudaMalloc(&d_data, N * sizeof(float));
cudaMalloc(&d_result, N * sizeof(float));
// 创建多个流
cudaStream_t streams[num_streams];
for (int i = 0; i < num_streams; i++) {
cudaStreamCreate(&streams[i]);
}
// 分块异步处理
for (int i = 0; i < num_streams; i++) {
int offset = i * chunk_size;
// 异步拷贝到设备
cudaMemcpyAsync(
d_data + offset,
h_data + offset,
chunk_size * sizeof(float),
cudaMemcpyHostToDevice,
streams[i]
);
// 启动 kernel
int block_size = 256;
int grid_size = (chunk_size + block_size - 1) / block_size;
kernel<<<grid_size, block_size, 0, streams[i]>>>(
d_data + offset,
d_result + offset,
chunk_size
);
// 异步拷贝回主机
cudaMemcpyAsync(
h_result + offset,
d_result + offset,
chunk_size * sizeof(float),
cudaMemcpyDeviceToHost,
streams[i]
);
}
// 等待所有流完成
cudaDeviceSynchronize();
// 清理
for (int i = 0; i < num_streams; i++) {
cudaStreamDestroy(streams[i]);
}
cudaFreeHost(h_data);
cudaFreeHost(h_result);
cudaFree(d_data);
cudaFree(d_result);
}
线程同步
同步原语
// 1. Block 内同步
__global__ void block_sync_example() {
__shared__ float smem[256];
// 写入共享内存
smem[threadIdx.x] = threadIdx.x;
// 等待所有线程完成写入
__syncthreads();
// 安全地读取其他线程写入的数据
float val = smem[(threadIdx.x + 1) % 256];
}
// 2. Warp 内同步 (CUDA 9.0+)
__global__ void warp_sync_example() {
int lane = threadIdx.x % 32;
unsigned mask = 0xffffffff; // 所有 32 个线程
float val = lane;
// Warp 内同步
__syncwarp(mask);
// Warp shuffle:直接从其他线程获取值
float neighbor = __shfl_sync(mask, val, (lane + 1) % 32);
// Warp 投票
int vote = __ballot_sync(mask, val > 16);
// Warp reduce
for (int offset = 16; offset > 0; offset /= 2) {
val += __shfl_down_sync(mask, val, offset);
}
}
// 3. 原子操作
__global__ void atomic_example(int* counter, float* sum, float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
// 原子加
atomicAdd(counter, 1);
atomicAdd(sum, data[idx]);
// 原子比较交换
int old = atomicCAS(counter, 0, 1);
// 原子最大/最小
atomicMax(counter, idx);
atomicMin(counter, idx);
// 原子交换
int prev = atomicExch(counter, idx);
}
}
// 4. 内存栅栏
__global__ void memory_fence_example() {
__shared__ int flag;
__shared__ float data;
if (threadIdx.x == 0) {
data = 42.0f;
__threadfence_block(); // 确保 data 对 block 内线程可见
flag = 1;
}
__syncthreads();
if (flag == 1) {
float val = data; // 保证读到正确的值
}
}
Cooperative Groups (CUDA 9.0+)
#include <cooperative_groups.h>
namespace cg = cooperative_groups;
__global__ void cooperative_groups_example(float* data, int n) {
// 获取线程块
cg::thread_block block = cg::this_thread_block();
// 获取 Warp
cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);
// Block 内同步
block.sync();
// Warp 内同步
warp.sync();
// Warp 内 reduce
int lane = warp.thread_rank();
float val = data[blockIdx.x * blockDim.x + threadIdx.x];
for (int offset = warp.size() / 2; offset > 0; offset /= 2) {
val += warp.shfl_down(val, offset);
}
// 只有 lane 0 有最终结果
if (lane == 0) {
// 使用 val
}
}
// Grid 级别同步(需要 cooperative launch)
__global__ void grid_sync_example(float* data, int n) {
cg::grid_group grid = cg::this_grid();
// 第一阶段计算
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
data[idx] *= 2;
}
// Grid 同步:等待所有 block 完成
grid.sync();
// 第二阶段计算
if (idx < n) {
data[idx] += data[(idx + 1) % n];
}
}
// 启动 Grid 同步 kernel
void launch_grid_sync_kernel() {
int device;
cudaGetDevice(&device);
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, device);
// 计算最大 block 数
int num_blocks_per_sm;
cudaOccupancyMaxActiveBlocksPerMultiprocessor(
&num_blocks_per_sm, grid_sync_example, 256, 0);
int num_blocks = num_blocks_per_sm * prop.multiProcessorCount;
// 使用 cooperative launch
void* args[] = { &d_data, &n };
cudaLaunchCooperativeKernel(
(void*)grid_sync_example,
num_blocks, 256,
args
);
}
性能分析与调优
占用率 (Occupancy)
// 占用率计算
void calculate_occupancy() {
int block_size = 256;
int min_grid_size;
int optimal_block_size;
// 自动计算最优 block size
cudaOccupancyMaxPotentialBlockSize(
&min_grid_size,
&optimal_block_size,
my_kernel,
0, // 动态共享内存大小
0 // block size 限制
);
// 计算给定 block size 的占用率
int max_active_blocks;
cudaOccupancyMaxActiveBlocksPerMultiprocessor(
&max_active_blocks,
my_kernel,
block_size,
0 // 动态共享内存大小
);
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, 0);
float occupancy = (float)(max_active_blocks * block_size) /
prop.maxThreadsPerMultiProcessor;
printf("Occupancy: %.2f%%\n", occupancy * 100);
}
// 影响占用率的因素
/*
1. 寄存器使用量
- 每个 SM 有固定数量的寄存器
- 线程使用的寄存器越多,能并发的线程越少
2. 共享内存使用量
- 每个 SM 有固定的共享内存
- Block 使用的共享内存越多,能并发的 Block 越少
3. Block Size
- 必须是 32 的倍数(Warp size)
- 太小会限制并行度,太大可能受资源限制
*/
// 查看寄存器使用量
// 编译时加 --ptxas-options=-v 查看
// nvcc -ptxas-options=-v kernel.cu
Nsight 分析指标
# 使用 Nsight Compute 分析
ncu --set full ./my_program
# 关键指标:
# 1. Memory Throughput - 内存带宽利用率
# 2. Compute Throughput - 计算利用率
# 3. SM Occupancy - SM 占用率
# 4. Memory Bound / Compute Bound - 瓶颈分析
# 5. Warp Stall Reasons - Warp 停顿原因
# 使用 Nsight Systems 分析时间线
nsys profile ./my_program
总结
CUDA 编程核心要点
| 概念 | 要点 |
|---|---|
| 线程层次 | Grid → Block → Warp → Thread |
| 内存层次 | 寄存器 → 共享内存 → L1/L2 → 全局内存 |
| 性能优化 | 合并访问、避免 Bank Conflict、提高占用率 |
| 同步机制 | __syncthreads、__syncwarp、原子操作 |
性能优化检查清单
□ 全局内存访问是否合并?
□ 共享内存是否有 Bank Conflict?
□ 占用率是否足够?
□ 是否有 Warp Divergence?
□ 是否充分利用内存带宽?
□ 是否隐藏了内存延迟?
□ 是否使用了异步操作?