归约算法优化
归约优化核心原则
- 优化目标: 将 O(n) 串行计算优化为 O(log n) 并行计算
- 核心技术:分治策略、共享内存、线程束洗牌指令
归约算法原理
问题定义
计算:
分治策略
将大规模问题分解为多个小问题,分别处理后再汇总:
基础实现(分治 + 共享内存)
两阶段归约
第一阶段:每个线程块计算局部和
template<int THREAD_PER_BLOCK>
__global__ void ReduceLarge(float* in, float* out_partial, int num_ele) {
__shared__ float sdata[THREAD_PER_BLOCK];
unsigned int tid = threadIdx.x;
unsigned int bid = blockIdx.x;
// 1. 从全局内存加载数据到共享内存
unsigned int offset = bid * THREAD_PER_BLOCK + tid;
sdata[tid] = in[offset];
__syncthreads();
// 2. 在共享内存中进行归约
for (int s = THREAD_PER_BLOCK / 2; s >= 2; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
// 3. 写入局部结果
if (tid == 0) {
out_partial[bid] = sdata[0] + sdata[1];
}
}
第二阶段:对局部结果再次归约
__global__ void ReduceSmall(float* in_partial, float* out, int partial_num) {
__shared__ float sdata[1024];
unsigned int tid = threadIdx.x;
// 加载局部结果
sdata[tid] = in_partial[tid];
__syncthreads();
// 归约
for (int s = partial_num / 2; s > 0; s >>= 1) {
if (tid < s) {
sdata[tid] += sdata[tid + s];
}
__syncthreads();
}
// 写入最终结果
if (tid == 0) {
out[0] = sdata[0];
}
}
启动配置:
constexpr int THREAD_PER_BLOCK = 1024;
int num_ele = 40960;
int partial_num = num_ele / THREAD_PER_BLOCK;
// 第一阶段
ReduceLarge<THREAD_PER_BLOCK><<<partial_num, THREAD_PER_BLOCK>>>(
in_ptr, out_partial_ptr, num_ele);
// 第二阶段
ReduceSmall<<<1, partial_num>>>(out_partial_ptr, out_ptr, partial_num);