如何对GPU中的reduce算法进行优化。官方地址
https://developer.download.nvidia.cn/assets/cuda/files/reduction.pdf
reduce算法的本质:
并行算法设计
GPU中,reduce采用了树形的计算方式
从上到下,将数据不断累加,直到得出最后的结果,25。GPU没有针对global数据的同步操作,只能针对block的数据进行同步。所以将reduce分为两个阶段,示意图如下:
假设给定一个长度为N的数组,需要计算该数组的所有元素之和。首先需要将数组分为m个小份。在第一阶段,开启m个block计算出m个小份的reduce值。在第二阶段,使用一个block将m个小份再次进行reduce,得到最终结果。由于第二段本质上可以调用第一阶段的kernel,所以本文只探索第一阶段的优化技巧。
kernel接口
__global__void reduce(T *input, T* output)
input:输入的数组,一个长度为N的数组;output:输出的数组,第一阶段的结果,长度为M的数组。
在开始CUDA编程前,设置三个参数:
1.BlockNum:开启的block数量,即M,代表需要将数组切成几份
2.Thread_per_block:每个block中开启的线程束,一般:128,256,512,1024
3.Num_per_block:每个block需要进行reduce操作的长度
其中:BlockNum*Num_per_block = N
reduce baseline算法:if (tid%(2*s)==0)
三个步骤:
1.将数据load至shared memory中
2.在shared memory中对数据进行reduce操作
3.将最后的结果写回global memory中。
在第一个步骤中,让Num_per_block和Thread_per_block一致,每个block设置256个线程,一个block负责256个数据的reduce工作。
假设需要处理32M()的数据,则有128K(
)个block。
tid:线程号;i:原始数组中的索引号。第tid号线程将第i号的数据从global中取出,放到shared memory的第tid元素中。比如在第0号block中,0号线程将0号元素取出,放到shared memory的第0号位置。
从硬件角度分析,为了执行代码,GPU需要分配存储资源和计算资源。存储资源包括在global memory中分配的一块32M*sizeof(float)的空间以及在shared memory中分配的256*sizeof(float)的空间。shared