与CUDA全局内存中的memcpy奇怪的“银行冲突”类型行为

时间:2012-08-28 21:04:44

标签: performance memory cuda global

我已将性能问题提炼到下面显示的代码中。此代码采用128,000个64字节结构(“规则”)的数组,并将它们分散到另一个数组中。例如,如果SCATTERSIZE为10,那么代码将从“小”数组中复制(“散布”)128,000个这些结构,它们在索引0,1,2,...,127999处连续存储,并放置它们在“大”数组中的索引0,10,20,30,...,1279990。

这是我无法弄清楚的:在计算能力1.3(Tesla C1060)的设备上,只要SCATTERSIZE是16的倍数,性能就会大大降低。在计算能力2.0(Tesla C2075)的设备上,性能会受到相当大的影响每当SCATTERSIZE是24的倍数时就是位。

我不认为这可能是共享内存库的事情,因为我没有使用共享内存。而且我认为它与合并无关。使用命令行分析器并检查“gputime”条目,我发现1.3设备的运行时间增加了300%,而2.0设备的运行时间增加了40%,因为坏的SCATTERSIZE。我很难过。这是代码:

#include <stdio.h>
#include <cuda.h>
#include <stdint.h>

typedef struct{
  float a[4][4];
} Rule;

#ifndef SCATTERSIZE
#define SCATTERSIZE 96
#endif

__global__ void gokernel(Rule* b, Rule* s){
  int idx = blockIdx.x * blockDim.x + threadIdx.x;
  memcpy(&b[idx * SCATTERSIZE], &s[idx], sizeof(Rule));
}


int main(void){
  int blocksPerGrid = 1000;
  int threadsPerBlock = 128;
  int numThreads = blocksPerGrid * threadsPerBlock;
  printf("blocksPerGrid = %d, SCATTERSIZE = %d\n", blocksPerGrid, SCATTERSIZE);

  Rule* small;      
  Rule* big;        

  cudaError_t err = cudaMalloc(&big, numThreads * 128 * sizeof(Rule));
  printf("Malloc big: %s\n",cudaGetErrorString(err));

  err = cudaMalloc(&small, numThreads * sizeof(Rule));
  printf("Malloc small: %s\n",cudaGetErrorString(err));

  gokernel <<< blocksPerGrid, threadsPerBlock >>> (big, small);
  err = cudaThreadSynchronize();
  printf("Kernel launch: %s\n", cudaGetErrorString(err));
}

1 个答案:

答案 0 :(得分:1)

由于__device__ memcpy的实现是隐藏的(它是内置的编译器),因此很难说原因究竟是什么。一个预感(感谢njuffa就是这个)就是所谓的分区驻留,其中许多线程的地址映射到一个或几个物理DRAM分区而不是分布在它们之间。

在SM 1_2 / 1_3 GPU上,分区驻留可能非常糟糕,具体取决于内存访问步幅,但从SM_2_0设备开始,这已经得到了改进,这可以解释为什么效果不太明显。

你可以经常通过在数组中添加一些填充来解决这个问题,以避免冒犯偏移,但根据你的计算,它可能不值得。