引言
在CUDA并行编程中,Reduce(归约)和SGEMM(单精度矩阵乘法)是两种基础且关键的运算模式,其性能优化直接影响GPU计算效率。本文系统梳理了Reduce操作从基础到高级的优化技术路线(v1-v8),并简要介绍SGEMM优化思路,旨在帮助开发者深入理解GPU内存架构、线程调度机制,掌握性能瓶颈分析与优化方法,从而在实际项目中实现高效的GPU计算加速。
GPU内存架构图

避免线程束分化

Reduce版本优化
v1
关键优化点:使用共享内存(shared memory)减少全局内存访问次数,提升访存效率。
cpp
__global__ void reduce_v1(float *d_in, float *d_out) {
extern __shared__ float sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
// 从全局内存加载数据到共享内存
sdata[tid] = d_in[i];
__syncthreads();
// 在共享内存中进行归约
for (unsigned int s = 1; s < blockDim.x; s *= 2) {
if (tid % (2 * s) == 0) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
// 将结果写回全局内存
if (tid == 0) {
d_out[blockIdx.x] = sdata[0];
}
}
v2
关键优化点:消除线程束分化(warp divergence),通过改变循环步长避免条件分支,提高warp执行效率。
cpp
__global__ void reduce_v2(float *d_in, float *d_out) {
extern __shared__ float sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
sdata[tid] = d_in[i];
__syncthreads();
// 优化:消除warp divergence
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
if (tid == 0) {
d_out[blockIdx.x] = sdata[0];
}
}
v3
关键优化点:消除bank conflict,通过调整共享内存访问模式避免多个线程同时访问同一bank。
cpp
__global__ void reduce_v3(float *d_in, float *d_out) {
extern __shared__ float sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
sdata[tid] = d_in[i];
__syncthreads();
// 优化:消除bank conflict
for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
// 处理最后32个元素(一个warp)
if (tid < 32) {
// 使用warp shuffle或直接计算
sdata[tid] += sdata[tid + 32];
sdata[tid] += sdata[tid + 16];
sdata[tid] += sdata[tid + 8];
sdata[tid] += sdata[tid + 4];
sdata[tid] += sdata[tid + 2];
sdata[tid] += sdata[tid + 1];
}
if (tid == 0) {
d_out[blockIdx.x] = sdata[0];
}
}
v4.1,主要用来解决idle线程
关键优化点:减少block数量,保持每个block中线程数不变,让每个线程处理更多数据,提高线程利用率。
cpp
__global__ void reduce_v4_1(float *d_in, float *d_out, int n) {
extern __shared__ float sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x * (blockDim.x * 2) + threadIdx.x;
unsigned int gridSize = blockDim.x * 2 * gridDim.x;
// 每个线程处理多个元素
float sum = 0;
while (i < n) {
sum += d_in[i];
if (i + blockDim.x < n) {
sum += d_in[i + blockDim.x];
}
i += gridSize;
}
sdata[tid] = sum;
__syncthreads();
// 归约(使用v2/v3的优化)
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
if (tid == 0) {
d_out[blockIdx.x] = sdata[0];
}
}
v4.2,主要用来解决idle线程
关键优化点:保持block数量不变,减少block中idle线程数量,调整线程分配策略。
cpp
__global__ void reduce_v4_2(float *d_in, float *d_out, int n) {
extern __shared__ float sdata[];
unsigned int tid = threadIdx.x;
unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int stride = blockDim.x * gridDim.x;
// 每个线程处理多个元素,减少idle线程
float sum = 0;
while (i < n) {
sum += d_in[i];
i += stride;
}
sdata[tid] = sum;
__syncthreads();
// 归约
for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
if (tid == 0) {
d_out[blockIdx.x] = sdata[0];
}
}
v5,unroll last warp
__syncthreads()是用来同步不同warp的,但是到最后小于一个warp数量的时候可能会出现warp divergence,那怎么办呢?直接一个warp内的线程做一样的操作就好了volatile关键字
v6. completely unroll
就是不用循环,直接展开
v7. 设置block的合理数量
- 一个thread累加多次,让block不要空闲,可以更好地隐藏延迟
v8. shuffle
- 通过
__shfl_down_sync()进行reduce,避免走shared memory,加速