CUDA:使用推力根据另一个数组定义的顺序对数组进行排序

时间:2018-08-27 20:02:35

标签: parallel-processing cuda gpu thrust

我有10个数组。我想对它们进行排序。但是由于它们的元素具有相同的行为,所以我想保存计算并仅对其中的一个进行排序,而其他元素将基于已排序的数组进行排序。 我正在用推力。 有一个最佳的原因吗? 预先谢谢你。

2 个答案:

答案 0 :(得分:1)

从评论中,我的建议是:

在第一个数据集(数组)上使用thrust::sort_by_key,将第一个数据集作为键,并将索引序列(0、1、2,...)作为值。然后在推力聚集或分散操作中使用重新排列的索引序列来重新排列其余的数组。

根据要求,这是一个可行的示例:

$ cat t282.cu
#include <thrust/sort.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/copy.h>
#include <thrust/sequence.h>
#include <iostream>
#include <thrust/iterator/permutation_iterator.h>
#include <thrust/iterator/zip_iterator.h>

const size_t ds = 5;
typedef float ft;

int main(){
  ft a1[ds] = {0.0f, -3.0f, 4.0f, 2.0f, 1.0f};
// data setup
  thrust::device_vector<ft> d_a1(a1, a1+ds);
  thrust::device_vector<ft> d_a2(ds);
  thrust::device_vector<ft> d_a3(ds);
  thrust::device_vector<ft> d_a2r(ds);
  thrust::device_vector<ft> d_a3r(ds);
  thrust::device_vector<size_t> d_i(ds);
  thrust::sequence(d_i.begin(), d_i.end());
  thrust::sequence(d_a2.begin(), d_a2.end());
  thrust::sequence(d_a3.begin(), d_a3.end());
// sort
  thrust::sort_by_key(d_a1.begin(), d_a1.end(), d_i.begin());
// copy, using sorted indices
  thrust::copy_n(thrust::make_permutation_iterator(thrust::make_zip_iterator(thrust::make_tuple(d_a2.begin(), d_a3.begin())), d_i.begin()), ds, thrust::make_zip_iterator(thrust::make_tuple(d_a2r.begin(), d_a3r.begin())));
// output results
  thrust::host_vector<ft> h_a1 = d_a1;
  thrust::host_vector<ft> h_a2 = d_a2r;
  thrust::host_vector<ft> h_a3 = d_a3r;
  std::cout << "a1: " ;
  thrust::copy_n(h_a1.begin(), ds, std::ostream_iterator<ft>(std::cout, ","));
  std::cout << std::endl << "a2: " ;
  thrust::copy_n(h_a2.begin(), ds, std::ostream_iterator<ft>(std::cout, ","));
  std::cout << std::endl << "a3: " ;
  thrust::copy_n(h_a3.begin(), ds, std::ostream_iterator<ft>(std::cout, ","));
  std::cout << std::endl;
}
$ nvcc -o t282 t282.cu
$ cuda-memcheck ./t282
========= CUDA-MEMCHECK
a1: -3,0,1,2,4,
a2: 1,0,4,3,2,
a3: 1,0,4,3,2,
========= ERROR SUMMARY: 0 errors
$

在这里,代替thrust::gatherthrust::scatter操作,我只是用thrust::copy_n进行了thrust::permutation_iterator,以实现重新排序。我将其余的数组组合在一起,以使用thrust::zip_iterator重新排序,但这不是唯一的方法。

请注意,我不是针对10个数组,而是针对3个数组,但这应该说明了该方法。扩展到10个阵列应该仅仅是机械的。但是请注意,由于thrust::tuple限于10个项目,因此必须对10-11个以上的数组进行一些修改。作为修改,您可以简单地循环调用thrust::copy_n,对每个数组重新排序一次,而不用使用zip_iterator。我认为这不会在效率上产生很大的差异。

答案 1 :(得分:0)

执行此操作的几种方法(与Thrust无关):

    1. 初始化索引indices0, 1, 2, 3...等的数组。
    2. 排序indices,比较功能是访问数组之一(比较最便宜的数组)中的元素,然后进行比较。调用结果数组
    3. 对于10个数组中的每个数组,arr都应用Gather操作,该操作使用排序后的indicesarr作为收集数据。即所有sorted_arr[i] = arr[indices[i]]的{​​{1}}。
    1. 使一种排序实现方式也执行“索引日志保持”,即,每当您在“真实”数组中交换或放置数据时,也要在indexs数组中设置索引。
    2. 在10个数组之一(排序最便宜的数组)上运行这种保持索引的排序。
    3. 将1.3版的Gather应用于其他9个数组
  1. i是用于排序(或比较元素)的最便宜的数组

    1. 创建适当类型的对cheap对的数组。
    2. 让这些对的比较仅使用对中的第二个元素。
    3. 排序pairs[i] = { i, cheap[i] }
    4. pairs投影到其第一个元素上:pairs
    5. indices[i] = pairs[i].first投影到第二个元素:pairs
    6. 将1.3版的Gather应用于其他九个数组

第二个选项应该是最快的,但需要更多的精力;加上推力,这可能很困难。第一个或第三个应该是最简单的;推力接受自定义比较器,对吗?如果没有,则可能必须使用适当的比较器来定义包装器类。