我正在研究一个执行矢量点积(A x B)的Cuda内核。我假设每个向量的长度是32(32,64,...)的倍数,并将块大小定义为等于数组的长度。块中的每个线程将A的一个元素与B的对应元素相乘(线程i ==> psum = A [i] xB [i])。在乘法之后,我使用了以下函数,这些函数使用warp shuffling技术来执行约简并计算所有乘法的总和。
__inline__ __device__
float warpReduceSum(float val) {
int warpSize =32;
for (int offset = warpSize/2; offset > 0; offset /= 2)
val += __shfl_down(val, offset);
return val;
}
__inline__ __device__
float blockReduceSum(float val) {
static __shared__ int shared[32]; // Shared mem for 32 partial sums
int lane = threadIdx.x % warpSize;
int wid = threadIdx.x / warpSize;
val = warpReduceSum(val); // Each warp performs partial reduction
if (lane==0)
shared[wid]=val; // Write reduced value to shared memory
__syncthreads(); // Wait for all partial reductions
//read from shared memory only if that warp existed
val = (threadIdx.x < blockDim.x / warpSize) ? shared[lane] : 0;
if (wid==0)
val = warpReduceSum(val); // Final reduce within first warp
return val;
}
我只是调用blockReduceSum(psum),其中psum是一个线程对两个元素的乘法。
当数组的长度不是32的倍数时,这种方法不起作用,所以我的问题是,我们可以更改此代码,以便它也适用于任何长度?或者它是不可能的,因为如果数组的长度不是32的倍数,一些warp的元素属于多个数组?
答案 0 :(得分:2)
首先,根据您使用的GPU,执行只有1个块的点积可能效率不高(只要您不在1个内核中批处理几个点产品,每个产品都由一个块完成)
回答你的问题:你可以通过调用你的内核来重用你编写的代码,其中线程数是高于N
(数组长度)32的最接近的倍数并引入{{1调用if
之前的语句是这样的:
blockReduceSum
这样,没有与之关联的数组元素的线程,但由于使用__global__ void kernel(float * A, float * B, int N) {
float psum = 0;
if(threadIdx.x < N) //threadIDx.x because your are using single block, you will need to change it to more general id once you move to multiple blocks
psum = A[threadIdx.x] * B[threadIdx.x];
blockReduceSum(psum);
//The rest of computation
}
而需要存在的线程,将为总和贡献0。