推力:填充隔离空间

时间:2014-10-31 02:24:27

标签: cuda opencl thrust

我有一个这样的数组:

0 0 0 1 0 0 0 0 5 0 0 3 0 0 0 8 0 0 < / p>

我希望每个非零元素一次扩展一个元素,直到它到达其他非零元素,结果是这样的:

1 1 1 1 1 1 5 5 5 5 3 3 3 3 8 8 8 8 < / p>

有没有办法用推力做到这一点?

1 个答案:

答案 0 :(得分:3)

  

有没有办法用推力做到这一点?

是的,这是一种可行的方法。

  1. 对于序列中的每个位置,计算2个距离。第一个是在左方向上距离最近的非零值的距离,第二个是在向右方向上到最近的非零值的距离。如果位置本身不为零,则左右距离都将计算为零。我们的基本引擎将是分段包含扫描,一个是从左到右计算的(用于计算每个零段从左边开始的距离),另一个是在反向计算(用于计算从右边开始的距离)每个零段)。使用您的示例:

    a vector:    0 0 0 1 0 0 0 0 5 0 0 3 0 0 0 8 0 0
    a left dist: ? ? ? 0 1 2 3 4 0 1 2 0 1 2 3 0 1 2
    a right dist:3 2 1 0 4 3 2 1 0 2 1 0 3 2 1 0 ? ?
    

    请注意,在每个距离计算中,如果该末端不是以非零值开始,则必须使用特殊情况(因为距离该方向的距离是&#34;未定义&#34;)。我们将通过为这些?距离分配大值来特殊情况,其原因将在下一步中变得明显。

  2. 我们现在将创建一个&#34;地图&#34; vector,对于每个输出位置,允许我们从属于该输出位置的原始输入向量中选择一个元素。这个地图矢量是通过取两个计算距离中的较小者,并从左或右调整指数来计算的:

    output index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
    a left dist:  ? ? ? 0 1 2 3 4 0 1  2  0  1  2  3  0  1  2
    a right dist: 3 2 1 0 4 3 2 1 0 2  1  0  3  2  1  0  ?  ?
    map vector:   3 3 3 3 3 3 8 8 8 8 11 11 11 11 15 15 15 15
    

    对于地图矢量计算,如果a left dist&gt; a right dist然后我们接受output index并向其添加a right dist,以在该位置生成地图矢量元素。否则,我们会使用output index并从中减去a left dist。请注意,上面的特殊情况?条目应被视为&#34;任意大&#34;这个计算。这在代码中通过使用大整数(1 <&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt

  3. 一旦我们有了地图矢量,使用它来从输入到输出矢量执行映射复制是一件小事:

    a vector:    0 0 0 1 0 0 0 0 5 0  0  3  0  0  0  8  0  0
    map vector:  3 3 3 3 3 3 8 8 8 8 11 11 11 11 15 15 15 15
    out vector:  1 1 1 1 1 1 5 5 5 5  3  3  3  3  8  8  8  8
    
  4. 这是一个完整的例子:

    $ cat t610.cu
    #include <thrust/device_vector.h>
    #include <thrust/copy.h>
    #include <thrust/scan.h>
    #include <thrust/iterator/permutation_iterator.h>
    #include <thrust/iterator/counting_iterator.h>
    #include <thrust/iterator/zip_iterator.h>
    #include <thrust/functional.h>
    #include <thrust/transform.h>
    #include <thrust/sequence.h>
    #include <iostream>
    #define IVAL (1<<30)
    
    // used to create input vector for prefix sums (distance vector computation)
    struct is_zero {
    
      template <typename T>
      __host__ __device__
      T operator() (T val) {
        return (val) ? 0:1;
        }
    };
    
    // inc and dec help with special casing of left and right ends
    struct inc {
    
      template <typename T>
      __host__ __device__
      T operator() (T val) {
        return val+IVAL;
        }
    };
    
    struct dec {
    
      template <typename T>
      __host__ __device__
      T operator() (T val) {
        return val-IVAL;
        }
    };
    
    // this functor is lifted from thrust example code
    // and is used to enable segmented scans based on flag delimitors
    // BinaryPredicate for the head flag segment representation
    // equivalent to thrust::not2(thrust::project2nd<int,int>()));
    template <typename HeadFlagType>
    struct head_flag_predicate : public thrust::binary_function<HeadFlagType,HeadFlagType,bool>
    {
        __host__ __device__
        bool operator()(HeadFlagType left, HeadFlagType right) const
        {
            return !right;
        }
    };
    
    // distance tuple ordering is left (0), then right (1)
    struct map_functor
    {
      template <typename T>
      __host__ __device__
      int operator() (T dist){
        int leftdist =  thrust::get<0>(dist);
        int rightdist = thrust::get<1>(dist);
        int idx =       thrust::get<2>(dist);
        return (leftdist > rightdist) ? (idx+rightdist):(idx-leftdist);
        }
    };
    
    int main(){
    
      int h_a[] = { 0, 0, 0, 1, 0, 0, 0, 0, 5, 0, 0, 3, 0, 0, 0, 8, 0, 0 };
      int n = sizeof(h_a)/sizeof(h_a[0]);
      thrust::device_vector<int> a(h_a, h_a+n);
      thrust::device_vector<int> az(n);
      thrust::device_vector<int> asl(n);
      thrust::device_vector<int> asr(n);
      thrust::transform(a.begin(), a.end(), az.begin(), is_zero());
      // set up distance from the  left vector (asl)
      thrust::transform_if(az.begin(), az.begin()+1, a.begin(), az.begin(),inc(), is_zero());
      thrust::transform(a.begin(), a.begin()+1, a.begin(), inc());
      thrust::inclusive_scan_by_key(a.begin(), a.end(), az.begin(), asl.begin(), head_flag_predicate<int>());
      thrust::transform(a.begin(), a.begin()+1, a.begin(), dec());
      thrust::transform_if(az.begin(), az.begin()+1, a.begin(), az.begin(), dec(), is_zero());
      // set up distance from the right vector (asr)
      thrust::device_vector<int> ra(n);
      thrust::sequence(ra.begin(), ra.end(), n-1, -1);
      thrust::transform_if(az.end()-1, az.end(), a.end()-1, az.end()-1, inc(), is_zero());
      thrust::transform(a.end()-1, a.end(), a.end()-1, inc());
      thrust::inclusive_scan_by_key(thrust::make_permutation_iterator(a.begin(), ra.begin()), thrust::make_permutation_iterator(a.begin(), ra.end()), thrust::make_permutation_iterator(az.begin(), ra.begin()), thrust::make_permutation_iterator(asr.begin(), ra.begin()), head_flag_predicate<int>());
      thrust::transform(a.end()-1, a.end(), a.end()-1, dec());
      // create combined map vector
      thrust::device_vector<int> map(n);
      thrust::counting_iterator<int> idxbegin(0);
      thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(asl.begin(), asr.begin(), idxbegin)), thrust::make_zip_iterator(thrust::make_tuple(asl.end(), asr.end(), idxbegin+n)),  map.begin(), map_functor());
      // use map to create output
      thrust::device_vector<int> result(n);
      thrust::copy(thrust::make_permutation_iterator(a.begin(), map.begin()), thrust::make_permutation_iterator(a.begin(), map.end()), result.begin());
      // display results
      std::cout << "Input vector:" << std::endl;
      thrust::copy(a.begin(), a.end(), std::ostream_iterator<int>(std::cout, " "));
      std::cout << std::endl;
      std::cout << "Output vector:" << std::endl;
      thrust::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
      std::cout << std::endl;
    }
    $ nvcc -arch=sm_20 -o t610 t610.cu
    $ ./t610
    Input vector:
    0 0 0 1 0 0 0 0 5 0 0 3 0 0 0 8 0 0
    Output vector:
    1 1 1 1 1 1 5 5 5 5 3 3 3 3 8 8 8 8
    $
    

    注意:

    1. 上述实现可能具有可以改进的领域,特别是在操作融合方面。但是,出于理解的目的,我认为融合会使代码更难阅读。

    2. 我真的只在你给出的特定例子上测试过它。您可能会发现错误。我的目的不是给你一个你使用但不了解的黑盒库函数,而是教你如何编写自己的代码来做你想要的。

    3. &#34;歧义&#34; JackOLantern指出仍然存在于您的问题陈述中。我通过选择我的地图仿函数行为模仿你所指示的输出来模糊它,但只是通过创建一个同样有效但相反的地图仿函数实现(使用&#34; if a left dist&lt; {{1然后...&#34;相反)我可以使3到8之间的结果采取其他可能的结果/状态。您的评论&#34;如果存在歧义,任何到达该职位的人首先将其价值填入该空间&#34;对我来说毫无意义,除非你的意思是&#34;我不关心你提供的结果。&#34;没有特定线程的概念首先到达特定点。线程(和块)可以按任何顺序执行,并且此顺序可以在不同设备之间更改,并运行以运行。