2013-04-22 39 views
0

让我从这篇文章道歉开始。我知道有几个帖子提出了同样的问题,但我已经尝试了所给出的解决方案,而且我仍然无法获得CUDA矩阵乘法的正确结果。CUDA矩阵乘法不正确的结果

从我遵循的示例中,我很确定我的内核算法是正确的。我不相信将2D数组传递给内核时有任何问题,并且当它们通过引用传递时,我觉得在数组打印到主机中时,2D解决方案数组应该包含正确的答案,但事实并非如此。

难道这是我的dim3 dimGrid(B,B)和dim3 dimThreads(T,T)变量的问题吗?我是CUDA框架的新手,我仍然试图围绕它进行研究。任何建议将非常感激。我的代码如下:

#include <stdio.h> 
#include <cuda.h> 
#include <stdlib.h> 

__global__ void MatMultiply (int *a, int *b, int *c, int N) { 
    int row = blockIdx.y * blockDim.y + threadIdx.y; 
    int col = blockIdx.x * blockDim.x + threadIdx.x; 
    int val = 0; 

    for (int e = 0; e < N; ++e) { 
     val += a[row*N + e] * b[e*N + col]; 
    } 
    c[row*N+col] = val; 
} 

int main(void) { 
    int N, B, T; 

    printf("Input integer for matrix dimension size: "); 
    scanf("%d", &N); 

    printf("Input number of threads in a block: "); 
    scanf("%d", &T); 

    printf("Input number of blocks in a grid: "); 
    scanf("%d", &B); 

    int size = N * N * sizeof(int); 

    int *a, *b, *c; 

    a = (int*)malloc(size); 
    b = (int*)malloc(size); 
    c = (int*)malloc(size); 

    for (int i = 0; i < N; i++) { 
     for (int j = 0; j < N; j++) { 
      a[i*N+j] = j + i*N; 
      b[i*N+j] = j + i*N; 
      c[i*N+j] = j + i*N; 
     } 
    } 

    int *dev_a, *dev_b, *dev_c; 

    cudaMalloc((void**)&dev_a, size); 
    cudaMalloc((void**)&dev_b, size); 
    cudaMalloc((void**)&dev_c, size); 

    cudaMemcpy(dev_a, a, size, cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_b, b, size, cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_c, c, size, cudaMemcpyHostToDevice); 

    dim3 dimGrid(B, B); 
    dim3 dimThreads(T, T); 
    MatMultiply<<<B, T>>>(dev_a,dev_b,dev_c, N); 

    cudaMemcpy(c, dev_c, size, cudaMemcpyDeviceToHost); 


    for (int i = 0; i < N; i++) { 
     for (int j = 0; j < N; j++) { 
      printf("%d\t", b[i*N + j]); 
     } 
     printf("\n"); 
    } 

    free(a); 
    free(b); 
    free(c); 

    cudaFree(dev_a); 
    cudaFree(dev_b); 
    cudaFree(dev_c); 

    return 0; 
} 

再次感谢。

回答

2

您在内核调用中没有使用dimGriddimThreads变量。相反,您只需启动一维线程块的一维网格。

除此之外,你没有检查任何错误。

+1

此外,最后,您打印出矩阵'b',这是您的输入矩阵之一。您可能想要打印出'c'。 – 2013-04-22 22:59:52

+0

谢谢。我不知道我是如何错过的。现在一切似乎都在起作用。 – Chris 2013-04-23 04:23:12

2

所以,这里的问题似乎是在建立线程和块和使用threadIdxblockDimgridDim

注意:在标签实用的解决方案

threadIdx切实解决这个特殊的问题是因为它的名字说的线程的ID。这意味着该值,或更preciselly它threadIdx.xthreadIdx.y部件将从指定线程计数的0值,或每块的值而线程被存储在blockDim.x去blockDim.y。例如,一个呼叫

someKernel<<<1,32>>>(....); 

将导致threadIdx.x从0到31和threadIdx.y值会不会在所有被迭代(我相信它将永远是0)。

不过,若你定义一个特定的CUDA结构为dim3并调用它threadsPerBlock,然后用它作为第二个参数是这样的:

dim3 threadsPerBlock(32, 32); 

someKernel<<<1,threadsPerBlock>>>(....); 

,那么你会得到两个threadIdx.xthreadIdx.y从0到31在内核执行中获取它们的各种组合。

请注意,您被限制为每个启动块的最大线程数。这个数字对于不同的显卡来说是不同的,或者更确切地说是它们支持的计算能力。在this link末尾的表格中查找这些数字因此,计算能力2.x和更高版本支持每块最多1024个线程,而早期版本支持512.还要注意,这意味着启动时每个块最多可以有32x32个线程在2个维度。

但是,如果你需要更多的东西呢?那么儿子,那么你启动更多的块!您也可以在1维或2维中启动块。例如

dim3 threadsPerBlock(32, 32); 
dim3 blocksPerGrid (256, 265); 

someKernel <<<blocksPerGrid,threadsPerBlock>>>(...); 

网格的大小存储在gridDim结构和在这种情况下,两个gridDim.xgridDim.y将是256,使得blockIdx.xblockIdx.y变量从0到255

实用的解决方案:

现在我们知道了,让我们看看你的代码。在你的代码,如果你例如设置牛逼为32和是256,你将有效地得到这样的:

threadIdx.x would go from 0 to 31 
threadIdx.y would go from 0 to 0 
blockIdx.x would go from 0 to 255 
blockIdx.y would go from 0 to 0 
blockDim.x would be 32 
blockDim.y would be 1 
gridDim.x would be 256 
gridDim.y would be 1 

现在让我们看看你的变量是如何应对这个...

row would go from 0 to 0 
col would go from 0 to 1023 

所以,这大概不是你想要的。你希望你的行和列都从0到N-1对不对?那么,这是你如何做到这一点:

int row = threadIdx.x + blockIdx.x * blockDim.x; 
int col = threadIdx.y + blockIdx.y * blockDim.y; 

此外,请确保您有足够的线程来覆盖矩阵的尺寸。这是确保你设置* threadsPerBlock * blocksPerGrid *大于您的N。这通常是最好的做法是这样的:

threads = 32 
dim3 threadsPerBlock (threads, threads); 
blocks = (N/threads) + 1; 
dim3 blocksPerGrid (blocks, blocks); 

“但如果我让它大于N,那么我可能有一些线程我不需要” - 说你 - “我不希望他们做的工作!”明智的你是先生,这样说。您可以通过简单的解决这个问题,如果条款中,你会附上你的计算,像这样:

if (row < N && col < N) 
{ 
    // your add... err... code here 
} 

希望有所帮助。享受CUDA;)