CUDA的内存访问性能很差

时间:2012-05-03 14:39:20

标签: cuda

我对CUDA很新,并试图编写一个测试程序。 我在GeForce GT 520卡上运行该应用程序,并且性能非常差。

应用程序用于处理某些图像,每行由一个单独的线程处理。 以下是该应用程序的简化版本。请注意,在实际应用程序中,所有常量实际上都是变量,作为调用者提供。

运行以下代码时,超过20秒才能完成执行。

但与使用 malloc / free 相反,当l_SrcIntegral被定义为本地数组时(如注释行中所示),需要不到1秒以完成执行。

由于数组的实际大小是动态的(而不是1700),因此无法在实际应用程序中使用此本地数组。

任何建议如何改善这个相当简单的代码的性能将不胜感激。

#include "cuda_runtime.h"
#include <stdio.h>

#define d_MaxParallelRows 320
#define d_MinTreatedRow   5
#define d_MaxTreatedRow   915
#define d_RowsResolution  1
#define k_ThreadsPerBlock 64

__global__ void myKernel(int Xi_FirstTreatedRow)
{
  int l_ThreadIndex = blockDim.x * blockIdx.x + threadIdx.x;
  if (l_ThreadIndex >= d_MaxParallelRows)
    return;
  int l_Row = Xi_FirstTreatedRow + (l_ThreadIndex * d_RowsResolution);
  if (l_Row <= d_MaxTreatedRow) {

    //float l_SrcIntegral[1700];
    float* l_SrcIntegral = (float*)malloc(1700 * sizeof(float));

    for (int x=185; x<1407; x++) {
      for (int i=0; i<1700; i++)
        l_SrcIntegral[i] = i;
    }

    free(l_SrcIntegral);
  }
}

int main()
{
  cudaError_t cudaStatus;

  cudaStatus = cudaSetDevice(0);

  int l_ThreadsPerBlock = k_ThreadsPerBlock;
  int l_BlocksPerGrid = (d_MaxParallelRows + l_ThreadsPerBlock - 1) / l_ThreadsPerBlock;

  int l_FirstRow = d_MinTreatedRow;
  while (l_FirstRow <= d_MaxTreatedRow) {
    printf("CUDA: FirstRow=%d\n", l_FirstRow);
    fflush(stdout);

    myKernel<<<l_BlocksPerGrid, l_ThreadsPerBlock>>>(l_FirstRow);

    cudaDeviceSynchronize();

    l_FirstRow += (d_MaxParallelRows * d_RowsResolution);
  }

  printf("CUDA: Done\n");

  return 0;
}

2 个答案:

答案 0 :(得分:1)

<强> 1

正如@aland所说,你甚至可能会遇到更糟糕的性能,只计算每个内核调用中的一行。

你必须考虑处理整个输入,只是理论上使用大规模并行处理的力量。

为什么只用320个线程启动多个内核来计算一行? 如何使用尽可能多的块,并使每个块的线程处理一行。

(每个区块320个线程不是一个好选择,看看如何达到更好的占用率)

<强> 2

如果作为寄存器和共享内存的快速资源不够,则必须使用tile apporach,这是使用GPGPU编程的基础之一。

将输入数据分成相同大小的切片,并在线程中循环处理它们。

在这里,我发布了一个这样的平铺方法的例子:

Parallelization in CUDA, assigning threads to each column

请注意该平铺方法中的范围检查!

为您提供想法的示例:

以任意大小的矩阵计算列向量中所有元素的总和。

每个块处理一列,并且该块的线程存储在一个tile中,它们的元素在共享内存数组中循环。完成后,他们使用平行减少计算总和,只是为了开始下一次迭代 最后,每个块计算出其向量的总和。

答案 1 :(得分:0)

您仍然可以使用共享内存来使用动态数组大小。只需在内核调用的<<<...>>>中传递第三个参数。这是每个块的共享内存大小。

一旦你在那里,只需将所有相关数据带入你的共享数组(你仍然应该尝试保持合并访问),为每个线程带来一个或几个(如果它与保持合并访问相关)元素。在线程被带到后同步(只有当你需要停止竞争条件,确保整个数组在完成任何计算之前都在共享内存中)并且你很高兴。

另外:你应该使用块和线程进行细分,而不是循环。我理解这只是一个使用本地数组的例子,但是,它仍然可以通过块/线程进行细分而不是嵌套for循环(这对于性能非常糟糕!)我希望你只使用1个块来运行示例代码和1个线程,否则没有多大意义。