微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Thrust:并行计算多个segment的set_difference

如何解决Thrust:并行计算多个segment的set_difference

我编写了一些代码来使用推力计算每段的 set_difference。这个想法是使用额外的数组来指示哪个元素属于哪个段,以及一个自定义比较器。 这会产生正确的输出集,但如果输入段为空,则输出段大小是错误的。 Reduce-by-key 用于使用每个元素的输出段标识符来计算每个段的元素数量。如果输入段为空,则相应的段 id 不会出现在输出段 id 中,从而导致错误结果。

正确:

segmentsizesLeft {4,3}
segmentIdsLeft {0,1,1}

outputsegmentIds {0,1}
outputsegmentsizes {3,1}

错误

segmentsizesLeft {0,3}
segmentIdsLeft {1,1}

outputsegmentIds {1}
outputsegmentsizes {1,uninitialized} //should be {0,1}

如何解决

#include <thrust/set_operations.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/tuple.h>
#include <thrust/reduce.h>
#include <thrust/iterator/constant_iterator.h>
#include <thrust/iterator/discard_iterator.h>

#include <iostream>

int main(){

    //compute set_difference(left,right),per segment
    //output: contiguous range of remaining elements,array of output segment sizes

#if 1    
    //correct result
    int numLeft = 7;
    int dataLeft[7]{0,2,3,4};
    int segmentsizesLeft[2]{4,3};
    int psLeft[3]{0,4,7};
    int segmentIdsLeft[7]{0,1};
#else 
    //wrong result. empty input segment is no longer present in output segment sizes
    int numLeft = 3;
    int dataLeft[3]{1,4};
    int segmentsizesLeft[2]{0,3};
    int segmentIdsLeft[3]{1,1};
#endif    
 
    int dataRight[7]{2,4};
    int segmentsizesRight[2]{1,2};
    int psRight[3]{0,3};
    int segmentIdsRight[3]{0,1};

    auto first1 = thrust::make_zip_iterator(thrust::make_tuple(&segmentIdsLeft[0],&dataLeft[0]));
    auto last1 = thrust::make_zip_iterator(thrust::make_tuple(&segmentIdsLeft[0] + numLeft,&dataLeft[0] + numLeft));

    auto first2 = thrust::make_zip_iterator(thrust::make_tuple(&segmentIdsRight[0],&dataRight[0]));
    auto last2 = thrust::make_zip_iterator(thrust::make_tuple(&segmentIdsRight[0] + 3,&dataRight[0] + 3));

    int segmentIdsOutput[7];
    int dataOutput[7];
    int segmentsizesOutput[2];

    auto output = thrust::make_zip_iterator(thrust::make_tuple(&segmentIdsOutput[0],&dataOutput[0]));

    auto comp = [](const auto& t1,const auto& t2){
        const int idl = thrust::get<0>(t1);
        const int idr = thrust::get<0>(t2);

        if(idl < idr) return true;
        if(idl > idr) return false;

        return thrust::get<1>(t1) < thrust::get<1>(t2);
    };

    auto outputend = thrust::set_difference(first1,last1,first2,last2,output,comp);

    int outputsize = thrust::distance(output,outputend);

    thrust::reduce_by_key(&segmentIdsOutput[0],&segmentIdsOutput[0] + outputsize,thrust::make_constant_iterator(1),thrust::make_discard_iterator(),&segmentsizesOutput[0]
    );

    std::cerr << "raw data output: ";
    for(int i = 0; i < outputsize; i++){
        std::cerr << " " << dataOutput[i];
    }
    std::cerr << "\n";

    std::cerr << "result segment sizes: ";
    for(int i = 0; i < 2; i++){
        std::cerr << " " << segmentsizesOutput[i];
    }
    std::cerr << "\n";
}

解决方法

以下方法正在起作用。不是丢弃唯一的段 ID 并将减少的值存储在输出数组中,而是将两个输出都保存到临时存储中。然后,将输出数组初始化为零以考虑任何空段,并使用键值对在正确位置设置段大小。

int uniqueIds[7];
int reducedCounts[7];

int numUnique = thrust::distance(
    &uniqueIds[0],thrust::reduce_by_key(
        &segmentIdsOutput[0],&segmentIdsOutput[0] + outputsize,thrust::make_constant_iterator(1),&uniqueIds[0],&reducedCounts[0]
    ).first
);

thrust::fill(&segmentsizesOutput[0],&segmentsizesOutput[0] + 2,0);

thrust::for_each_n(
    thrust::make_zip_iterator(thrust::make_tuple(&uniqueIds[0],&reducedCounts[0])),numUnique,[&] (auto tup){
        segmentsizesOutput[thrust::get<0>(tup)] = thrust::get<1>(tup);
    }
);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。