如何使用CUDA推力通过索引列表从矩阵中收集行
这似乎是一个简单的问题,但我就是想不出一个优雅的方式来做到这一点与CUDA推力 我有一个二维矩阵NxM和一个大小为L的所需行索引向量,它是所有行的子集(即L如何使用CUDA推力通过索引列表从矩阵中收集行,cuda,thrust,Cuda,Thrust,这似乎是一个简单的问题,但我就是想不出一个优雅的方式来做到这一点与CUDA推力 我有一个二维矩阵NxM和一个大小为L的所需行索引向量,它是所有行的子集(即L
非常感谢您的帮助。我认为没有办法使用推力来完成此操作,但是,由于操作会受到内存限制,因此编写一个以尽可能高的性能执行此操作的内核应该很容易。只需创建与向量中的索引数量相同的线程。让每个线程计算一行的源地址和目标地址,然后使用
memcpy()
复制该行
您可能还需要仔细考虑是否可以设置后续处理步骤来就地访问这些行,从而避免了整个、昂贵的“压缩”操作,只清理内存。即使对行的寻址变得稍微复杂一些(可能需要额外的内存查找和乘法),总体性能可能会更好。
我认为没有办法使用推力来实现这一点,但是,因为操作会受到内存限制,编写一个以尽可能高的性能执行此操作的内核应该很容易。只需创建与向量中的索引数量相同的线程。让每个线程计算一行的源地址和目标地址,然后使用memcpy()
复制该行
您可能还需要仔细考虑是否可以设置后续处理步骤来就地访问这些行,从而避免了整个、昂贵的“压缩”操作,只清理内存。即使对行进行寻址变得稍微复杂一些(可能需要额外的内存查找和乘法),总体性能可能会更好。
您所问的似乎是一个非常直接的流压缩问题,在使用推力时没有任何特别的问题,但有一些曲折。为了选择要复制的行,您需要具有流压缩算法可以使用的模具或密钥。需要使用要复制的行列表通过搜索或选择操作来构造 执行此操作的一个示例过程如下所示:计数迭代器
和变换迭代器
,可以结合使用推力::复制_if
使用模具在输入矩阵上执行流压缩#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);
}
}
(标准免责声明:使用风险自负)
执行参数的选择可以变得更加华丽,但除此之外,这应该是所需的全部。如果您的行非常小,您可能希望研究使用每行扭曲而不是块(因此一个块复制多行)。如果输出行数超过65535行,则需要使用二维栅格,或修改代码使每个块执行多行。但是,与基于推力的解决方案一样,这应该可以让您开始了。您所问的似乎是一个非常直接的流压缩问题,使用推力并没有任何特殊问题,但有一些曲折。为了选择要复制的行,您需要具有流压缩算法可以使用的模具或密钥。需要使用要复制的行列表通过搜索或选择操作来构造 执行此操作的一个示例过程如下所示:
计数迭代器
和变换迭代器
,可以结合使用推力::复制_if
使用模具在输入矩阵上执行流压缩#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);
}
}
(标准免责声明:使用风险自负)
执行参数的选择可以变得更加华丽,但除此之外,这应该是所需的全部。如果您的行非常小,您可能希望研究使用每行扭曲而不是块(因此一个块复制多行)。如果输出行数超过65535行,则需要使用二维网格或修改代码
const int N = 7, M = 5;
float L_host[] = {3, 6, 4, 1};
int szL = sizeof(L_host) / sizeof(float);
// generate random NxM matrix with cuComplex data
array A = randu(N, M, c32);
// array used to index rows
array L(szL, 1, L_host);
print(A);
print(L);
array B = A(L,span); // copy selected rows of A
print(B);
A =
0.7402 + 0.9210i 0.6814 + 0.2920i 0.5786 + 0.5538i 0.2133 + 0.4131i 0.7305 + 0.9400i
0.0390 + 0.9690i 0.3194 + 0.8109i 0.3557 + 0.7229i 0.0328 + 0.5360i 0.8432 + 0.6116i
0.9251 + 0.4464i 0.1541 + 0.4452i 0.2783 + 0.6192i 0.7214 + 0.3546i 0.2674 + 0.0208i
0.6673 + 0.1099i 0.2080 + 0.6110i 0.5876 + 0.3750i 0.2527 + 0.9847i 0.8331 + 0.7218i
0.4702 + 0.5132i 0.3073 + 0.4156i 0.2405 + 0.4148i 0.9200 + 0.1872i 0.6087 + 0.6301i
0.7762 + 0.2948i 0.2343 + 0.8793i 0.0937 + 0.6326i 0.1820 + 0.5984i 0.5298 + 0.8127i
0.7140 + 0.3585i 0.6462 + 0.9264i 0.2849 + 0.7793i 0.7082 + 0.0421i 0.0593 + 0.4797i
L = (row indices)
3.0000
6.0000
4.0000
1.0000
B =
0.6673 + 0.1099i 0.2080 + 0.6110i 0.5876 + 0.3750i 0.2527 + 0.9847i 0.8331 + 0.7218i
0.7140 + 0.3585i 0.6462 + 0.9264i 0.2849 + 0.7793i 0.7082 + 0.0421i 0.0593 + 0.4797i
0.4702 + 0.5132i 0.3073 + 0.4156i 0.2405 + 0.4148i 0.9200 + 0.1872i 0.6087 + 0.6301i
0.0390 + 0.9690i 0.3194 + 0.8109i 0.3557 + 0.7229i 0.0328 + 0.5360i 0.8432 + 0.6116i
float *g_data = 0, *g_data2 = 0;
int g_N = 2000, g_M = 2000, // matrix of size g_N x g_M
g_L = 400; // copy g_L rows
void af_test()
{
array A(g_N, g_M, (cuComplex *)g_data, afDevicePointer);
array L(g_L, 1, g_data2, afDevicePointer);
array B = (A(L, span));
std::cout << "sz: " << B.elements() << "\n";
}
int main()
{
// input matrix N x M of cuComplex
array in = randu(g_N, g_M, c32);
g_data = (float *)in.device< cuComplex >();
// generate unique row indices
array in2 = setunique(floor(randu(g_L) * g_N));
print(in2);
g_data2 = in2.device<float>();
const int N_ITERS = 30;
try {
info();
af::sync();
timer::tic();
for(int i = 0; i < N_ITERS; i++) {
af_test();
}
af::sync();
printf("af: %.5f seconds\n", timer::toc() / N_ITERS);
} catch (af::exception& e) {
fprintf(stderr, "%s\n", e.what());
}
in.unlock();
in2.unlock();
}