使用 thrust::reverse 和 make_zip_iterator+make_tuple

Using thrust::reverse with make_zip_iterator+make_tuple

我在使用 thrust::make_zip_iterator( thrust::make_tuple( )) 类型语法构造的 zip_iterator 上使用 thrust::reverse 函数时遇到奇怪的行为(参见答案来自 JackOLantern 这里是该组合的一个很好的例子。

我希望像下面的示例代码一样反转多个设备向量的一些任意指示的部分。当我通过将它们组合在一起并将它们压缩在一起来一次进行反转时,随之而来的是意想不到的行为。范围的前半部分正确更改为范围后半部分的反转,但是范围的后半部分保持不变。

我一直在以类似的方式使用其他推力函数(sort_by_key、uniqe_by_key、adjacent_difference 等),没有问题。我只是错误地执行了这个,还是出于某种原因这在基本层面上不起作用?我的一个想法是,也许 zip_iterator 不是反向所需的双向。这是真的?我找不到这样说明的文档。

解决方法是单独反转向量,其工作原理如下所示。但是,我怀疑这会降低效率。请注意,在我的实际用例中,我有大小约为 10,000 的向量,并且我正在压缩 3-7 个向量的任意位置以进行操作。

#include <iostream>
#include <ostream>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/tuple.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/sequence.h>
#include <thrust/reverse.h>

int main(){

    // initial host vectors
    const int N=10;
    thrust::host_vector<int> h1(N);
    thrust::host_vector<float> h2(N);

    // fill them
    thrust::sequence( h1.begin(), h1.end(), 0);
    thrust::sequence( h2.begin(), h2.end(), 10., 0.5);

    // print initial contents
    for (size_t i=0; i<N; i++){
        std::cout << h1[i] << " " << h2[i] << std::endl;
    }

    // transfer to device
    thrust::device_vector<int> d1 = h1;
    thrust::device_vector<float> d2 = h2;

    // what chunk to invert
    int iStart = 3; int iEnd = 8;

    // attempt to reverse middle via zip_iterators
    thrust::reverse(
            thrust::make_zip_iterator( thrust::make_tuple( d1.begin()+iStart, d2.begin()+iStart)),
            thrust::make_zip_iterator( thrust::make_tuple( d1.begin()+iEnd, d2.begin()+iEnd))
            );

    // pull back and write out unexpected ordering
    thrust::host_vector<int> temp1 = d1;
    thrust::host_vector<float> temp2 = d2;
    std::cout << "<==========>" << std::endl;
    for (size_t i=0; i<N; i++){
        std::cout << temp1[i] << " " << temp2[i] << std::endl;
    }

    // reset device variables
    d1 = h1;
    d2 = h2;

    // reverse individually
    thrust::reverse( d1.begin()+iStart, d1.begin()+iEnd);
    thrust::reverse( d2.begin()+iStart, d2.begin()+iEnd);

    // pull back and write out the desired ordering
    temp1 = d1;
    temp2 = d2;
    std::cout << "<==========>" << std::endl;
    for (size_t i=0; i<N; i++){
        std::cout << temp1[i] << " " << temp2[i] << std::endl;
    }

    return 0;
}

输出

0 10
1 10.5
2 11
3 11.5
4 12
5 12.5
6 13
7 13.5
8 14
9 14.5
<==========>
0 10
1 10.5
2 11
7 13.5
6 13
5 12.5
6 13
7 13.5
8 14
9 14.5
<==========>
0 10
1 10.5
2 11
7 13.5
6 13
5 12.5
4 12
3 11.5
8 14
9 14.5

评论中来自 Robert Crovella 的信息与最初 post 中给出的解决方法相结合似乎可以回答问题 - 因此,我将在此处将它们结合起来,以便问题可以标记为 "answered." 如果其他人希望 post 其他解决方案,我非常愿意查看它们并移动 "official answer" 复选标记。话虽这么说...

问题的解答分为两部分:

  1. 如果使用旧版本的 CUDA 并且升级是一个选项:升级到最新的 CUDA 版本并且操作应该有效(测试在 CUDA 9.2.148 上工作 - 谢谢罗伯特!)
  2. 如果无法升级到较新版本的 CUDA:单独对向量应用反向以获得与初始 post 中给出的相同结果。为了完整起见,下面复制了只有工作解决方案的代码。

    #include <iostream>
    #include <ostream>
    #include <thrust/device_vector.h>
    #include <thrust/host_vector.h>
    #include <thrust/tuple.h>
    #include <thrust/iterator/zip_iterator.h>
    #include <thrust/sequence.h>
    #include <thrust/reverse.h>
    
    int main(){
    
        // initial host vectors
        const int N=10;
        thrust::host_vector<int> h1(N);
        thrust::host_vector<float> h2(N);
    
        // fill them
        thrust::sequence( h1.begin(), h1.end(), 0);
        thrust::sequence( h2.begin(), h2.end(), 10., 0.5);
    
        // print initial contents
        for (size_t i=0; i<N; i++){
            std::cout << h1[i] << " " << h2[i] << std::endl;
        }
    
        // transfer to device
        thrust::device_vector<int> d1 = h1;
        thrust::device_vector<float> d2 = h2;
    
        // what chunk to invert
        int iStart = 3; int iEnd = 8;
    
        // reverse individually
        thrust::reverse( d1.begin()+iStart, d1.begin()+iEnd);
        thrust::reverse( d2.begin()+iStart, d2.begin()+iEnd);
    
        // pull back and write out the desired ordering
        temp1 = d1;
        temp2 = d2;
        std::cout << "<==========>" << std::endl;
        for (size_t i=0; i<N; i++){
            std::cout << temp1[i] << " " << temp2[i] << std::endl;
        }
    
        return 0;
    }