如何在cuda中并行化具有交叉元素依赖性的嵌套循环?

时间:2014-01-03 00:18:32

标签: for-loop cuda parallel-processing nested sum

我是cuda的初学者,我遇到了一些困难

如果我有一个大小为N的输入向量A和结果向量B,并且B [i]依赖于除A [i]之外的A的所有元素,我怎么能编码它而不必多次调用内核在一个串口for循环?我想不出一种同时兼顾外圈和内圈的方法。

编辑:拥有cc 2.0的设备

示例:

// a = some stuff
int i;
int j;
double result = 0;
for(i=0; i<1000; i++) {
  double ai = a[i]; 
  for(j=0; j<1000; j++) {
    double aj = a[j];
    if (i == j)
      continue;
    result += ai - aj;  
  }
}

我现在有这个:

//in host
int i;
for(i=0; i<1000; i++) {
    kernelFunc <<<2, 500>>> (i, d_a)
}

有没有办法消除串行循环?

1 个答案:

答案 0 :(得分:2)

这样的事情应该有用,我想:

__global__ void my_diffs(const double *a, double *b, const length){

  unsigned idx = threadIdx.x + blockDim.x*blockIdx.x;
  if (idx < length){
    double my_a = a[idx];
    double result = 0.0;
    for (int j=0; j<length; j++)
      result += my_a - a[j];
    b[idx] = result;
  }
}

(用浏览器编写,未经测试)

这可以通过几种方式进一步优化,但对于具有L1缓存的cc 2.0和更新设备,这些优化的好处可能很小:

  1. 使用共享内存 - 我们可以将每个元素的全局加载数减少到每个元素一个。但是,初始加载将缓存在L1中,并且您的数据集非常小(1000 double个元素?)因此可能会受到限制
  2. 创建偏移索引方案,因此每个线程使用来自缓存行的不同元素来创建合并访问(即,为每个线程修改j索引)。同样,对于cc 2.0和更新的设备,由于L1缓存以及广播warp全局读取的能力,这可能没有多大帮助。
  3. 如果你必须使用cc 1.x设备,那么你将获得一次或多次优化的重要意义 - 在这种情况下,我在这里显示的代码将明显变慢。

    请注意,我选择不打扰我们从其自身减去a[i]的特殊情况,因为它应该大约为零,并且不应该打扰您的结果。如果您对此感到担忧,可以轻松地将其解决。

    如果你增加块并减少每个块的线程,你也会获得更多性能,可能是这样的:

    my_diffs<<<8,128>>>(d_a, d_b, len);
    

    原因是许多GPU有超过1或2个SM。为了最大化这些具有如此小数据集的GPU的性能,我们希望尝试在每个SM上启动至少一个块。在网格中有更多的块使这更有可能。

    如果你想完全并行计算,那么方法是在GPU内存中创建一个二维矩阵(让我们称之为c [...]),方形尺寸等于长度你的矢量。然后我会创建一个2D线程网格,并让每个线程执行减法(a[row] - a[col])并将其结果存储在c[row*len+col]中。然后,我将启动第二个(1D)内核来对c的列进行求和(每个线程都有一个循环来对一列求和)以创建结果向量b。但是我不确定这会比我概述的方法更快。这种“更完全并行化”的方法也不会轻易适应我所讨论的优化。