具有约简的Cuda内核 - 2个矩阵的点积的逻辑错误

时间:2013-03-30 02:08:19

标签: cuda gpu gpu-programming reduction

我刚刚开始使用CUDA,并试图将我的大脑包裹在CUDA减少算法中。就我而言,我一直试图得到两个矩阵的点积。但我只得到大小为2的矩阵的正确答案。对于任何其他大小的矩阵,我错了。

这只是测试,因此我保持矩阵尺寸非常小。只有大约100个,所以只有1个块才适合它。 任何帮助将不胜感激。谢谢!

这是常规代码

float* ha = new float[n]; // matrix a
float* hb = new float[n]; // matrix b
float* hc = new float[1]; // sum of a.b

float dx = hc[0];
float hx = 0;
// dot product
for (int i = 0; i < n; i++)
    hx += ha[i] * hb[i];

这是我的cuda内核

__global__ void sum_reduce(float* da, float* db, float* dc, int n)
 {
     int tid = threadIdx.x;
     dc[tid] = 0;
     for (int stride = 1; stride < n; stride *= 2) {
         if (tid % (2 * stride) == 0)
                 dc[tid] += (da[tid] * db[tid]) + (da[tid+stride] * db[tid+stride]);
         __syncthreads();
     }
 }

我的完整代码:http://pastebin.com/zS85URX5

1 个答案:

答案 0 :(得分:2)

希望你能弄明白为什么它适用于n = 2的情况,所以让我们跳过它,看看它为什么在其他情况下失败,让我们选择n = 4。当n = 4时,您有4个线程,编号为0到3.

在for循环的第一次迭代中,stride = 1,因此传递if测试的线程是线程0和2。

thread 0:   dc[0] += da[0]*db[0] + da[1]*db[1];
thread 2:   dc[2] += da[2]*db[2] + da[3]*db[3];

到目前为止一切顺利。在for循环的第二次迭代中,stride为2,因此传递if测试的线程是线程0(仅)。

thread 0:   dc[0] += da[0]*db[0] + da[2]*db[2]; 

但这没有意义,也不是我们想要的。我们想要的是:

dc[0] += dc[2];

所以它被打破了。我花了一些时间试着考虑如何在几个步骤中解决这个问题,但这对我来说只是减少了。如果用这段代码替换你的内核代码,我认为你会有很好的结果。它并不像你的代码那么多,但它是我能找到的最适合你所设想的所有情况的东西(即n&lt;最大线程块大小,使用单个块):

 // CUDA kernel code
 __global__ void sum_reduce(float* da, float* db, float* dc, int n)
 {
     int tid = threadIdx.x;
     // do multiplication in parallel for full width of threads
     dc[tid] = da[tid] * db[tid];
     // wait for all threads to complete multiply step
     __syncthreads();
     int stride = blockDim.x;
     while (stride > 1){
       // handle odd step
       if ((stride & 1) && (tid == 0)) dc[0] += dc[stride - 1];
       // successively divide problem by 2
       stride >>= 1;
       // add each upper half element to each lower half element
       if (tid < stride) dc[tid] += dc[tid + stride];
       // wait for all threads to complete add step
       __syncthreads();
       }
 }

请注意,我并没有真正使用n参数。由于您使用n个线程启动内核,因此blockDim.x内置变量在这种情况下等于n