GPU实现中的顺序操作

时间:2016-01-19 04:19:01

标签: cuda parallel-processing gpu

我必须在GPU

中实现以下算法
for(int I = 0; I < 1000; I++){
    VAR1[I+1] = VAR1[I] + VAR2[2*K+(I-1)];//K is a constant
}

每次迭代都依赖于之前的版本,因此并行化很困难。我不确定原子操作在这里是否有效。我该怎么办?

修改

VAR1 VAR2 都是1D数组。

VAR1[0] = 1

1 个答案:

答案 0 :(得分:5)

这是一类名为recurrence relations的问题。根据递归关系的结构,可能存在封闭形式的解决方案,其描述了如何单独地(即并行地,没有递归地)计算每个元素。其中一篇早期开创性论文(关于并行计算)是Kogge and Stone,并且存在用于并行化特定形式的方法和策略。

有时递归关系是如此简单,以至于我们可以通过一点点&#34;检查&#34;来识别封闭形式的公式或算法。这个short tutorial给了这个想法更多的处理。

在您的情况下,让我们看看我们是否可以通过确定VAR1的前几个词应该是什么来发现任何内容,将之前的词替换为更新的词:

i      VAR1[i]
___________________
0        1
1        1 + VAR2[2K-1]
2        1 + VAR2[2K-1] + VAR2[2K]
3        1 + VAR2[2K-1] + VAR2[2K] + VAR2[2K+1]
4        1 + VAR2[2K-1] + VAR2[2K] + VAR2[2K+1] + VAR2[2K+2]
...

希望突然发现的是,上面的VAR2[]条款遵循prefix sum的模式。

这意味着可以通过以下方式给出一种可能的解决方法:

VAR1[i] = 1+prefix_sum(VAR2[2K + (i-2)])   (for i > 0) notes:(1) (2)
VAR1[i] = 1                                (for i = 0)

现在,前缀总和可以并行完成(这不是真正完全独立的操作,但它可以并行化。我不想在这里过多地讨论术语或纯度。我&#39;为你提出的问题提供并行化的一种可能的方法,而不是唯一的方法。)要在GPU上并行执行前缀和,我会使用像CUB这样的库或Thrust。或者你可以write your own虽然我不推荐它。

注意:

  1. 使用-1或-2作为前缀和i的偏移量可能取决于您使用inclusiveexclusive扫描或前缀总和操作

  2. 必须在适当的域上定义
  3. VAR2才能使其合理化。但是,您的问题陈述中隐含了该要求。

  4. 这是一个简单的工作示例。在这种情况下,由于VAR2索引术语2K+(I-1)仅代表I2K-1)的固定偏移量,因此我们只是将偏移量0用于演示目的,因此VAR2只是与VAR1在同一个域中的一个简单数组。为了演示目的,我将VAR2定义为只是所有1的数组。 gpu并行计算发生在VAR1向量中,CPU等效计算只是在cpu变量中即时计算,用于验证目的:

    $ cat t1056.cu
    #include <thrust/scan.h>
    #include <thrust/device_vector.h>
    #include <thrust/host_vector.h>
    #include <thrust/transform.h>
    #include <iostream>
    
    const int dsize = 1000;
    using namespace thrust::placeholders;
    int main(){
    
      thrust::device_vector<int> VAR2(dsize, 1);  // initialize VAR2 array to all 1's
      thrust::device_vector<int> VAR1(dsize);
      thrust::exclusive_scan(VAR2.begin(), VAR2.end(), VAR1.begin(), 0); // put prefix sum of VAR2 into VAR1
      thrust::transform(VAR1.begin(), VAR1.end(), VAR1.begin(),  _1 += 1);   // add 1 to every term
      int cpu = 1;
      for (int i = 1; i < dsize; i++){
        int gpu = VAR1[i];
        cpu += VAR2[i];
        if (cpu != gpu) {std::cout << "mismatch at: " << i << " was: " << gpu << " should be: " << cpu << std::endl; return 1;}
        }
      std::cout << "Success!" << std::endl;
      return 0;
    }
    
    $ nvcc -o t1056 t1056.cu
    $ ./t1056
    Success!
    $
    

    有关扫描操作用于解决线性递归问题的其他参考,请参阅Blelloch的论文here第1.4节。