如何使用推力无需排序将相同的元素组合在一起

时间:2015-04-04 10:38:49

标签: cuda thrust

我有一个元素数组,每个元素定义"等于"仅限运营商。 换句话说,没有为这种类型的元素定义排序。

由于我无法在推力直方图示例中使用推力::排序,如何使用推力将相同的元素组合在一起?

例如:

我的数组最初是

a e t b c a c e t a

其中相同的字符代表相同的元素。

在详细说明之后,数组应该是

a a a t t b c c e e

但它也可以

a a a c c t t e e b

或任何其他排列

由于

2 个答案:

答案 0 :(得分:3)

我建议您遵循@ m.s所规定的方法。在那里发布的答案。正如我在评论中所说,元素的排序是一种非常有用的机制,有助于降低这类问题的复杂性。

然而,提出的问题询问是否可以在没有排序的情况下对相似元素进行分组。使用像GPU这样的固有并行处理器,我花了一些时间思考如何在没有排序的情况下完成它。

如果我们同时拥有大量对象以及大量独特的对象类型,那么我认为可以为问题带来一定程度的并行性,然而我在这里概述的方法仍然会有非常分散的内存访问模式。对于只有少量不同或唯一对象类型的情况,我在这里讨论的算法几乎没有值得赞扬。这只是一种可能的方法。可能还有其他更好的方法:

  1. 出发点是开发一组"链接列表"表示每个元素左边的匹配邻居和右边的匹配邻居。这是通过我的search_functorthrust::for_each在整个数据集上完成的。这一步是相当平行的,并且对于大型数据集也具有合理的内存访问效率,但它确实需要从开始到结束的整个数据集的最坏情况遍历(副作用,我会称之为,不能够使用排序;我们必须将每个元素与其他元素进行比较,直到找到匹配项。生成两个链表允许我们避免全面比较。

  2. 一旦我们拥有从步骤1构建的列表(右邻居和左邻居),使用thrust::count计算唯一对象的数量就很容易了。

  3. 然后我们使用thrust::copy_if流压缩来获取每个唯一元素的起始索引(即数据集中每种类型的唯一元素的最左侧索引)。

  4. 下一步是计算每个唯一元素的实例数。这一步是进行列表遍历,每个元素列表一个线程。如果我有少量独特元素,这将无法有效利用GPU。此外,列表遍历将导致糟糕的访问模式。

  5. 在我们计算了每种类型对象的数量之后,我们可以通过thrust::exclusive_scan在每种类型的对象的数量上为输出列表中的每个对象类型构建一系列起始索引

  6. 最后,我们可以将每个输入元素复制到输出列表中的适当位置。由于我们无法对元素进行分组或排序,因此我们必须再次使用列表遍历。再一次,如果唯一对象类型的数量很少,这将是低效的GPU使用,并且还将具有糟糕的内存访问模式。

  7. 这是一个完整的示例,使用您的示例数据字符集。为了帮助澄清我们打算对没有固有排序的对象进行分组的想法,我创建了一个有点任意的对象定义(my_obj),它定义了==比较运算符,但没有定义{ {1}}或<

    >

答案 1 :(得分:2)

In the discussion我们发现您的真正目标是消除float4元素向量中的重复项。 为了应用thrust::unique,需要对元素进行排序。

所以你需要一个4维数据的排序方法。这可以使用空间填充曲线来完成。我之前使用z-order curve (aka morton code)对3D数据进行排序。 3D case available有高效的CUDA实现,但是快速的Google搜索没有返回4D案例的即用型实现。

我找到了一篇论文,其中列出了使用z阶曲线对n维数据点进行排序的通用算法: Fast construction of k-Nearest Neighbor Graphs for Point Clouds (参见算法1:浮点莫顿顺序算法)。 此算法还有一个C++ implementation available

对于4D数据,可以展开循环,但可能有更简单,更有效的算法。

因此(未完全实现的)操作序列将如下所示:

#include <thrust/device_vector.h>
#include <thrust/unique.h>
#include <thrust/sort.h>

inline __host__ __device__ float dot(const float4& a, const float4& b)
{
    return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}

struct identity_4d
{
  __host__ __device__
  bool operator()(const float4& a, const float4& b) const
  {
    // based on the norm function you provided in the discussion
    return dot(a,b) < (0.1f*0.1f);
  }
};

struct z_order_4d
{
  __host__ __device__
  bool operator()(const float4& p, const float4& q) const
  {
    // you need to implement the z-order algorithm here
    // ...
  }
};

int main()
{
  const int N = 100;
  thrust::device_vector<float4> data(N);
  // fill the data
  // ...

  thrust::sort(data.begin(),data.end(), z_order_4d());

  thrust::unique(data.begin(),data.end(), identity_4d());

}