本文介绍如何在GPU上实现多线程块中开多个线程处理任务。
单线程块里开启多个线程
在代码实现上,和开启多个线程块,每个线程块只有一个线程的区别:
- 将 add<<<N,1>>>(dev_a,dev_b,dev_c) 改为 add<<<1,N>>>(dev_a,dev_b,dev_c) 。
容易看出 add<<<N,1>>> 的第一个参数是开启的线程块的数目,第二个参数是一个线程块里的线程数。
- 将 int tid = blockIdx.x; 改为 int tid = threadIdx.x;
代码:
|
|
多个线程块开启多个(固定数量的)线程
和上文第一种情况相比,依然只需要改变核函数的索引计算方法和核函数的调用方式即可:
- 核函数的索引计算方法:
将:
|
|
改为:
|
|
注意的是, blockDim 其实是一个三维的线程数组,但在例子中只用到一维线程块,故只需要用到 blockIdx.x ,该变量是一个常数,保存的是线程块中每一维的线程数量。索引 tid 的值显然为:当前线程块中的线程索引 + 每一个线程块中线程数(这里只有一维) * 当前线程块索引(从0开始)。
- 核函数的调用方式: 将:
|
|
改为:
|
|
由于 N 是一个整型,N+127/128 是为了向上取整(可多不可少)。
在GPU上实现任意长度的矢量求和
由于单线程格里的线程块有一个最大值(比如65535),当GPU处理的矢量数目超过,在情况2中当 (N + 127) / 128 超过65535时,函数就会调用失败,这时候可以这样处理:将调用的线程块数目和每个线程块中调用线程的数目都固定,比如都固定为128。然后利用这个线程格中的 128 * 128 个线程顺序处理GPU上的所有任务。比如:索引号为1的线程处理完id为 1 的任务后会跳转到id为 1+ 128 * 128 的任务进行处理,故我们只需要知道如何计算每一个并行线程的初始索引,以及如何确定递增量的大小,就可完成GPU上所有任务的处理。
和情况2相比:
- 核函数的索引计算方法: 将:
|
|
改为:
|
|
- 核函数的调用方式: 将:
|
|
改为:
|
|
即可。
完整代码:
|
|