优化Cuda内核时间执行

时间:2016-07-15 14:34:23

标签: time cuda gpu cpu

我是一名学习Cuda的学生,我想优化我的核心功能的执行时间。结果,我实现了一个计算两张图片之间差异的短程序。所以我比较了C中的经典CPU执行和Cuda C中的GPU执行之间的执行时间。

在这里,您可以找到我正在谈论的代码:

int *imgresult_data = (int *) malloc(width*height*sizeof(int));
int size = width*height;

switch(computing_type)
{

    case GPU:

    HANDLE_ERROR(cudaMalloc((void**)&dev_data1, size*sizeof(unsigned char)));
    HANDLE_ERROR(cudaMalloc((void**)&dev_data2, size*sizeof(unsigned char)));
    HANDLE_ERROR(cudaMalloc((void**)&dev_data_res, size*sizeof(int)));

    HANDLE_ERROR(cudaMemcpy(dev_data1, img1_data, size*sizeof(unsigned char), cudaMemcpyHostToDevice)); 
    HANDLE_ERROR(cudaMemcpy(dev_data2, img2_data, size*sizeof(unsigned char), cudaMemcpyHostToDevice));
    HANDLE_ERROR(cudaMemcpy(dev_data_res, imgresult_data, size*sizeof(int), cudaMemcpyHostToDevice));

    float time;
    cudaEvent_t start, stop;

    HANDLE_ERROR( cudaEventCreate(&start) );
    HANDLE_ERROR( cudaEventCreate(&stop) );
    HANDLE_ERROR( cudaEventRecord(start, 0) );

    for(int m = 0; m < nb_loops ; m++)
    {
        diff<<<height, width>>>(dev_data1, dev_data2, dev_data_res);
    }

    HANDLE_ERROR( cudaEventRecord(stop, 0) );
    HANDLE_ERROR( cudaEventSynchronize(stop) );
    HANDLE_ERROR( cudaEventElapsedTime(&time, start, stop) );

    HANDLE_ERROR(cudaMemcpy(imgresult_data, dev_data_res, size*sizeof(int), cudaMemcpyDeviceToHost));

    printf("Time to generate:  %4.4f ms \n", time/nb_loops);

    break;

    case CPU:

    clock_t begin = clock(), diff;

    for (int z=0; z<nb_loops; z++)
    {
        // Apply the difference between 2 images
        for (int i = 0; i < height; i++)
        {
            tmp = i*imgresult_pitch;
            for (int j = 0; j < width; j++)
            {
                imgresult_data[j + tmp] = (int) img2_data[j + tmp] - (int) img1_data[j + tmp];
            }
        }
    }
    diff = clock() - begin;

    float msec = diff*1000/CLOCKS_PER_SEC;
    msec = msec/nb_loops;
    printf("Time taken %4.4f milliseconds", msec);

    break;
}

这是我的核心功能:

__global__ void diff(unsigned char *data1 ,unsigned char *data2, int *data_res)
{
    int row = blockIdx.x;
    int col = threadIdx.x;
    int v = col + row*blockDim.x;

    if (row < MAX_H && col < MAX_W)
    {
        data_res[v] = (int) data2[v] - (int) data1[v];
    }
}

我为每个人获得了这些执行时间

  • CPU:1,3210ms
  • GPU:0,3229ms

我想知道为什么GPU结果不会像它应该的那样低。我是Cuda的初学者,所以如果有一些经典错误请全面。

EDIT1: 感谢您的反馈意见。我试图删除&#39; if&#39;来自内核的条件,但它并没有改变我的程序执行时间。

然而,在安装了Cuda profiler之后,它告诉我我的线程并没有同时运行。我不明白为什么会有这种消息,但这似乎是正确的,因为我使用GPU的速度比使用CPU快5到6倍。该比率应该更大,因为每个线程应该同时处理一个像素到所有其他像素。如果你知道我做错了什么,那就太好了......

流量。

2 个答案:

答案 0 :(得分:-2)

代码可能存在其他问题,但这就是我所看到的。 __global__ void diff中的以下行被认为不是最佳的:

if (row < MAX_H && col < MAX_W)
{
    data_res[v] = (int) data2[v] - (int) data1[v];
}

内核中的条件运算符会导致 warp divergence 。这意味着warp中的ifelse部分按顺序执行,而不是并行执行。另外,正如您可能已经意识到的那样,if仅在边界处评估为false。为了避免分歧和不必要的计算,将图像分成两部分:

  1. row < MAX_H && col < MAX_W始终为true的中心部分。为此区域创建其他内核。此处不需要if

  2. 将使用您的diff内核的边框区域。

  3. 显然,你将修改调用内核的代码。

    另外注意:

    1. GPU具有面向吞吐量的架构,但不像CPU那样面向延迟。这意味着在处理少量数据时,CPU可能比CUDA更快。您是否尝试过使用大型数据集?

    2. CUDA Profiler是一个非常方便的工具,可以告诉你在代码中不是最佳的。

答案 1 :(得分:-2)

我认为你没有正确测量时间,内存复制在GPU中是一个耗时的步骤,在测量你的时间时你应该考虑到这一步。

我看到了一些你可以测试的细节:

  1. 我认为您使用MAX_H和MAX_H作为常量,您可以考虑使用cudaMemcpyToSymbol()。

  2. 请记住使用__syncthreads()同步线程,这样就不会在每次循环迭代之间出现问题。

  3. CUDA适用于warp,因此每个块的块和线程数更好地作为8的倍数,但每块不超过512个线程,除非您的硬件支持它。以下是每个块使用128个线程的示例:&lt;&lt;&lt;(cols * rows + 127)/ 128,128&gt;&gt;&gt;。

  4. 请记住,在GPU中释放已分配的内存并销毁创建的时间事件。

  5. 在你的内核函数中,你可以有一个变量int v = threadIdx.x + blockIdx.x * blockDim.x。

  6. 您是否在执行时间旁边测试过您的结果是否正确?我认为你应该在使用填充时使用cudaMallocPitch()和cudaMemcpy2D()。