简单的添加例子:共享内存版本的减少执行速度比全局内存慢

时间:2011-12-13 09:43:56

标签: cuda parallel-processing nvidia

我已经实现了两个版本的add。两者中的加法概念完全相同。唯一的区别是在一个代码中(下面的第一个代码)我使用全局内存,而对于第二个代码我使用共享内存。正如在几个地方提到的,共享内存版本应该更快但是对于我的情况,全局内存版本更快。 请告诉我出错的地方。注意:我有一个cc 2.1的cc。因此,对于共享内存,我有32个银行。由于我在示例中仅使用16个整数,因此我的代码不应存在银行冲突。 如果这是正确的,请告诉我

全球版

#include<stdio.h>
__global__ void reductionGlobal(int* in, int sizeArray, int offset){

    int tid = blockIdx.x * blockDim.x + threadIdx.x;

    if(tid < sizeArray ){
        if(tid % (offset * 2 ) == 0){
            in[tid] += in[tid+offset];
        }

    }

}
int main(){
    int size = 16; // size of present input array. Changes after every loop iteration
    int cidata[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

    int* gidata;
    cudaMalloc((void**)&gidata, size* sizeof(int));
    cudaMemcpy(gidata,cidata, size * sizeof(int), cudaMemcpyHostToDevice);
    int offset = 1; 
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start, 0);
    while(offset < size){
        //use kernel launches to synchronize between different block. syncthreads() will not work
        reductionGlobal<<<4,4>>>(gidata,size,offset);
        offset *=2;

    }
    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    float elapsedTime; 
    cudaEventElapsedTime(&elapsedTime , start, stop);
    printf("time is %f ms", elapsedTime);
    int* output = (int*)malloc( size * sizeof(int));
    cudaMemcpy(output, gidata, size * sizeof(int), cudaMemcpyDeviceToHost);
    printf("The sum of the array using only global memory is %d\n",output[0]);
    getchar();
    return 0;
}

共享内存版本:

#include<stdio.h>

__global__ void computeAddShared(int *in , int *out, int sizeInput){
    extern __shared__ float temp[];

    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    int ltid = threadIdx.x;
    temp[ltid] = 0;
    while(tid < sizeInput){
        temp[ltid] += in[tid];
        tid+=gridDim.x * blockDim.x; // to handle array of any size
    }
    __syncthreads();
    int offset = 1;
    while(offset < blockDim.x){
        if(ltid % (offset * 2) == 0){
            temp[ltid] = temp[ltid] + temp[ltid + offset];
        }
        __syncthreads();
        offset*=2;
    }
    if(ltid == 0){
        out[blockIdx.x] = temp[0];
    }

}

int main(){

    int size = 16; // size of present input array. Changes after every loop iteration
    int cidata[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

    int* gidata;
    int* godata;
    cudaMalloc((void**)&gidata, size* sizeof(int));
    cudaMemcpy(gidata,cidata, size * sizeof(int), cudaMemcpyHostToDevice);
    int TPB  = 4;
    int blocks = 10; //to get things kicked off
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start, 0);
    while(blocks != 1 ){
        if(size < TPB){
            TPB  = size; // size is 2^sth
        }
        blocks  = (size+ TPB -1 ) / TPB;
        cudaMalloc((void**)&godata, blocks * sizeof(int));
        computeAddShared<<<blocks, TPB,TPB>>>(gidata, godata,size);
        cudaFree(gidata);
        gidata = godata;
        size = blocks;
    }

    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    float elapsedTime; 
    cudaEventElapsedTime(&elapsedTime , start, stop);
    printf("time is %f ms", elapsedTime);
    int *output = (int*)malloc(sizeof(int));
    cudaMemcpy(output, gidata, sizeof(int), cudaMemcpyDeviceToHost);
    //Cant free either earlier as both point to same location
    cudaFree(godata);
    cudaFree(gidata);
    printf("The sum of the array is %d\n", output[0]);
    getchar();
    return 0;
}

1 个答案:

答案 0 :(得分:2)

这里有很多错误。首先,一些一般性评论:

  • 你正在减少16个数字,这是一个荒谬的 输入尺寸小。 CUDA在主机上都有很多固定的开销 和设备方面。您为设备提供的工作量 是如此之小,你所测量的只是那些开销,而不是GPU 执行时间处理时间。你看到的两个代码之间的区别 可能只是因为增加了设置开销 共享内存版本。当然与代码本身无关。如果要测量代码的实际性能,则为该代码提供的工作量必须足够大,以确保执行时间远远大于设置时间。请放心,即使是在小型GPU上,你也只有5个数量级以上的工作量。
  • 您提到了银行冲突,但这是您正在使用的架构上的一个稻草人。与旧硬件相比,Fermi具有完全不同的共享内存布局,并且只有相对较小的银行冲突问题。当然,在这种情况下无需担心。

至于实际的减少代码本身:

  • 如果你无法想出减少输入数组的方法 在单个内核启动中每个线程的一个部分总和,然后你真的没有想到 关于这个问题就够了。你目前的方法在“全球” 而“共享”版本的效率非常低。并行缩减是一个已解决的问题,CUDA SDK附带了关于优化和GPU降低性能的优秀白皮书。你应该在做其他事之前阅读它。
  • 一旦你到达每个线程有一个部分总和的点, 你想要执行共享内存缩减每个块来拥有 减少每块发出一个部分和。这只需要 两个内核启动计算完全减少。
  • 您的“共享”版本有缓冲区溢出,应该导致 运行时错误。启动时指定的动态共享内存大小 时间是字节,而不是单词。如果您的代码有错误检查,那么您 已经找到了这个。费米拥有出色的共享内存 保护,如果你尝试写,它会产生运行时故障 在静态或动态分配之外。