Cuda 如何使用无排序的推力将相等的元素组合在一起
我有一个元素数组,每个元素只定义“等于”操作符。 换句话说,对于这种类型的元素,没有定义排序 既然我不能像在推力直方图示例中那样使用推力::排序,那么如何使用推力将相等的元素组合在一起呢 例如: 我的数组最初是Cuda 如何使用无排序的推力将相等的元素组合在一起,cuda,thrust,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 或任何其他排列 谢谢我们发现您的真正目标是消除float4元素向量中的重复项。 为了应用,需
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
或任何其他排列
谢谢我们发现您的真正目标是消除float4
元素向量中的重复项。
为了应用,需要对元素进行排序
所以你需要一个四维数据的排序方法。这可以使用空间填充曲线来完成。我以前使用过对3D数据进行排序。有一些高效的CUDA实现,但是quick Google并没有为4D案例返回一个现成的实现
我找到了一篇论文,其中列出了使用z阶曲线对n维数据点进行排序的通用算法:
(参见算法1:浮点莫顿顺序算法)。
这个算法也有一个简单的例子
对于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());
}
#包括
#包括
#包括
内联\uuuuuu主机\uuuuuuuu设备\uuuuuuu浮点点(常量浮点4&a、常量浮点4&b)
{
返回a.x*b.x+a.y*b.y+a.z*b.z+a.w*b.w;
}
结构标识
{
__主机设备__
布尔运算符()(常量浮点4&a、常量浮点4&b)常量
{
//基于您在讨论中提供的norm函数
返回点(a、b)<(0.1f*0.1f);
}
};
结构z_顺序4d
{
__主机设备__
布尔运算符()(常量浮点4&p,常量浮点4&q)常量
{
//您需要在这里实现z顺序算法
// ...
}
};
int main()
{
常数int N=100;
推力::设备_矢量数据(N);
//填写数据
// ...
推力::排序(data.begin(),data.end(),z_order_4d());
唯一(data.begin(),data.end(),identity_4d());
}
我建议您遵循@m.s.在发布的答案中所述的方法。正如我在评论中所说,元素排序是一种非常有用的机制,有助于降低此类问题的复杂性
然而,提出的问题是,是否有可能在不进行排序的情况下对类似的元素进行分组。有了一个天生的并行处理器,比如GPU,我花了一些时间思考如何在不进行排序的情况下实现它
如果我们既有大量的对象,又有大量独特的对象类型,那么我认为有可能为这个问题带来某种程度的并行性,但是我在这里概述的方法仍然会有糟糕的、分散的内存访问模式。对于只有少量不同或唯一对象类型的情况,我在这里讨论的算法没有什么值得推荐的。这只是一种可能的方法。很可能还有其他更好的方法:
search_functor
和struch::for_each
在整个数据集上实现的。这一步相当并行,对于大型数据集也具有相当高的内存访问效率,但它确实需要从开始到结束对整个数据集进行最坏情况下的遍历(我称之为副作用,即无法使用排序;我们必须将每个元素与其他元素进行比较,直到找到匹配项)。两个链表的生成允许我们避免所有对所有的比较推力::count
计算唯一对象的数量就很容易了推力::copy_if
流压缩获得每个唯一元素的起始索引(即数据集中每种类型的唯一元素的最左边索引)asch::exclusive\u scan
为输出列表中的每种对象类型构建一系列起始索引my_obj
),它定义了=
比较运算符,但没有定义
$cat t707.cu
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
模板
我的班
{
T元素;
整数指数;
公众:
__主机设备我的对象():元素(0),索引(0){};
__主机设备我的对象(ta):元素(a),索引(0){};
__主机设备我的对象(ta,intidx):元素(a),索引(idx){};
__主机设备__
不明白{
重新
$ cat t707.cu
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/for_each.h>
#include <thrust/transform.h>
#include <thrust/transform_scan.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/copy.h>
#include <thrust/count.h>
#include <iostream>
template <typename T>
class my_obj
{
T element;
int index;
public:
__host__ __device__ my_obj() : element(0), index(0) {};
__host__ __device__ my_obj(T a) : element(a), index(0) {};
__host__ __device__ my_obj(T a, int idx) : element(a), index(idx) {};
__host__ __device__
T get() {
return element;}
__host__ __device__
void set(T a) {
element = a;}
__host__ __device__
int get_idx() {
return index;}
__host__ __device__
void set_idx(int idx) {
index = idx;}
__host__ __device__
bool operator ==(my_obj &e2)
{
return (e2.get() == this->get());
}
};
template <typename T>
struct search_functor
{
my_obj<T> *data;
int end;
int *rn;
int *ln;
search_functor(my_obj<T> *_a, int *_rn, int *_ln, int len) : data(_a), rn(_rn), ln(_ln), end(len) {};
__host__ __device__
void operator()(int idx){
for (int i = idx+1; i < end; i++)
if (data[idx] == data[i]) {
ln[i] = idx;
rn[idx] = i;
return;}
return;
}
};
template <typename T>
struct copy_functor
{
my_obj<T> *data;
my_obj<T> *result;
int *rn;
copy_functor(my_obj<T> *_in, my_obj<T> *_out, int *_rn) : data(_in), result(_out), rn(_rn) {};
__host__ __device__
void operator()(const thrust::tuple<int, int> &t1) const {
int idx1 = thrust::get<0>(t1);
int idx2 = thrust::get<1>(t1);
result[idx1] = data[idx2];
int i = rn[idx2];
int j = 1;
while (i != -1){
result[idx1+(j++)] = data[i];
i = rn[i];}
return;
}
};
struct count_functor
{
int *rn;
int *ot;
count_functor(int *_rn, int *_ot) : rn(_rn), ot(_ot) {};
__host__ __device__
int operator()(int idx1, int idx2){
ot[idx1] = idx2;
int i = rn[idx1];
int count = 1;
while (i != -1) {
ot[i] = idx2;
count++;
i = rn[i];}
return count;
}
};
using namespace thrust::placeholders;
int main(){
// data setup
char data[] = { 'a' , 'e' , 't' , 'b' , 'c' , 'a' , 'c' , 'e' , 't' , 'a' };
int sz = sizeof(data)/sizeof(char);
for (int i = 0; i < sz; i++) std::cout << data[i] << ",";
std::cout << std::endl;
thrust::host_vector<my_obj<char> > h_data(sz);
for (int i = 0; i < sz; i++) { h_data[i].set(data[i]); h_data[i].set_idx(i); }
thrust::device_vector<my_obj<char> > d_data = h_data;
// create left and right neighbor indices
thrust::device_vector<int> ln(d_data.size(), -1);
thrust::device_vector<int> rn(d_data.size(), -1);
thrust::for_each(thrust::counting_iterator<int>(0), thrust::counting_iterator<int>(0) + sz, search_functor<char>(thrust::raw_pointer_cast(d_data.data()), thrust::raw_pointer_cast(rn.data()), thrust::raw_pointer_cast(ln.data()), d_data.size()));
// determine number of unique objects
int uni_objs = thrust::count(ln.begin(), ln.end(), -1);
// determine the number of instances of each unique object
// get object starting indices
thrust::device_vector<int> uni_obj_idxs(uni_objs);
thrust::copy_if(thrust::counting_iterator<int>(0), thrust::counting_iterator<int>(0)+d_data.size(), ln.begin(), uni_obj_idxs.begin(), (_1 == -1));
// count each object list
thrust::device_vector<int> num_objs(uni_objs);
thrust::device_vector<int> obj_type(d_data.size());
thrust::transform(uni_obj_idxs.begin(), uni_obj_idxs.end(), thrust::counting_iterator<int>(0), num_objs.begin(), count_functor(thrust::raw_pointer_cast(rn.data()), thrust::raw_pointer_cast(obj_type.data())));
// at this point, we have built object lists that have allowed us to identify a unique, orderable "type" for each object
// the sensible thing to do would be to employ a sort_by_key on obj_type and an index sequence at this point
// and use the reordered index sequence to reorder the original objects, thus grouping them
// however... without sorting...
// build output vector indices
thrust::device_vector<int> copy_start(num_objs.size());
thrust::exclusive_scan(num_objs.begin(), num_objs.end(), copy_start.begin());
// copy (by object type) input to output
thrust::device_vector<my_obj<char> > d_result(d_data.size());
thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(copy_start.begin(), uni_obj_idxs.begin())), thrust::make_zip_iterator(thrust::make_tuple(copy_start.end(), uni_obj_idxs.end())), copy_functor<char>(thrust::raw_pointer_cast(d_data.data()), thrust::raw_pointer_cast(d_result.data()), thrust::raw_pointer_cast(rn.data())));
// display results
std::cout << "Grouped: " << std::endl;
for (int i = 0; i < d_data.size(); i++){
my_obj<char> temp = d_result[i];
std::cout << temp.get() << ",";}
std::cout << std::endl;
for (int i = 0; i < d_data.size(); i++){
my_obj<char> temp = d_result[i];
std::cout << temp.get_idx() << ",";}
std::cout << std::endl;
return 0;
}
$ nvcc -o t707 t707.cu
$ ./t707
a,e,t,b,c,a,c,e,t,a,
Grouped:
a,a,a,e,e,t,t,b,c,c,
0,5,9,1,7,2,8,3,4,6,
$