如何推广方阵乘法来处理任意维度

时间:2015-06-19 09:14:00

标签: c cuda parallel-processing matrix-multiplication

我编写了这个程序,并且在使用内核调用行中的dim3变量来理解如何使用多个块时遇到了一些麻烦。当我进行1000 * 1000矩阵乘法时,此代码工作正常,但对于较低尺寸(如100 * 100,200 * 200)没有得到正确答案。

#include <stdio.h>
#include <cuda.h>
#define width 1000

__global__ void kernel(int *a,int *b,int *c)
{

        int tx = threadIdx.x + blockIdx.x*blockDim.x;
        int ty = threadIdx.y + blockIdx.y*blockDim.y;

        int sum=0,k;

        for(k=0;k<(width);++k)
        {
                sum += a[ty*width +k]*b[k*width + tx];
        }
        c[ty*width + tx] = sum;
}


int main()
{
        int a[width*width],c[width*width],b[width*width];
        int *dev_a,*dev_b,*dev_c;
        int i,count=0;
        int size = (width*width)*sizeof(int);

         for(i=0;i<(width*width);i++)
        {
                a[i] = 1;
                b[i] = 1;
        }

        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);

        dim3 dimBlock(20,20);
        dim3 blockID(50,50);

        kernel<<<blockID,dimBlock>>>(dev_a,dev_b,dev_c);

        cudaMemcpy(&c,dev_c,size,cudaMemcpyDeviceToHost);

        for(i=0;i<(width*width);i++)
        {
                count++;
                if(count == (width+1))
                {
                        count = 1;
                        printf("\n");
                }

                printf("%d ",c[i]);
        }
        printf("\n");
        return 0;
}

1 个答案:

答案 0 :(得分:1)

此代码适用于非常具体的维度,但不适用于其他维度。

width完全等于块尺寸(线程数 - 您显示的代码中的20)和网格尺寸(块数 - 50 in)的乘积时,它将适用于方阵乘法你已经展示的代码。)

因此当width为20 * 50(1000)时,它将如图所示工作。但是,如果我将width更改为其他值(例如800)并且不进行其他更改,则代码将无效。但是,在800的情况下,我可以通过将网格尺寸从50更改为40,然后width = 800 = 20 * 40来使代码工作。

但是如果我需要乘以width 799的两个矩阵怎么办?我无法想出一个与width完全匹配的网格和块维度的产品。

这是CUDA编程中一个相当标准的问题 - 我无法提出方便的块和网格尺寸来完全匹配我的工作(即数据)大小,如果我发起太多(线程/块)事情看起来不像工作。

要解决这个问题,我们必须做两件事:

  1. 确保至少启动足够但可能超过足够的线程(线程块)来覆盖整个数据集
  2. 在内核中添加条件代码,以便只有与有效数据相对应的线程才能正常工作。
  3. 为了解决上面的第1项,我们将网格尺寸计算修改为:

        dim3 dimBlock(16,16);
        dim3 blockID((width+dimBlock.x-1)/dimBlock.x,(width+dimBlock.y-1)/dimBlock.y);
    

    为了解决上面的第2项,我们修改了我们的内核代码,以调整线程是否与有效数据相对应的线程行为:

    __global__ void kernel(int *a,int *b,int *c, int mwidth)
    {
    
            int tx = threadIdx.x + blockIdx.x*blockDim.x;
            int ty = threadIdx.y + blockIdx.y*blockDim.y;
            if ((tx<mwidth)&&(ty<mwidth)){
    
              int sum=0,k;
    
              for(k=0;k<(mwidth);++k)
              {
                    sum += a[ty*mwidth +k]*b[k*mwidth + tx];
              }
              c[ty*mwidth + tx] = sum;}
    }
    

    由于我们使用新参数修改了内核,因此我们必须在调用时传递该参数:

        kernel<<<blockID,dimBlock>>>(dev_a,dev_b,dev_c, width);
    

    这应该是逻辑扩展您显示的代码以处理“任意”维度所需的内容。我建议您在遇到CUDA代码时遇到问题时添加proper cuda error checking