C++ 如何有效地将数据从2D主机阵列(带填充)复制到1D设备阵列,并删除CUDA中的原始填充?

C++ 如何有效地将数据从2D主机阵列(带填充)复制到1D设备阵列,并删除CUDA中的原始填充?,c++,cuda,C++,Cuda,主机上有一个带填充的2D列主数组,例如: |1 4 7| |2 5 8| A_h = |3 6 9| |x x x| |x x x| 我想将数据作为1D阵列复制到设备内存: {1, 2, 3, 4, 5, 6, 7, 8, 9} //preferred 或 使用CUDA和/或推力实现这一目标的最快有效方法是什么 编辑:我跟随Robert的评论删除了使用推力时的循环,但代码只能复制第一列

主机上有一个带填充的2D列主数组,例如:

        |1   4   7|
        |2   5   8|
 A_h =  |3   6   9|
        |x   x   x|
        |x   x   x|
我想将数据作为1D阵列复制到设备内存:

{1, 2, 3, 4, 5, 6, 7, 8, 9} //preferred

使用CUDA和/或推力实现这一目标的最快有效方法是什么

编辑:我跟随Robert的评论删除了使用推力时的循环,但代码只能复制第一列。如何在不使用循环的情况下使其适用于整个阵列

thrust::counting_iterator<int> first(0);
thrust::counting_iterator<int> last = first + rows;
thrust::device_vector<real_type> A_d(rows * cols);
thrust::copy(thrust::make_permutation_iterator(A_h, first), 
     thrust::make_permutation_iterator(A_h, last), A_d.begin());
推力::首先计算迭代器(0);
推力::计数迭代器最后一次=第一次+行;
推力:设备矢量A\u d(行*cols);
推力::复制(推力::生成置换迭代器(A_h,第一),
推力::生成置换迭代器(A_h,last),A_d.begin();

如果用例只是将一个较大源的子集复制到一个较小的目的地,而这个目的地没有跨步(如此连续),那么一个带有谓词的条件复制可能是最简单的方法(我猜gather也会起作用)。大概是这样的:

#include <vector>
#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/iterator/counting_iterator.h>

struct indexer
{
    int lda0;
    int lda1;

    indexer() = default;

    __device__ __host__
        indexer(int l0, int l1) : lda0(l0), lda1(l1) {};

    __device__ __host__
        bool operator()(int x) {
            int r = x % lda0;
            return (r < lda1);
        };
};

int main()
{
    const int M0 = 5, N=3;
    const int M1 = 3;
    const int  len1 = M1*N;

    {
        std::vector<int> data{ 1, 2, 3, -1, -1, 4, 5, 6, -1, -1, 7, 8, 9, -1, -1 };
        thrust::device_vector<int> ddata = data;
        thrust::device_vector<int> doutput(len1);

        indexer pred(M0, M1);

        thrust::counting_iterator<int> idx(0);
        thrust::copy_if(ddata.begin(), ddata.end(), idx, doutput.begin(), pred);

        for(int i=0; i<len1; i++) {
            int val = doutput[i];
            std::cout << i << " " << val << std::endl;
        }
    }

    return 0;
}

如果您想要更一般的东西(如此快速的输入和输出),那么您可能会使用与
scatter\u If
相同的想法。正如注释中所指出的,这可以通过
cudaMemcpy2D
或一个拷贝内核来完成。

没有办法直接完成。最简单的方法是使用
cudaMemcpy2D
,其中
cuda
标签上有许多问题。最快的方法可能是在主机上重新格式化阵列,然后执行“普通”cudaMemcpy(或推力复制)。如果不知道数组和填充的确切大小,就不可能确定什么是最快的方法。另一个常见的建议是使用
cudaMemcpy
按原样发送数组,然后在设备代码中重新格式化(使用内核,或者按原样使用)。嗨,罗伯特,谢谢你的回复。假设矩阵A的大小为N×M,主机填充为64字节。你能详细说明一下“然后用设备代码重新格式化”吗?我已经知道你的矩阵的大小是N×M。如果你写的是纯主机代码,你想把这个矩阵从填充的重新格式化为未添加的,你知道怎么做吗?如果是这样的话,那么编写一个CUDA内核来做同样的事情。这将是一个非常简单的CUDA内核。您正在将数据从填充的输入数组复制到未添加的输出数组。您可以在主机端或设备端执行此操作。根据N和M的实际值,一种方法可能优于另一种方法。如果没有基准测试,可能无法确定。是的,我已经编写了一个代码,使用推力删除填充,但它与宿主代码非常相似:循环每列并将数据复制到行。我以为你在回复中提出了一个新的算法来“在设备代码中重新格式化”,这样我就可以学习了。谢谢。理想情况下,您永远不会在推力代码中编写循环。通过
置换迭代器
和对
推力::复制
的单个调用,可以完成从填充到未添加的推力复制操作。不需要循环。在您的回答中,如果有5个参数(并且没有策略),请复制。这是个错误吗?不,不是。向下滚动…明白了吗
#include <vector>
#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/iterator/counting_iterator.h>

struct indexer
{
    int lda0;
    int lda1;

    indexer() = default;

    __device__ __host__
        indexer(int l0, int l1) : lda0(l0), lda1(l1) {};

    __device__ __host__
        bool operator()(int x) {
            int r = x % lda0;
            return (r < lda1);
        };
};

int main()
{
    const int M0 = 5, N=3;
    const int M1 = 3;
    const int  len1 = M1*N;

    {
        std::vector<int> data{ 1, 2, 3, -1, -1, 4, 5, 6, -1, -1, 7, 8, 9, -1, -1 };
        thrust::device_vector<int> ddata = data;
        thrust::device_vector<int> doutput(len1);

        indexer pred(M0, M1);

        thrust::counting_iterator<int> idx(0);
        thrust::copy_if(ddata.begin(), ddata.end(), idx, doutput.begin(), pred);

        for(int i=0; i<len1; i++) {
            int val = doutput[i];
            std::cout << i << " " << val << std::endl;
        }
    }

    return 0;
}
$ nvcc -arch=sm_52 -std=c++11 -o subset subset.cu
$ ./subset 
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9