あなたが求めているのは、かなり単純なストリーム圧縮の問題のように思えます。推力でそれを行うのに特に問題はありませんが、いくつかのひねりがあります。コピーする行を選択するには、ストリーム圧縮アルゴリズムで使用できるステンシルまたはキーが必要です。これは、コピーする行のリストを使用して検索または選択操作によって構築する必要があります。
これを行う手順の例は、次のようになります。
- 入力行列の任意のエントリの行番号を返す反復子を作成します。スラストには非常に便利な機能が
counting_iterator
あり、transform_iterator
これを組み合わせてこれを行うことができます
- その行番号反復子の検索を実行して、コピーする行のリストに一致するエントリを見つけます。
thrust::binary search
これに使用できます。検索により、ストリーム圧縮操作のステンシルが得られます
- ステンシルを使用
thrust::copy_if
して入力行列でストリーム圧縮を実行するために使用します。
多くの作業と中間ステップのように思えますが、カウントと変換の反復子は実際には中間デバイス ベクトルを生成しません。必要な唯一の中間ストレージはステンシル配列です。これはブール値 (つまり m*n バイト) にすることができます。
コードの完全な例:
#include <thrust/copy.h>
#include <thrust/binary_search.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/device_vector.h>
#include <cstdio>
struct div_functor : public thrust::unary_function<int,int>
{
int m;
div_functor(int _m) : m(_m) {};
__host__ __device__
int operator()(int x) const
{
return x / m;
}
};
struct is_true
{
__host__ __device__
bool operator()(bool x) { return x; }
};
int main(void)
{
// dimensions of the problem
const int m=20, n=5, l=4;
// Counting iterator for generating sequential indices
// Sample matrix containing 0...(m*n)
thrust::counting_iterator<float> indices(0.f);
thrust::device_vector<float> in_matrix(m*n);
thrust::copy(indices, indices+(m*n), in_matrix.begin());
// device vector contain rows to select
thrust::device_vector<int> select(l);
select[0] = 1;
select[1] = 4;
select[2] = 9;
select[3] = 16;
// construct device iterator supplying row numbers via a functor
typedef thrust::counting_iterator<int> counter;
typedef thrust::transform_iterator<div_functor, counter> rowIterator;
rowIterator rows_begin = thrust::make_transform_iterator(thrust::make_counting_iterator(0), div_functor(n));
rowIterator rows_end = rows_begin + (m*n);
// constructor a stencil array which indicates which entries will be copied
thrust::device_vector<bool> docopy(m*n);
thrust::binary_search(select.begin(), select.end(), rows_begin, rows_end, docopy.begin());
// use stream compaction on the matrix with the stencil array
thrust::device_vector<float> out_matrix(l*n);
thrust::copy_if(in_matrix.begin(), in_matrix.end(), docopy.begin(), out_matrix.begin(), is_true());
for(int i=0; i<(l*n); i++) {
float val = out_matrix[i];
printf("%i %f\n", i, val);
}
}
(通常の免責事項:自己責任で使用してください)
私がする唯一のコメントについては、copy_if
直接使用できるバイナリステンシルが既にあることを考えると、呼び出しの述語が少し冗長に感じるということですが、バイナリ ステンシルを直接。同様に、ストリーム圧縮呼び出しで行のリストを直接使用する賢明な方法を思いつきませんでした。これを推力で行うもっと効率的な方法があるかもしれませんが、少なくともこれで始めることができます。
あなたのコメントから、スペースが狭く、バイナリ検索とステンシル作成の追加のメモリ オーバーヘッドがアプリケーションにとって法外なものであるようです。その場合、Roger Dahl の回答に対するコメントで提供したアドバイスに従い、代わりにカスタム コピー カーネルを使用します。スラスト デバイス ベクトルは、カーネルに直接渡すことができるポインターにキャストできるthrust::raw_pointer_cast
ため ( )、既存のスラスト コードに干渉する必要はありません。行ごとにスレッドのブロックを使用してコピーすることをお勧めします。これにより、読み取りと書き込みの合体が可能になり、thrust::copy
行ごとに使用するよりもはるかに優れたパフォーマンスが得られます。非常に単純な実装は次のようになります (推力の例のほとんどを再利用します)。
#include <thrust/copy.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/device_vector.h>
#include <cstdio>
__global__
void rowcopykernel(const float *in, float *out, const int *list, const int m, const int n, const int l)
{
__shared__ const float * inrowp;
__shared__ float * outrowp;
if (threadIdx.x == 0) {
inrowp = (blockIdx.x < l) ? in + (n*list[blockIdx.x]) : 0;
outrowp = out + (n*blockIdx.x);
}
__syncthreads();
for(int i=threadIdx.x; (inrowp != 0) && (i<n); i+=blockDim.x) {
*(outrowp+i) = *(inrowp+i);
}
}
int main(void)
{
// dimensions of the problem
const int m=20, n=5, l=4;
// Sample matrix containing 0...(m*n)
thrust::counting_iterator<float> indices(0.f);
thrust::device_vector<float> in_matrix(m*n);
thrust::copy(indices, indices+(m*n), in_matrix.begin());
// device vector contain rows to select
thrust::device_vector<int> select(l);
select[0] = 1;
select[1] = 4;
select[2] = 9;
select[3] = 16;
// Output matrix
thrust::device_vector<float> out_matrix(l*n);
// raw pointer to thrust vectors
int * selp = thrust::raw_pointer_cast(&select[0]);
float * inp = thrust::raw_pointer_cast(&in_matrix[0]);
float * outp = thrust::raw_pointer_cast(&out_matrix[0]);
dim3 blockdim = dim3(128);
dim3 griddim = dim3(l);
rowcopykernel<<<griddim,blockdim>>>(inp, outp, selp, m, n, l);
for(int i=0; i<(l*n); i++) {
float val = out_matrix[i];
printf("%i %f\n", i, val);
}
}
(標準免責事項: 自己責任で使用してください)。
実行パラメータの選択はより手の込んだものにすることができますが、それ以外の場合は、それで十分です。行が非常に小さい場合は、ブロックではなく行ごとにワープを使用して調査することをお勧めします (したがって、1 つのブロックが複数の行をコピーします)。出力行が 65535 を超える場合は、2D グリッドを使用するか、コードを変更して各ブロックが複数の行を実行するようにする必要があります。しかし、推力ベースのソリューションと同様に、これで始められるはずです。