如何强制仿函数看到整个推力::矢量,以便可以进行排序?

时间:2016-07-12 15:12:11

标签: c++ cuda functor thrust

我是CUDA的新手,并且对仿函数有点麻烦。我试图输入一个推力::矢量的推力::矢量到一个仿函数。目前我可以输入一个向量并对每个元素做一些事情并使用thrust :: for_each返回修改后的向量,但是如果我想要在仿函数中对向量进行排序,我需要能够立即输入整个向量所以仿函数可以作为一个整体来对待它。有没有办法做到这一点?

下面的代码编译,但不返回已排序的向量。

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <stdlib.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <thrust/functional.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/reduce.h>
#include <thrust/transform_reduce.h>
#include <thrust/transform.h>
#include <thrust/sort.h>
#include <thrust/execution_policy.h>
#include <thrust/system/cuda/execution_policy.h>
#include <thrust/tuple.h>
#include <thrust/count.h>
#include <thrust/sequence.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/for_each.h>
#include <ctime>
#include <cstdio>
#include <cassert> 

using namespace std;
template<typename T>
struct sort_vector
{
    __host__ __device__ thrust::device_vector<float> operator()    (thrust::tuple<thrust::device_vector<float>, thrust::device_vector<float>>     x)
    {
    thrust::device_vector<float> y = thrust::get<0>(x);
    thrust::sort(y.begin(), y.end());
    return thrust::get<1>(x) = y;
    }
};

int main() {
    thrust::device_vector<float> d_fraction(5);
    d_fraction[0] = 1;
    d_fraction[1] = 5;
    d_fraction[2] = 3;
    d_fraction[3] = 2;
    d_fraction[4] = 4;

    cout << "original" << endl;
    int f = 0;
    while (f < 5){
        cout << d_fraction[f] << endl;
        f++;
    }

    cudaStream_t s1;
    cudaStreamCreate(&s1);
    thrust::device_vector<float> result1(5);

    thrust::for_each(thrust::cuda::par.on(s1), 
    thrust::make_zip_iterator(thrust::make_tuple(d_fraction.begin(), result1.begin())),
    thrust::make_zip_iterator(thrust::make_tuple(d_fraction.end(), result1.end())), sort_vector<thrust::device_vector<float>>());

    cudaStreamSynchronize(s1);

    cout << "sorted" << endl;
    int d = 0;
    while (d < 5){
        cout << Sresult2[d] << endl;
        d++;
    }

    cudaStreamDestroy(s1);
    return 0;
}

然而,当我尝试使用诸如

之类的引用时
 _host__ __device__ thrust::device_vector<float> operator()    (thrust::tuple<thrust::device_vector<float> &, thrust::device_vector<float> &>     x)

代码不再编译。

我是否可能需要为向量转换引用指针,以便仿函数可以看到整个向量? 或者是否有可能问题是我按值传递向量,并且有一种不同的方式我不知道将向量传递给函子?

1 个答案:

答案 0 :(得分:4)

仿函数通常在单个线程的上下文中运行。如果使用CUDA后端,我们就是在谈论CUDA线程。

对矢量进行排序的典型方法是直接在矢量上使用thrust::sort。在最微不足道的用法中,根本不需要算子定义。

如果你想在一个仿函数&#34;中对一个向量&#34;进行排序,那么有必要将一个指向该向量的指针传递给仿函数,并让仿函数对此进行处理。

推力设备代码(在仿函数的上下文中执行的代码)通常不能直接处理thrust::device_vector之类的结构。目前还不可能建立器件矢量的器件矢量。

因此,我已将您的代码修改为可行的内容并在仿函数中进行排序&#34;。我选择将要排序的矢量连接成单个矢量。我们将此向量的地址传递给排序仿函数,然后每个线程计算其排序范围,并将其传递给thrust::sort以便在线程中进行顺序排序:

$ cat t1211.cu
#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/sort.h>
#include <thrust/execution_policy.h>
#include <thrust/for_each.h>
#include <thrust/sequence.h>
#include <cstdlib>

const int num_segs = 3;  // number of segments to sort
const int num_vals = 5;  // number of values in each segment
const int range = 100;   // range of values

using namespace std;

template <typename T>
struct sort_vector
{
    T *data;
    sort_vector(T *_data) : data(_data) {};
    __host__ __device__ void operator()(int idx)
    {
    thrust::sort(thrust::seq, data+idx*num_vals, data+((idx+1)*num_vals));
    }
};

int main() {
    thrust::device_vector<float> d_data(num_segs*num_vals);
    for (int i = 0; i < num_segs*num_vals; i++)
      d_data[i] = rand()%range;

    cout << "original" << endl;
    int f = 0;
    while (f < num_segs*num_vals){
        cout << d_data[f] << endl;
        f++;
    }
    thrust::device_vector<int> d_idxs(num_segs);
    thrust::sequence(d_idxs.begin(), d_idxs.end());
    cudaStream_t s1;
    cudaStreamCreate(&s1);
    //thrust::device_vector<float> result1(5);

    thrust::for_each(thrust::cuda::par.on(s1),
    d_idxs.begin(),
    d_idxs.end(), sort_vector<float>(thrust::raw_pointer_cast(d_data.data())));

    cudaStreamSynchronize(s1);

    cout << "sorted" << endl;
    int d = 0;
    while (d < num_segs*num_vals){
        cout << d_data[d] << endl;
        d++;
    }

    cudaStreamDestroy(s1);
    return 0;
}
$ nvcc -o t1211 t1211.cu
$ ./t1211
original
83
86
77
15
93
35
86
92
49
21
62
27
90
59
63
sorted
15
77
83
86
93
21
35
49
86
92
27
59
62
63
90
$

在这种情况下,正如thrust::seq所显示的那样,每个线程中正在进行的工作是按顺序完成的。 (这里的线程一起并行运行,但它们没有合作 - 每个线程都在处理一个独立的问题。)

这不是唯一可行的解​​决方案。您可能对this问题/答案感兴趣,其中包含各种其他相关想法。

要清楚,我认为你在这里讨论的是一个&#34;矢量化&#34; (或分段)排序。这不是最快的方法,但我试图为您展示一些可行的概念作为您所展示内容的直接扩展,以回答您的问题(&#34;如何强迫仿函数看到一个整个推力::向量,以便排序是可能的?&#34;)上面链接的问题/答案讨论了一种更快的矢量化排序方法。