2016-07-12 7 views
0

私はCUDAを初めて使い、ファンクタに少し問題があります。私はthrust :: vectorのベクトルをファンクタに入力しようとしています。現在私はベクトルを入力して各要素に何かを行い、thrust :: for_eachを使って修正したベクトルを返すことができますが、もし私がファンクタでベクトルをソートしたいのであれば、ベクトル全体を一度に入力できるようにする必要がありますファンクタは全体としてそれに作用することができます。これを行う方法はありますか?functorがthrust :: vector全体を見て、並べ替えができるようにするにはどうすればいいですか?

以下のコードはコンパイルされますが、ソートされたベクトルは返されません。しかし

#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) 

コードは、もはやコンパイルなどの参照を使用しないようにしてください。

ファンクタがベクトル全体を見ることができるように、ベクトルの参照ポインタをキャストする必要がありますか? または、問題が、値によってベクトルを渡している可能性があります。また、ベクトルをファンクタに渡すことを知らない別の方法がありますか?

答えて

3

通常、ファンクタは単一のスレッドのコンテキストから動作します。 CUDAバックエンドを使用している場合は、CUDAスレッドについて説明しています。

ベクターをソートする典型的な方法は、ベクトルに直接thrust::sortを使用することです。最も単純な使用法では、ファンクタの定義はまったく必要ありません。

「ファンクタ内で」ベクトルをソートする場合は、そのベクトルへのポインタをファンクタに渡し、ファンクタでそのベクトルを処理させる必要があります。

推力装置コード(ファンクタのコンテキストで実行されるもの)は、一般的にthrust::device_vectorのような構造体を直接処理することはできません。現在、デバイスベクトルのデバイスベクトルを構築することは、現在のところ不可能です。

したがって、コードを実行可能で、「ファンクタ内」でソートしました。ソートするベクトルを連結して1つのベクトルにすることを選択しました。我々は、ソートファンクタこのベクターのアドレスを渡し、その後、各スレッドは、ソートし、その範囲を算出し、スレッドにシーケンシャルソートに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質問/回答には他にもさまざまな考えがあります。

ここでは、「ベクトル化」(またはセグメント化)の並べ替えを検討していると思います。これは最速の方法ではありませんが、あなたの質問に答えるために、あなたが示していることを簡単に拡張するための実用的な概念を実証しようとしています(「ファンタソートが可能ですか? ")上記のリンクされた質問/回答では、ベクトル化されたソートのより高速なアプローチについて説明しています。

+0

@Robert_Crovellaこれはうまくいきました、ありがとうございます!クイックフォローアップの質問。ファンクタに複数のポインタを送ることは可能ですか?このようにして2つの推力ベクトルを加算することができますか?私は既存のコードを修正しようとしていますが、メンバー関数の無効な再宣言などのエラーを送信しています。 – gracie

+0

はい、複数の初期化パラメータをファンクタに送ることができます。これは実際には純粋なC++アクティビティです(ファンクタは実際には推論に固有のものではなく、C++の動物です)。複数の値を受け取り、それらをクラスデータメンバに割り当てるファンクタのコンストラクタを作成するだけで済みます。例としては、[copy_func]コンストラクタが必要とする[here](http://stackoverflow.com/questions/35736801/making-the-number-of-key-occurances-equal-using-cuda-thrust/35737950#35737950)があります。 2つのパラメータ。 2つのポインタを渡すことはできませんが、多くのパラメータを渡すことはできません。 –

+0

実際、私の答えにリンクしている回答には、複数のパラメータを持つファンクタ( 'sort_functor')が含まれています。そこでの初期化は、デフォルト以外のコンストラクタを使用するのではなく、ファンクタオブジェクトをインスタンス化し、動的パラメータの初期化を使用して別々に実行されます。いずれの方法も実行可能です。 –

関連する問題