2016-05-10 4 views
0

私はAoSからSoAへのアプローチをメモリの融合を利用してリファクタリングしています。その目的のために、私は共通のキーで縮小され、出力ベクトルの値を計算するために使われる2つのベクトルを持っています。元のコードは、私がエミュレートしたい単一のファンクタでこれを行いました。本質的reduce_by_key()から2つの縮小ベクトルの関数として出力

:RᵢとSᵢが同じ鍵によって低減ベクトルであり、Oᵢは、対応する出力ベクトルである

Oᵢ=Rᵢ/Sᵢ、。

以下

は私が何をしようとしている例示コードです:

typedef tuple<int,int> Tuple; 

struct BinaryTupleOp : public thrust::binary_function<Tuple const &, Tuple const &, int> 
{ 
    __host__ __device__ 
    int operator()(Tuple const & lhs, Tuple const & rhs) const { 
    // get<0> = vals, get<1> = other_vals                       

    return (get<0>(lhs) + get<0>(rhs))/(get<1>(lhs) + get<1>(rhs)); 
    } 

}; 


int main(int argc, char ** argv) 
{ 
    const int N = 7; 

    device_vector<int> keys(N); 

    keys[0] = 1; // represents sorted keys                        
    keys[1] = 1; 
    keys[2] = 2; 
    keys[3] = 2; 
    keys[4] = 3; 
    keys[5] = 3; 
    keys[6] = 3; 

    device_vector<int> vals(N); 

    vals[0] = 6; // just some random numbers                       
    vals[1] = 3; 
    vals[2] = 9; 
    vals[3] = 4; 
    vals[4] = 6; 
    vals[5] = 1; 
    vals[6] = 5; 

    device_vector<int> other_vals(N); 

    other_vals[0] = 4; // more randomness                        
    other_vals[1] = 1; 
    other_vals[2] = 3; 
    other_vals[3] = 6; 
    other_vals[4] = 2; 
    other_vals[5] = 5; 
    other_vals[6] = 7; 


    device_vector<int> new_keys(N); 
    device_vector<int> output(N); 

    typedef device_vector<int>::iterator Iterator; 
    thrust::pair<Iterator, Iterator> new_end; 

    thrust::equal_to<int> binary_pred; 

    new_end = thrust::reduce_by_key(keys.begin(), keys.end(), 
            make_zip_iterator(make_tuple(vals.begin(), other_vals.begin())), 
            new_keys.begin(), 
            output.begin(), 
            binary_pred, 
            BinaryTupleOp()); 

    Iterator i = new_keys.begin(); 
    Iterator j = output.begin(); 

    for (; 
     i != new_end.first; 
     i++, j++) { 
    std::cout << "key " << *i << " sum " << *j << endl; 
    } 

    return 0; 
} 

残念ながら、これはerror: no operator "=" matches these operandserror: no suitable conversion function from "InputValueType" to "TemporaryType" exists、およびerror: no suitable conversion function from "const thrust::tuple<int, int, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type, thrust::null_type>" to "int" existsなどのエラーを生成します。私はそれが問題の究極のソースだと思うので、ファンクタのパラメータ型の変種で遊んだことがありますが、無駄です。

回避策として、私はおそらく別々に2つの縮小を分解し、変換を使用して出力ベクトルを作成します。 (おそらく、さまざまなtransform_reduce()コールを一緒にリンクすることを示唆しているかもしれませんが、私は、AFAIKが存在しないreduce_transform()のような逆を欲しそうです)。

答えて

3

一方、私は間違っていますか?

thrust::reduce(またはthrust::reduce_by_key)は、並列削減を実行します。この並列減少には、ペアごとに適用できる縮小演算子が必要です。非常に単純な例を取るために、3つの要素(E1,、およびE3)を減らし、削減操作を定義するために使用するバイナリ操作(bOp)があるとします。推力は、このような何かを行う可能性があります:

バイナリopが結合または要素 E1と単一の一時的な部分的な結果に E2を、「減らす」ために使用され、この結果は、バイナリにフィードバックされ、ある
E1  E2  E3 
\  /
    bOp 
    \   /
      bOp 
      | 
     result 

オペラを要素E3と組み合わせて最終的にresultを生成する。

これは、バイナリopの出力(したがって値出力イテレータの出力タイプ)が入力タイプ(したがって値入力イテレータの入力タイプ)と一致しなければならないことを意味します。

しかし、あなたのバイナリopがこの要件を満たし、またあなたが値を入力するために合格したイテレータの種類を行うと、出力値はありません:

new_end = thrust::reduce_by_key(keys.begin(), keys.end(), 
           make_zip_iterator(make_tuple(vals.begin(), other_vals.begin())), 
    /* dereferencing the above iterator produces an <int, int> tuple */ 
           new_keys.begin(), 
           output.begin(), 
    /* dereferencing the above iterator produces an int */ 
           binary_pred, 
           BinaryTupleOp()); 

私はバイナリOPための上記の一般的な要件を信じている(つまり、値の入力と出力イテレータ種類)thrust docs for this functionように表現されるため:

InputIterator2のVALUE_TYPE OutputIterator2のVALUE_TYPEに変換可能です。

私はあなたがここに示された問題を解決するために二つのアプローチを考えることができ

Oᵢ=Rᵢ/Sᵢ、RᵢとSᵢは、同じキーで縮小ベクトルである、とOᵢは、対応する出力でありますベクター。私はあなたがすでに述べたと思う最初のもの

  1. はちょうどシーケンスが一緒にzip形式2つの値をreduce_by_keyを実行します。これは、単一のreduce_by_keyコールで行うことができます。次に、圧縮された結果シーケンスをthrust::transform呼び出しに渡して、要素ごとの除算を実行します。

  2. スラスト・コールですべてを達成することが必死なら、賢明な出力イテレーターは@ m.sによって行われます。 hereは、出力値に対して変換操作を実行する可能性があります。 (編集:いくつかの研究の後、私はこの方法を削減して使用することができますかわからない)

あなたは上記の提案のいずれかを好きではなかった場合、それは単一でご希望の低減を実現することが可能ですreduce_by_keyを呼び出すと、追加のストレージ(および間違いなくいくつかの無駄な操作)が犠牲になります。基本的な考え方は、2タプルの代わりに3タプルを整理することです。各縮小ステップでは、lhsrhsタプルの対応する(第1および第2)成分を結合(合計)し、これらの結果を出力タプルの第1および第2の位置に格納する。さらに、出力タプルの1番目と2番目の位置の除算の結果として、出力タプルの3番目の位置の値を計算します。ここでは除算を含めてintの操作をしていますので、簡単な結果確認のために入力データを少し変更しました。

$ cat t1143.cu 
#include <thrust/device_vector.h> 
#include <thrust/reduce.h> 
#include <thrust/iterator/zip_iterator.h> 
#include <thrust/iterator/constant_iterator.h> 
#include <iostream> 


using namespace std; 
using namespace thrust; 
typedef tuple<int,int,int> Tuple; 

struct BinaryTupleOp : public thrust::binary_function<const Tuple &, const Tuple &, Tuple> 
{ 
    __host__ __device__ 
    Tuple operator()(const Tuple & lhs, const Tuple & rhs) const { 
    Tuple temp; 
    get<0>(temp) = get<0>(lhs)+get<0>(rhs); 
    get<1>(temp) = get<1>(lhs)+get<1>(rhs); 
    get<2>(temp) = get<0>(temp)/get<1>(temp);         

    return temp; 
    } 

}; 


int main(int argc, char ** argv) 
{ 
    const int N = 7; 

    device_vector<int> keys(N); 

    keys[0] = 1; // represents sorted keys           
    keys[1] = 1; 
    keys[2] = 2; 
    keys[3] = 2; 
    keys[4] = 3; 
    keys[5] = 3; 
    keys[6] = 3; 

    device_vector<int> vals(N); 

    vals[0] = 6; // just some random numbers          
    vals[1] = 3; 
    vals[2] = 8; 
    vals[3] = 4; 
    vals[4] = 5; 
    vals[5] = 5; 
    vals[6] = 5; 

    device_vector<int> other_vals(N); 

    other_vals[0] = 1; // more randomness           
    other_vals[1] = 2; 
    other_vals[2] = 1; 
    other_vals[3] = 2; 
    other_vals[4] = 1; 
    other_vals[5] = 1; 
    other_vals[6] = 1; 


    device_vector<int> new_keys(N); 
    device_vector<int> output(N); 

    device_vector<int> v1(N); 
    device_vector<int> v2(N); 

    thrust::equal_to<int> binary_pred; 

    int rsize = thrust::get<0>(thrust::reduce_by_key(keys.begin(), keys.end(), 
            make_zip_iterator(make_tuple(vals.begin(), other_vals.begin(), thrust::constant_iterator<int>(0))), 
            new_keys.begin(), 
            make_zip_iterator(make_tuple(v1.begin(), v2.begin(), output.begin())), 
            binary_pred, 
            BinaryTupleOp())) - new_keys.begin(); 

    for (int i = 0; i < rsize; i++){ 
    int key = new_keys[i]; 
    int val = output[i]; 
    std::cout << "key " << key << " sum " << val << endl; 
    } 

    return 0; 
} 
$ nvcc -o t1143 t1143.cu 
$ ./t1143 
key 1 sum 3 
key 2 sum 4 
key 3 sum 5 
$ 

他にも、希望の結果を作り上げる方法についてのより良いアイデアがあるかもしれません。

+1

タイムリーで、長く、適切な対応をいただき、ありがとうございました。あなたのポーズされたソリューションは、確かに私が探していたものでした。 私はreduce_by_key' 'のためにRTFDだったとき、私はあなたが親切に強調されていること、この詳細を逃した: は' InputIterator2のVALUE_TYPEは、自然にそれが直接、単一のPODにタプルを割り当てるには少し難しいですが、OutputIterator2のvalue_type.' に変換され、それは私が不注意でやろうとしていたものです。 私は今すぐ元のコードに適切な変更を適用しています。再びありがとう。 –

関連する問題