CUDA矩阵乘法的结果不正确

时间:2013-04-22 22:23:16

标签: matrix cuda multiplication

让我先为这篇文章道歉。我知道有几个帖子问我同样的问题,但是我已经尝试了给出的解决方案,但我仍然没有得到正确的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 个答案:

答案 0 :(得分:2)

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

除此之外,您没有检查是否有任何错误。

答案 1 :(得分:2)

所以,这里的问题似乎是设置线程和块并使用 threadIdx blockDim gridDim

注意:标签实用解决方案

中对此特定问题的实际解决方案

threadIdx 就像名称所示的线程ID一样。这意味着这个值,或者说更精确的是它的 threadIdx.x threadIdx.y 组件将从0到指定的线程数,或者更确切地说是每个块的线程< / strong>存储在 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.x threadIdx.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.x gridDim.y 都是256,使 blockIdx.x blockIdx.y 变量从0到255。

实用解决方案:

现在我们知道了,让我们来看看你的代码。在您的代码中,如果您将 T 设置为32而 B 设置为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

所以,这可能不是你想要的。你想要你的行和col从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子句解决这个问题,你可以在其中包含你的计算,如下所示:

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

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