仔细检查对CUDA中内存合并的理解

时间:2015-07-02 05:02:14

标签: c++ cuda

假设我定义了一些GPU可见的数组:

double* doubleArr = createCUDADouble(fieldLen);
float* floatArr = createCUDAFloat(fieldLen);
char* charArr = createCUDAChar(fieldLen);

现在,我有以下CUDA线程:

void thread(){
  int o = getOffset(); // the same for all threads in launch
  double d = doubleArr[threadIdx.x + o];
  float f = floatArr[threadIdx.x + o];
  char c = charArr[threadIdx.x + o];
}

我不太确定我是否正确地解释了文档,它对我的​​设计非常关键:对double,float和char的内存访问是否会很好地合并?(猜猜:是的,它将适合sizeof(type) * blockSize.x / (transaction size)个交易,加上上边界和下边界可能还有一个额外的交易。)

1 个答案:

答案 0 :(得分:2)

是的,对于您展示的所有案例,假设createCUDAxxxxx转换为某种普通cudaMalloc类型的操作,一切都应该很好地合并。

如果我们有通过cudaMalloc分配的普通1D设备数组,一般来说,如果我们的加载模式包含表单的数组索引,我们应该在线程之间有良好的合并行为:

data_array[some_constant + threadIdx.x];

数组的数据类型无关紧要 - 它会很好地合并。

但是,从性能角度来看,全局负载(假设L1未命中)将以最小128字节的粒度发生。因此,为每个帖子加载较大的尺寸(例如intfloatdoublefloat4等)可能会提供更好的性能。如果负载跨越足够多的经线,则缓存往往会减轻任何差异。

使用profiler对特定代码进行验证也很容易。根据您选择的探查器,有很多方法可以做到这一点,例如使用nvprof,您可以这样做:

nvprof --metric gld_efficiency ./my_exe

它将返回一个平均百分比数字,或多或少准确地反映了全局负载上发生的最佳合并的百分比。

This是我经常引用的有关内存优化的其他背景信息的演示文稿。

我想有人会来并注意到这种模式:

data_array[some_constant + threadIdx.x];

大致对应于上述演示文稿的幻灯片40-41上所示的访问类型。并且 aha !! 效率降至50%-80%。如果只考虑单个扭曲载荷,那就是这样。但是,参考幻灯片40,我们看到“第一个”加载将需要加载两个高速缓存行。然而,之后,为了简单起见,额外的负载(向右移动)每个warp-load只需要一个额外的/新的高速缓存行(假设存在L1或L2高速缓存,并且合理的位置,即没有捶打)。因此,在一个相当大的数组(超过128个字节)上,平均值要求将是每个warp一个新的高速缓存行,这相当于100%的效率。