はCPU

2016-04-25 9 views
1
#include <iostream> 
#include <assert.h> 
#include <sys/time.h> 

#define BLOCK_SIZE 32 // CUDA block size 

__device__ inline int getValFromMatrix(int* matrix, int row, int col,int matSize) { 
    if (row<matSize && col<matSize) {return matrix[row*matSize + col];} 
    return 0; 
} 

__device__ inline int getValFromVector(int* vector, int row, int matSize) { 
    if (row<matSize) {return vector[row];} 
    return 0; 
} 

__global__ void matVecMultCUDAKernel(int* aOnGPU, int* bOnGPU, int* cOnGPU, int matSize) { 
    __shared__ int aRowShared[BLOCK_SIZE]; 
    __shared__ int bShared[BLOCK_SIZE]; 
    __shared__ int myRow; 
    __shared__ double rowSum; 

    int myIndexInBlock = threadIdx.x; 
    myRow = blockIdx.x; 
    rowSum = 0; 

    for (int m = 0; m < (matSize/BLOCK_SIZE + 1);m++) { 
     aRowShared[myIndexInBlock] = getValFromMatrix(aOnGPU,myRow,m*BLOCK_SIZE+myIndexInBlock,matSize); 
     bShared[myIndexInBlock] = getValFromVector(bOnGPU,m*BLOCK_SIZE+myIndexInBlock,matSize); 

     __syncthreads(); // Sync threads to make sure all fields have been written by all threads in the block to cShared and xShared 

     if (myIndexInBlock==0) { 
      for (int k=0;k<BLOCK_SIZE;k++) { 
       rowSum += aRowShared[k] * bShared[k]; 
      } 
     } 
    } 

    if (myIndexInBlock==0) {cOnGPU[myRow] = rowSum;} 
} 

static inline void cudaCheckReturn(cudaError_t result) { 
    if (result != cudaSuccess) { 
     std::cerr <<"CUDA Runtime Error: " << cudaGetErrorString(result) << std::endl; 
     assert(result == cudaSuccess); 
    } 
} 

static void matVecMultCUDA(int* aOnGPU,int* bOnGPU, int* cOnGPU, int* c, int sizeOfc, int matSize) { 
    matVecMultCUDAKernel<<<matSize,BLOCK_SIZE>>>(aOnGPU,bOnGPU,cOnGPU,matSize); // Launch 1 block per row 

    cudaCheckReturn(cudaMemcpy(c,cOnGPU,sizeOfc,cudaMemcpyDeviceToHost)); 
} 


static void matVecMult(int** A,int* b, int* c, int matSize) { 
    // Sequential implementation: 
    for (int i=0;i<matSize;i++) { 
     c[i]=0; 
     for (int j=0;j<matSize;j++) { 
      c[i]+=(A[i][j] * b[j]); 
     } 
    } 
} 

int main() { 
    int matSize = 1000; 

    int** A,* b,* c; 
    int* aOnGPU,* bOnGPU,* cOnGPU; 

    A = new int*[matSize]; 
    for (int i = 0; i < matSize;i++) {A[i] = new int[matSize]();} 
    b = new int[matSize](); 
    c = new int[matSize](); 

    int aSizeOnGPU = matSize * matSize * sizeof(int), bcSizeOnGPU = matSize * sizeof(int); 

    cudaCheckReturn(cudaMalloc(&aOnGPU,aSizeOnGPU)); // cudaMallocPitch? 
    cudaCheckReturn(cudaMalloc(&bOnGPU,bcSizeOnGPU)); 
    cudaCheckReturn(cudaMalloc(&cOnGPU,bcSizeOnGPU)); 

    srand(time(NULL)); 

    for (int i=0;i<matSize;i++) { 
     b[i] = rand()%100; 
     for (int j=0;j<matSize;j++) { 
      A[i][j] = rand()%100; 
     } 
    } 

    for (int i=0;i<matSize;i++) {cudaCheckReturn(cudaMemcpy((aOnGPU+i*matSize),A[i],bcSizeOnGPU,cudaMemcpyHostToDevice));} 
    cudaCheckReturn(cudaMemcpy(bOnGPU,b,bcSizeOnGPU,cudaMemcpyHostToDevice)); 

    int iters=1; 
    timeval start,end; 

    // Sequential run: 
    gettimeofday(&start,NULL); 
    for (int i=0;i<iters;i++) {matVecMult(A,b,c,matSize);} 
    gettimeofday(&end,NULL); 
    std::cout << (end.tv_sec*1000000 + end.tv_usec) - (start.tv_sec*1000000 + start.tv_usec) << std::endl; 

    // CUDA run: 
    gettimeofday(&start,NULL); 
    for (int i=0;i<iters;i++) {matVecMultCUDA(aOnGPU,bOnGPU,cOnGPU,c,bcSizeOnGPU,matSize);} 
    gettimeofday(&end,NULL); 
    std::cout << (end.tv_sec*1000000 + end.tv_usec) - (start.tv_sec*1000000 + start.tv_usec) << std::endl; 

    cudaCheckReturn(cudaFree(aOnGPU)); 
    cudaCheckReturn(cudaFree(bOnGPU)); 
    cudaCheckReturn(cudaFree(cOnGPU)); 


    for (int i = 0; i < matSize; ++i) { 
     delete[] A[i]; 
    } 
    delete[] A; 
    delete[] b; 
    delete[] c; 
} 

よりもCUDAに速く行くための行列*ベクトル乗算を取得できません与える:はCPU

267171 
580253 

を私は行列の乗算を行う方法で、http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#shared-memoryのガイドに従ってきました。私は行列(A)とベクトル(B)の両方に共有メモリを使用しましたが、どの行列サイズ(100 * 100-20000 * 20000)またはブロックサイズ(32-1024)を選択しても、順次実装は常に速度面でのCUDAの実装は、約2倍の速さです。

私は行列*ベクトルの乗算を使用しているので、共有された配列とブロックは少し異なって扱われます。私は行列の一部の上に2Dブロックの代わりに行列の行ごとに1つのブロックを使用しています。

私の実装は間違っているのですか、単にCUDAがCPUより高速ではありませんか?

答えて

3

最初の項目:CPU上にないcuda実装の境界をチェックします。ブランチングはGPU上では本当に高価です。

2番目:cudaemcpyをcudaパフォーマンスでカウントします。結果をcpuに戻さなくてはならないうちに、乗算を1回だけ実行することは非常に珍しいことです。 通常は(CGなど)、コピーする前にGPUで数百回の乗算を実行します。

3番目:教育目的以外では実装しないでください(CUDAリリースのすべてに同梱されているCUBLASのような)ベンダーライブラリを使用するのはやめてください。