【问题标题】:Matrix Multiplication with CUDA, long execution timeCUDA矩阵乘法,执行时间长
【发布时间】:2017-01-26 00:26:59
【问题描述】:

我是 CUDA 的新手,我一直试图找出我在这里做错了什么。 CUDA 所花费的时间不仅仅是使用 CPU 来乘以矩阵。如果我做错了什么,请告诉我。 这是我的代码:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <assert.h>
#include <time.h>
#define size 100   // Matrix size
#define cols size   // Matrix width
#define rows size   // Matrix height

void checkCUDAError(const char *msg)
{
    cudaError_t err = cudaGetLastError();
    if( cudaSuccess != err) 
    {
        fprintf(stderr, "Cuda error: %s: %s.\n", msg, cudaGetErrorString( err) );
        exit(EXIT_FAILURE);
    }                         
}
__global__ void matrixMul( int *A, int *B, int *C)
{   
    int bx = blockIdx.x; // Block index
    int tx = threadIdx.x; // Thread index
    int ts = blockDim.x; // number of threads   
    // Declaration of the shared memory C element
    extern __shared__ int c_element_sum[];
    c_element_sum[tx] = A[tx+((bx/ts)*ts)] * B[(bx%ts)+(tx*ts)];

    //Block until all threads in the block have written their data to shared mem
    __syncthreads();

    int sum;
    for(int i=0; i<ts; i++){
        if(i==0){
            sum=c_element_sum[i];
        }
        else{
            sum+=c_element_sum[i];
        }
    }
    C[bx] = sum;

}


/////////////////////////////////////////////////////////
// Program main
/////////////////////////////////////////////////////////

int main(int argc, char** argv)
{
   //create timer.
   clock_t t1, t2;

   //start timer
   t1=clock();

   //allocate host memory for matrices
   unsigned int size_A = cols * rows;
   unsigned int mem_size_A = sizeof(int) * size_A;
   int* mA = (int*) malloc(mem_size_A);

   unsigned int size_B = cols * rows;
   unsigned int mem_size_B = sizeof(int) * size_B;
   int* mB = (int*) malloc(mem_size_B);

   unsigned int size_C = cols * rows;
   unsigned int mem_size_C = sizeof(int) * size_C;
   int* mC = (int*) malloc(mem_size_C);

   //initialize host memory
   for (int i = 0; i < size_A; ++i){
       mA[i] = 1;
       mB[i] = 1;
       mC[i] = 0;
   }

   // allocate device memory
   int* d_mA;
   int* d_mB;
   int* d_mC;
   cudaMalloc((void**) &d_mA, mem_size_A);
   cudaMalloc((void**) &d_mB, mem_size_B);
   cudaMalloc((void**) &d_mC, mem_size_C);

   //copy host memory to device (A and B)
   cudaMemcpy(d_mA, mA, mem_size_A, cudaMemcpyHostToDevice);
   cudaMemcpy(d_mB, mB, mem_size_B, cudaMemcpyHostToDevice);
   cudaMemcpy(d_mC, mC, mem_size_C, cudaMemcpyHostToDevice);

   // setup execution parameters
   int numThreadsPerBlock = cols;
   int numBlocks = (cols * rows);
   int sharedMemSize = numThreadsPerBlock * sizeof(int);

   dim3 dimGrid(numBlocks);
   dim3 dimBlock(numThreadsPerBlock);

   // execute the kernel
   matrixMul <<< dimGrid, dimBlock, sharedMemSize >>>(d_mA, d_mB, d_mC);

   //Block until device has completed
   cudaThreadSynchronize();

   // check if kernel execution generated an error
   // Check for any CUDA errors
   checkCUDAError("kernel invocation");

   //copy result from device to host
   cudaMemcpy(mC, d_mC, mem_size_C, cudaMemcpyDeviceToHost);

   // Check for any CUDA errors
   checkCUDAError("memcpy");

   //stop timer
   t2 = clock();

   //check results
   for (int i = 0; i < size_C; ++i){
       assert(mC[i] == cols);
   }

   //clean up memory
   free(mA);
   free(mB);
   free(mC);
   cudaFree(d_mA);
   cudaFree(d_mB);
   cudaFree(d_mC);

   printf("WITH CUDA - clocks: %d \n\n", t2-t1);

   //////////////////////////////
   ///////// CPU ONLY //////////
   /////////////////////////////

   //create timer.
   clock_t cpu_t1, cpu_t2;

   //start timer
   cpu_t1=clock();

   //allocate host memory for matrices
   unsigned int cpu_size_A = cols * rows;
   unsigned int cpu_mem_size_A = sizeof(int) * cpu_size_A;
   int* cpu_mA = (int*) malloc(cpu_mem_size_A);

   unsigned int cpu_size_B = cols * rows;
   unsigned int cpu_mem_size_B = sizeof(int) * cpu_size_B;
   int* cpu_mB = (int*) malloc(cpu_mem_size_B);

   unsigned int cpu_size_C = cols * rows;
   unsigned int cpu_mem_size_C = sizeof(int) * cpu_size_C;
   int* cpu_mC = (int*) malloc(cpu_mem_size_C);

   //initialize host memory
   for (int i = 0; i < cpu_size_A; ++i){
       cpu_mA[i] = 1;
       cpu_mB[i] = 1;
       cpu_mC[i] = 0;
   }

   int ts = cols;
   for(int bx=0; bx<(cols*rows);bx++){
       int sum = 0;
       for(int tx=0; tx<cols; tx++){
          sum += cpu_mA[tx+((bx/ts)*ts)] * cpu_mB[(bx%ts)+(tx*ts)];
       }
       cpu_mC[bx]=sum;
   }

   //stop timer
   cpu_t2 = clock();

   //check results
   for (int i = 0; i < cpu_size_C; ++i){
       assert(cpu_mC[i] == cols);
   }

   //clean up memory
   free(cpu_mA);
   free(cpu_mB);
   free(cpu_mC);

   printf("CPU ONLY - clocks: %d \n\n", cpu_t2-cpu_t1);

   return 0;
}

【问题讨论】:

  • 您应该在调用内核后立即测量内存,否则,您将考虑复制和分配内存所花费的时间,这非常慢。
  • 您有什么理由编写自己的矩阵乘法例程吗? IIRC CUDA 有这个内置函数供你调用。
  • @fontanini:谢谢,从现在开始我会牢记这一点。
  • @Mike Bantegui:我写这个作为练习,试图在我休息的时候自学 CUDA,但是感谢你的建议,我不知道有一个内置函数,它会来在开始处理更复杂的事情时派上用场。

标签: c++ c cuda


【解决方案1】:

根据您的程序,这是预期的。您的计时器看起来像是为程序的整个执行计时,其中包括复制到设备、计算时间以及将结果复制回来。鉴于您为程序提供的工作量相当小(100x100 矩阵),内存副本的开销远远超过使用内核进行计算时获得的任何计算收益。您的内核本身也不是最有效的实现。

我不认为你做错了什么错误,只是你没有为 GPU 提供足够大的工作块,你可能会进一步优化你的内核。请注意,简单地扩大块的大小可能不会显着提高相对于 CPU 的性能,因为您还将扩大内存管理时间。虽然在 CUDA 上编写程序的第一个实现相对简单,但要从中获得良好的性能要困难得多。使用 CUDA 最有效的方法是拥有高比率的计算与内存事务。例如,拥有一个由多个计算密集型内核组成的管道来连续对一块数据进行操作,只需要在开始和结束时进行主机-设备复制。

如果这只是一个帮助您学习为 CUDA 编写代码的程序,那么这是一个很好的步骤,深入了解如何优化矩阵乘法内核将在许多其他情况下为您提供很好的帮助。如果您正在编写此内核以用于生产软件,我建议您使用高度优化的线性代数库 CUBLAS:http://developer.nvidia.com/cublas(或其他已经为您完成了艰苦工作的库)。

【讨论】:

  • 感谢您的解释,非常有帮助。我写这个程序只是为了练习。我正在尝试自学CUDA和更多的C,因为大学在教我任何有价值的东西方面做得很糟糕。我想我最好自己去学习一些东西,否则我毕业时除了一些半途而废的 Java 和一张我将在未来几年支付的论文。
  • 我一直认为College的真正目的是教你如何自学,所以在这方面,似乎还可以。
猜你喜欢
  • 2012-05-06
  • 2011-09-07
  • 2012-12-09
  • 1970-01-01
  • 2012-02-07
  • 2011-04-21
  • 2012-12-13
  • 1970-01-01
  • 2019-08-09
相关资源
最近更新 更多