如何使用CUDA推力通过索引列表从矩阵中收集行

如何使用CUDA推力通过索引列表从矩阵中收集行,cuda,thrust,Cuda,Thrust,这似乎是一个简单的问题,但我就是想不出一个优雅的方式来做到这一点与CUDA推力 我有一个二维矩阵NxM和一个大小为L的所需行索引向量,它是所有行的子集(即L

这似乎是一个简单的问题,但我就是想不出一个优雅的方式来做到这一点与CUDA推力

我有一个二维矩阵NxM和一个大小为L的所需行索引向量,它是所有行的子集(即L
  • 从原始NxM矩阵复制所需行以形成新矩阵LxM的最有效方法是什么
  • 是否可以为原始NxM矩阵创建一个迭代器,该迭代器将只对属于所需行的元素进行解引用

  • 非常感谢您的帮助。

    我认为没有办法使用推力来完成此操作,但是,由于操作会受到内存限制,因此编写一个以尽可能高的性能执行此操作的内核应该很容易。只需创建与向量中的索引数量相同的线程。让每个线程计算一行的源地址和目标地址,然后使用
    memcpy()
    复制该行


    您可能还需要仔细考虑是否可以设置后续处理步骤来就地访问这些行,从而避免了整个、昂贵的“压缩”操作,只清理内存。即使对行的寻址变得稍微复杂一些(可能需要额外的内存查找和乘法),总体性能可能会更好。

    我认为没有办法使用推力来实现这一点,但是,因为操作会受到内存限制,编写一个以尽可能高的性能执行此操作的内核应该很容易。只需创建与向量中的索引数量相同的线程。让每个线程计算一行的源地址和目标地址,然后使用
    memcpy()
    复制该行


    您可能还需要仔细考虑是否可以设置后续处理步骤来就地访问这些行,从而避免了整个、昂贵的“压缩”操作,只清理内存。即使对行进行寻址变得稍微复杂一些(可能需要额外的内存查找和乘法),总体性能可能会更好。

    您所问的似乎是一个非常直接的流压缩问题,在使用推力时没有任何特别的问题,但有一些曲折。为了选择要复制的行,您需要具有流压缩算法可以使用的模具或密钥。需要使用要复制的行列表通过搜索或选择操作来构造

    执行此操作的一个示例过程如下所示:

  • 构造一个迭代器,返回输入矩阵中任何项的行号。推力有一个非常有用的
    计数迭代器
    变换迭代器
    ,可以结合使用
  • 对该行号迭代器执行搜索,以查找与要复制的行列表匹配的条目<代码>推力::二进制搜索可用于此。搜索将生成流压缩操作的模板
  • 使用
    推力::复制_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);
        }
    }
    
    (标准免责声明:使用风险自负)


    执行参数的选择可以变得更加华丽,但除此之外,这应该是所需的全部。如果您的行非常小,您可能希望研究使用每行扭曲而不是块(因此一个块复制多行)。如果输出行数超过65535行,则需要使用二维栅格,或修改代码使每个块执行多行。但是,与基于推力的解决方案一样,这应该可以让您开始了。

    您所问的似乎是一个非常直接的流压缩问题,使用推力并没有任何特殊问题,但有一些曲折。为了选择要复制的行,您需要具有流压缩算法可以使用的模具或密钥。需要使用要复制的行列表通过搜索或选择操作来构造

    执行此操作的一个示例过程如下所示:

  • 构造一个迭代器,返回输入矩阵中任何项的行号。推力有一个非常有用的
    计数迭代器
    变换迭代器
    ,可以结合使用
  • 对该行号迭代器执行搜索,以查找与要复制的行列表匹配的条目<代码>推力::二进制搜索可用于此。搜索将生成流压缩操作的模板
  • 使用
    推力::复制_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);
        }
    }
    
    (标准免责声明:使用风险自负)

    执行参数的选择可以变得更加华丽,但除此之外,这应该是所需的全部。如果您的行非常小,您可能希望研究使用每行扭曲而不是块(因此一个块复制多行)。如果输出行数超过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();
    }