查找CUDA数组中每个不同值的第一个索引

时间:2017-12-14 14:24:38

标签: cuda thrust

假设我们有一个这样的数组:

0, 0, 0, 1, 2, 2, 2, 3, 3, 4, ...

我想得到每个值的每个第一次出现的索引,所以在这个例子中[0,3,4,7,9]。对数组进行排序,所有可能的值都是已知且连续的。

我可能的解决方案是为此数组中的每个元素使用内核,并使用atomicmin来保存最低的索引。但我认为可以采用更好的方法。

2 个答案:

答案 0 :(得分:6)

如果您提供索引矢量,只需拨打thrust::unique_by_key()即可执行此操作。通过thrust::sequence()。这是一个有效的例子:

$ cat t3.cu
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/unique.h>
#include <thrust/sequence.h>
#include <iostream>

int main(){

  int keys[] = {0, 0, 0, 1, 2, 2, 2, 3, 3, 4};
  int ks = sizeof(keys)/sizeof(keys[0]);
  thrust::device_vector<int> d_keys(keys, keys+ks);
  thrust::device_vector<int> d_result(ks);
  thrust::sequence(d_result.begin(), d_result.end());
  int rs  = (thrust::unique_by_key(d_keys.begin(), d_keys.end(), d_result.begin())).first - d_keys.begin();
  thrust::copy_n(d_result.begin(), rs, std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl;
}

$ nvcc -arch=sm_35 -o t3 t3.cu
$ ./t3
0,3,4,7,9,
$

此处发生的重要活动是流压缩,推力为各种用例提供​​nice set of routines。例如,此操作也可以使用thrust::unique_copy()完成,在这种情况下,由于某些额外的代码复杂性,您可以省去thrust::sequence()调用(它将被thrust::counting_iterator替换与您的数据和适当的选择函子一起压缩,但它仍然需要相同长度的输出向量。

答案 1 :(得分:3)

正如@tera指出的那样,您可以将一个数字与前一个数字进行比较,以确定它是否是唯一数字序列中的第一个匹配项。您可以编写内核来为此条件生成掩码,以使掩码数组包含第一次出现的数字的索引和负数(如-1,因为它不能是索引)。之后,使用推力通过使用谓词来计算非-1值。然后使用与上面相同的谓词从掩码中复制这些值。最后,将结果复制回主持人。

以下是上述方法的示例实现。

#include <iostream>
#include <cuda_runtime.h>
#include <thrust/device_vector.h>
#include <thrust/count.h>
#include <thrust/copy.h>

using namespace std;


//Copy index
__global__ void is_first_occurence(int* input, int* is, int count)
{
    const int tid = blockIdx.x * blockDim.x + threadIdx.x;

    if(tid<count)
    {
        if(tid == 0)
        {
            is[0] = 0;
        }
        else if(input[tid] != input[tid-1])
        {
            is[tid] = tid;
        }
        else
        {
            is[tid] = -1;
        }
    }
}


struct isFirst
{
  __host__ __device__ bool operator()(const int x)
  {
    return (x != -1);
  }
};

int main(int argc, char** argv)
{
    const int count = 13;

    std::vector<int> arr = { 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4 ,4 };

    thrust::device_vector<int> arr_d = arr;
    thrust::device_vector<int> mask_d(arr_d.size());

    int* pArr = thrust::raw_pointer_cast(arr_d.data() );
    int* pMask = thrust::raw_pointer_cast(mask_d.data() );

    dim3 block(16);
    dim3 grid((count + block.x -1)/block.x);

    is_first_occurence<<<grid,block>>>(pArr, pMask, count);
    cudaDeviceSynchronize();

    int num_unique = thrust::count_if(mask_d.begin(), mask_d.end(), isFirst());

    thrust::copy_if(mask_d.begin(), mask_d.end(), arr_d.begin(), isFirst());

    std::vector<int> unique_indices(num_unique);

    thrust::copy(arr_d.begin(), arr_d.begin() + num_unique, unique_indices.begin());

    for(auto i:unique_indices)
    {
        cout<<i<<endl;
    }

    return 0;
}

使用以下命令进行编译和测试:

  

nvcc -o get_unique get_unique.cu -std = c ++ 11 -arch = sm_61