0

我有以下代码cuda_computation.cu

#include <iostream>
#include <stdio.h>
#include <cuda.h>
#include <assert.h>

void checkCUDAError(const char *msg);

__global__ void euclid_kernel(float *x, float* y, float* f)
{
  int idx = blockIdx.x*blockDim.x + threadIdx.x;
  int i = blockIdx.x;
  int j = threadIdx.x;
  f[idx] = sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
}
int main()
{
  float *xh;
  float *yh;
  float *fh;
  float *xd;
  float *yd;
  float *fd;

  size_t n = 256;
  size_t numBlocks = n;
  size_t numThreadsPerBlock = n;

  size_t memSize = numBlocks * numThreadsPerBlock * sizeof(float);
  xh = (float *) malloc(n * sizeof(float));
  yh = (float *) malloc(n * sizeof(float));
  fh = (float *) malloc(memSize);

  for(int ii(0); ii!=n; ++ii)
    {
      xh[ii] = ii;
      yh[ii] = ii;
    }

  cudaMalloc( (void **) &xd, n * sizeof(float) );
  cudaMalloc( (void **) &yd, n * sizeof(float) );
  cudaMalloc( (void **) &fd, memSize );
  for(int run(0); run!=10000; ++run)
    {
      //change value to avoid optimizations
      xh[0] = ((float)run)/10000.0;
      cudaMemcpy( xd, xh, n * sizeof(float), cudaMemcpyHostToDevice );
      checkCUDAError("cudaMemcpy");
      cudaMemcpy( yd, yh, n * sizeof(float), cudaMemcpyHostToDevice );
      checkCUDAError("cudaMemcpy");
      dim3 dimGrid(numBlocks);
      dim3 dimBlock(numThreadsPerBlock);
      euclid_kernel<<< dimGrid, dimBlock >>>( xd, yd, fd );
      cudaThreadSynchronize();
      checkCUDAError("kernel execution");
      cudaMemcpy( fh, fd, memSize, cudaMemcpyDeviceToHost );
      checkCUDAError("cudaMemcpy");
    }
  cudaFree(xd);
  cudaFree(yd);
  cudaFree(fd);
  free(xh);
  free(yh);
  free(fh);
  return 0;
}

void checkCUDAError(const char *msg)
{
  cudaError_t err = cudaGetLastError();
  if( cudaSuccess != err) 
    {
      fprintf(stderr, "Cuda error: %s: %s.\n", msg, cudaGetErrorString( err) );
      exit(-1);
    }                         
}

在 FX QUADRO 380 上运行大约需要 6 英寸,而仅使用一个 i7-870 内核的相应串行版本只需大约 3 英寸。我错过了什么吗?代码在某些方面是否优化?还是只是预期的行为,对于简单的计算(比如这个所有对欧几里得距离),移动内存所需的开销超过了计算增益?

4

3 回答 3

2

您正在拆分问题,以便每个块负责一个 i 与所有 256 个 j。这是一个糟糕的局部性,因为必须为每个块重新加载这 256 个 j,总共加载 2*256*(256 + 1) 个。相反,拆分您的网格,以便每个块负责一个范围,例如 16 个 i 和 16 个 j,这仍然是 256 个块 * 256 个线程。但是现在每个块只加载 2*(16+16) 个值,总共或 2*256*32 个总负载。这个想法是,尽可能多次重用每个加载的值。这可能不会对 256x256 产生巨大影响,但随着尺寸的扩大,它变得越来越重要。

这种优化用于有效的矩阵乘法,它具有类似的局部性问题。有关详细信息,请参阅http://en.wikipedia.org/wiki/Loop_tiling或谷歌“优化矩阵乘法”。或许NVIDIA SDK中的矩阵乘法内核给出了一些细节和想法。

于 2012-05-23T13:53:10.593 回答
2

我认为您正在被移动数据的时间杀死。特别是由于您使用单个值调用 CUDA 内核,因此将大量值作为一维数组上传并对其进行操作可能会更快。

同样 sqrt 没有在 Cuda 上的硬件中完成(至少不是在我的 GPU 上),而 CPU 已经为此优化了 FPU 硬件,并且可能比 GPU 快 10 倍,对于这样的小工作可能会保留所有结果在 timign 运行之间缓存。

于 2012-05-22T22:29:02.493 回答
2

减少全局内存读取,因为它们很昂贵。每个线程有 4 次全局内存读取,使用共享内存可以减少到 2 次。

__global__ void euclid_kernel(const float * inX_g, const float* inY_g, float * outF_g)
{
    const unsigned int threadId = blockIdx.x * blockDim.x + threadIdx.x;

    __shared__ float xBlock_s;
    __shared__ float yBlock_s;

    if(threadIdx.x == 0)
    {
        xBlock_s = inX_g[blockIdx.x];
        yBlock_s = inY_g[blockIdx.x];
    }
    __syncthreads();

    float xSub = xBlock_s - inX_g[threadIdx.x];
    float ySub = yBlock_s - inY_g[threadIdx.x];

    outF_g[threadId] = sqrt(xSub * xSub + ySub * ySub);
}

您还应该使用不同的块大小进行测试(只要您有 100% 的占用率)。

于 2012-05-23T00:10:43.847 回答