【问题标题】:CUDA performance doubtsCUDA 性能疑点
【发布时间】:2011-07-17 11:17:17
【问题描述】:

由于我没有得到 CUDA 论坛的回复,所以在这里尝试一下:

在 CUDA 中做了一些程序后,我现在开始获得它们的有效带宽。但是我有一些奇怪的结果,例如在下面的代码中,我可以对向量中的所有元素求和(无论维度如何),展开代码和“正常”代码的带宽似乎具有相同的中值结果(约 3000 Gb/s) 我不知道我是否做错了什么(AFAIK 程序运行良好),但从我目前阅读的内容来看,Unroll 代码应该有更高的带宽。

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <math.h>
#define elements 1000
#define blocksize 16    


__global__ void vecsumkernel(float*input, float*output,int nelements){



    __shared__ float psum[blocksize];
    int tid=threadIdx.x;

    if(tid + blockDim.x * blockIdx.x < nelements)
    psum[tid]=input[tid+blockDim.x*blockIdx.x];
    else
    psum[tid]=0.0f;
    __syncthreads();

    //WITHOUT UNROLL

    int stride;     
    for(stride=blockDim.x/2;stride>0;stride>>=1){
            if(tid<stride)
                    psum[tid]+=psum[tid+stride];
    __syncthreads();
    }
    if(tid==0)
            output[blockIdx.x]=psum[0];


    //WITH UNROLL
 /*
    if(blocksize>=512 && tid<256) psum[tid]+=psum[tid+256];__syncthreads();
    if(blocksize>=256 && tid<128) psum[tid]+=psum[tid+128];__syncthreads();
    if(blocksize>=128 && tid<64) psum[tid]+=psum[tid+64];__syncthreads();


    if (tid < 32) {
            if (blocksize >= 64) psum[tid] += psum[tid + 32];
            if (blocksize >= 32) psum[tid] += psum[tid + 16];
            if (blocksize >= 16) psum[tid] += psum[tid + 8];
            if (blocksize >=  8) psum[tid] += psum[tid + 4];
            if (blocksize >=  4) psum[tid] += psum[tid + 2];
            if (blocksize >=  2) psum[tid] += psum[tid + 1];
    }*/

    if(tid==0)
            output[blockIdx.x]=psum[0];



}

void vecsumv2(float*input, float*output, int nelements){
    dim3 dimBlock(blocksize,1,1);
    int i;

    for(i=((int)ceil((double)(nelements)/(double)blocksize))*blocksize;i>1;i(int)ceil((double)i/(double)blocksize)){
            dim3 dimGrid((int)ceil((double)i/(double)blocksize),1,1);
            printf("\ni=%d\ndimgrid=%u\n ",i,dimGrid.x);

            vecsumkernel<<<dimGrid,dimBlock>>>(i==((int)ceil((double)(nelements)/(double)blocksize))*blocksize ?input:output,output,i==((int)ceil((double)(nelements)/(double)blocksize))*blocksize ? elements:i);
    }

 }

 void printVec(float*vec,int dim){
    printf("\n{");
    for(int i=0;i<dim;i++)
            printf("%f ",vec[i]);
    printf("}\n");
 }

 int main(){
    cudaEvent_t evstart, evstop;
    cudaEventCreate(&evstart);
    cudaEventCreate(&evstop);


    float*input=(float*)malloc(sizeof(float)*(elements));
    for(int i=0;i<elements;i++)
            input[i]=(float) i;


    float*output=(float*)malloc(sizeof(float)*elements);



    float *input_d,*output_d;

    cudaMalloc((void**)&input_d,elements*sizeof(float));

    cudaMalloc((void**)&output_d,elements*sizeof(float));



    cudaMemcpy(input_d,input,elements*sizeof(float),cudaMemcpyHostToDevice);


    cudaEventRecord(evstart,0);

    vecsumv2(input_d,output_d,elements);

    cudaEventRecord(evstop,0);
    cudaEventSynchronize(evstop);
    float time;
    cudaEventElapsedTime(&time,evstart,evstop);
    printf("\ntempo gasto:%f\n",time);
    float Bandwidth=((1000*4*2)/10^9)/time;
    printf("\n Bandwidth:%f Gb/s\n",Bandwidth);


    cudaMemcpy(output,output_d,elements*sizeof(float),cudaMemcpyDeviceToHost);


    cudaFree(input_d);
    cudaFree(output_d);
    printf("soma do vector");
    printVec(output,4);



   }

【问题讨论】:

    标签: performance cuda bandwidth gpu


    【解决方案1】:

    您展开的代码中有很多分支。我数了十个额外的分支。通常,在 GPU 上的 warp 内进行分支是昂贵的,因为 warp 中的所有线程最终都在等待分支(分歧)。

    有关经线发散的更多信息,请参见此处:

    http://forums.nvidia.com/index.php?showtopic=74842

    您是否尝试过使用分析器查看发生了什么?

    【讨论】:

    • 我从 Nvidia reduction.pdf 中获取了展开代码。我不知道分析器的目的是什么(我一个月前才开始使用 cuda,计算机工程/编程不是我的领域)
    • 另外,你怎么数10个额外的分支?
    • 我在数“如果”。你不是每次都做十个,但是有很多可能的代码路径,每个分支都可能导致扭曲发散。
    【解决方案2】:

    3000 Gb/s 没有意义。 PCIe的最大总线速度为每个方向8Gb/s。

    查看本文Parallel Prefix Sum,了解如何加快实施速度。 还要考虑thrust 库已经在Reductions 模块中实现了这个

    【讨论】:

    • 我虽然这个值真的很高,但我用我在 CUDA 编程指南上看到的公式找到了带宽:float Bandwidth=((1000*4*2)/10^9)/time ; // BW=(Bytesread+Byteswrite)/10^9/time
    【解决方案3】:

    您的未展开代码无效。对于stride&lt;32,相同warp 的一些线程进入for 循环,而其他线程则没有。因此,经线的一些(但不是全部)线程命中__syncthreads()。 CUDA 规范说,当这种情况发生时,行为是未定义的。

    可能会发生 warp 不同步,并且一些线程已经开始加载下一个数据块,在 __syncthreads() 的下一个实例上停止,而之前的线程仍停留在您之前的循环中。

    我不确定在这种特殊情况下您是否会遇到这种情况。

    【讨论】:

    • 与展开的代码一样,我用 Nvidia reduction.pdf 检查我的代码,我认为代码是相同的 =/。我相信你说的是真的
    • 查看代码我不明白为什么有些线程不进入for循环。我看到只有一些线程(tid
    • 我明白了你所说的,如果步幅是前 16,tid 从 0 到 16 输入 for,而其他没有(我想这就是你的意思)。我认为分支冬青发生在 if 条件下,但我不知道这是否有问题。我所知道的是它的效率应该较低
    【解决方案4】:

    我看到您正在内核中进行归约和。这是 NVIDIA 提供的一个很好的 presentation,用于优化 GPU 上的减少。您会注意到,在本指南中,提供 2 GB/s 吞吐量的相同代码已优化为 63 GB/s

    【讨论】:

      猜你喜欢
      • 2013-01-24
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      • 2016-05-22
      • 1970-01-01
      • 2016-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多