0

让我首先为这篇文章道歉。我知道有几篇帖子问我在这里提出的相同问题,但我已经尝试了给出的解决方案,但我仍然没有得到正确的 CUDA 矩阵乘法结果。

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

这可能是我的 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;
}

再次感谢。

4

2 回答 2

2

因此,这里的问题似乎在于设置线程和块以及使用threadIdxblockDimgridDim

注意:此特定问题的实用解决方案标签实用解决方案

threadIdx 顾名思义就是线程的 ID。这意味着这个值,或者更准确地说,它的threadIdx.xthreadIdx.y组件将从 0 的值变为指定的线程数,或者更确切地说是存储在blockDim.xblockDim.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 在内核执行中获得它们的各种组合。

请注意,您被限制为每个启动的块的最大线程数。对于不同的显卡,或者更准确地说,它们支持的计算能力,这个数字是不同的。在此链接末尾的表中查找这些数字因此,计算能力 2.x 及更高版本支持每个块最多 1024 个线程,而早期版本支持 512 个。另请注意,这意味着每个块最多 32x32 个线程二维发射。

但是如果你需要更多呢?好吧,儿子,那你发射更多的块!您还可以在 1 或 2 维中启动块。例如

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

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

网格的大小存储在gridDim结构中,在这种情况下,gridDim.xgridDim.y都是 256,使得blockIdx.xblockIdx.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

所以,这可能不是你真正想要的。您希望 row 和 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 ;)

于 2013-04-23T00:38:55.637 回答
2

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

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

于 2013-04-22T22:35:01.557 回答