【问题标题】:Custom iterator works with std::sort but not with tbb::parallel_sort?自定义迭代器适用于 std::sort 但不适用于 tbb::parallel_sort?
【发布时间】:2015-09-23 08:58:49
【问题描述】:

我正在尝试使用tbb::parallel_sort 同时对 2 个数组进行排序。英特尔的文档在这里说https://software.intel.com/en-us/node/506167The requirements on the iterator and sequence are the same as for std::sort.。情况似乎并非如此。我的自定义迭代器在std::sort 下工作得很好,但在tbb::parallel_sort 下会产生编译错误。请看下面的代码:

int main()//needs boost and tbb to compile
{
    int values_size = 6;
    int nums1[] = {5, 8, 7, 89, 56, 4};
    int nums2[] = {2, 1, 1, 4, 9, 2};

    //WORKS!
    std::sort(do_dual_sort.make_iter(nums1, nums2), 
    do_dual_sort.make_iter(nums1+values_size, nums2+values_size),
    do_dual_sort.make_comp_desc(nums1, nums2));

    //DOESN'T COMPILE
    tbb::parallel_sort(do_dual_sort.make_iter(nums1, nums2), 
    do_dual_sort.make_iter(nums1+values_size, nums2+values_size),
    do_dual_sort.make_comp_desc(nums1, nums2));

    for(unsigned int i = 0; i < values_size; i++) cout << "nums1[" << i << "] " << nums1[i] << " | nums2[" << i << "] "  << nums2[i] << "\n";
    return 0;
}

class dual_sort
{
public:
    template <class T, class T2>
    struct helper_type {
        public:
            typedef boost::tuple<typename iterator_traits<T>::value_type, typename iterator_traits<T2>::value_type> value_type;
            typedef boost::tuple<typename iterator_traits<T>::value_type&, typename iterator_traits<T2>::value_type&> ref_type;
    };

    template <typename T1, typename T2>
    class dual_iterator : public boost::iterator_facade<dual_iterator<T1, T2>,
                                                        typename helper_type<T1, T2>::value_type,
                                                        boost::random_access_traversal_tag,
                                                        typename helper_type<T1, T2>::ref_type> {
    public:
        explicit dual_iterator(T1 iter1, T2 iter2) : mIter1(iter1), mIter2(iter2) {}
        typedef typename iterator_traits<T1>::difference_type difference_type;
    private:
        void increment() { ++mIter1; ++mIter2; }
        void decrement() { --mIter1; --mIter2; }
        bool equal(dual_iterator const& other) const { return mIter1 == other.mIter1; }
        typename helper_type<T1, T2>::ref_type dereference() const { return (typename helper_type<T1, T2>::ref_type(*mIter1, *mIter2)); }
        difference_type distance_to(dual_iterator const& other) const { return other.mIter1 - mIter1; }
        void advance(difference_type n) { mIter1 += n; mIter2 += n; }

        T1 mIter1;
        T2 mIter2;
        friend class boost::iterator_core_access;
    };

    template <typename T1, typename T2>
    dual_iterator<T1, T2> make_iter(T1 t1, T2 t2) { return dual_iterator<T1, T2>(t1, t2); }

    template <class T1, class T2> struct iter_comp_desc {
        typedef typename helper_type<T1, T2>::value_type T;
        bool operator()(const T& t1, const T& t2) const { return get<0>(t1) > get<0>(t2); }
        bool operator()(const char*& t1, const char*& t2) const { return strcmp(get<0>(t1), get<0>(t2)) == 1; }
    };

    template <class T1, class T2> iter_comp_desc<T1, T2> make_comp_desc(T1 t1, T2 t2) { return iter_comp_desc<T1, T2>(); }

} do_dual_sort;

我得到的编译错误是:

error C2512: 'dual_sort::dual_iterator<T1,T2>' : no appropriate default constructor available
with
[
    T1=int *,
    T2=int *
]
tbb44_20150728oss\include\tbb/parallel_sort.h(201) : see reference to function template instantiation 'void tbb::internal::parallel_quick_sort<RandomAccessIterator,Compare>(RandomAccessIterator,RandomAccessIterator,const Compare &)' being compiled
with
[
    RandomAccessIterator=dual_sort::dual_iterator<int *,int *>,
    Compare=dual_sort::iter_comp_desc<int *,int *>
]
main.cpp(1125) : see reference to function template instantiation 'void tbb::parallel_sort<dual_sort::dual_iterator<T1,T2>,dual_sort::iter_comp_desc<T1,T2>>(RandomAccessIterator,RandomAccessIterator,const Compare &)' being compiled
with
[
    T1=int *,
    T2=int *,
    RandomAccessIterator=dual_sort::dual_iterator<int *,int *>,
    Compare=dual_sort::iter_comp_desc<int *,int *>
]

编辑:我使用的编译器是Visual Studio 2012。您可以尝试用std 替换一些增强功能,以使其在g++ 上工作。

【问题讨论】:

  • 你想要什么样的关注?您已经吸引了 2 位 TBB 开发人员的注意。但是您的代码即使使用纯 boost/C++ 也无法工作,请修复它的基础知识,然后询问与 tbb 相关的问题,如果还有什么超出我的建议。
  • @Anton :感谢您到目前为止的帮助。老实说,我不是一个经验丰富的开发人员。如果您能帮助我使我的代码与 TBB 并行排序一起工作,我将不胜感激。这是我找到代码的地方:stackoverflow.com/a/18905661/3065448
  • 您确定您的任务是对压缩容器进行排序吗?就像那个答案假设
  • @Anton:我的任务是使用tbb::parallel_sort 对两个数组int *arr 进行排序。如果您知道更好的方法,请告诉我。
  • 所以,它们都是同一类型,对吧?在这种情况下,您不需要这些繁重的元组/模板魔术

标签: c++ iterator tbb


【解决方案1】:

对于 RandomAccessIterator,reference 必须是对 value_type 的引用。它不能是引用元组。

因此,您的双迭代器不是有效的 RandomAccessIterator。

许多算法仍然有效,但这并不能使您的代码有效。

相同的要求并不意味着任何适用于std::sort 的给定实现也适用于tbb::parallel_sortstd::sort 的给定实现不必强制执行标准。

不管文档如何,如果实现不适用于您的代码,它也不会适用于您的代码。

最简单的方法可能是在原始数组中创建一个伪索引对(或迭代器)数组,然后对其进行排序。您只需要正确覆盖&lt;

【讨论】:

  • 是的,但为了做到这一点,我需要使用我没有的额外内存
  • @We'reAllMadHere 垃圾。你有足够的额外内存。如何控制您正在使用的任何代码是否未分配?
  • @Yakk 感谢您的帮助,尽管我并没有真正解决问题,但您帮了我很多,这就是为什么我会给您赏金
【解决方案2】:

tbb/parallel_sort.h 中的 class quick_sort_range 包含 RandomAccessIterator begin; 成员,该成员在其构造函数中复制初始化并默认初始化,然后在另一个构造函数中分配。因此,它需要默认和可复制构造和可分配的迭代器。

因此,TBB 文档声称与 std::sort 相同的要求是不正确的,因为后者只需要随机访问迭代器,are not required 是可分配的,而 TBB 实现需要它用于版本

可以修复默认可构造和可分配的要求,但可能会保留移动或可复制构造(使文档中的声明正确)。您可以在TBB Forum 上报告此问题。

据我所知,您可以安全地将默认和复制构造函数和赋值运算符添加到代码中,以使用 tbb::parallel_sort 编译它。

这是带有您的 sn-p 的在线编译器:http://coliru.stacked-crooked.com/a/47dafd091d36a9c4

【讨论】:

  • 很遗憾,你错了。声称相同要求的文档可能是正确的(我不明白这是如何显然不是这样)。请参阅下面 Yakk 的出色回答。
  • @Walter std::sort 要求迭代器是可分配的吗?我没有找到这样的要求。所以我的回答是正确的
  • @Anton,说 std::sort 相同的要求并不是很有帮助,因为几乎没有人知道确切这些要求是什么。即使某个iteratorstd::sort 一起用于某个实现,这并不意味着它满足这些要求(std::sort 的实现可能不会使用所有形式要求)。所以文档应该更明确——但众所周知,tbb 文档是草率的。
  • @Walter 我同意并对 TBB 开发人员说同样的话。但我的意思是我的回答仍然正确,所以请删除您的评论并投反对票
  • @Anton -1 您链接到的页面表明随机访问迭代器概念不需要赋值,并声明它们需要赋值 (b = a)。事实上,所有的迭代器都必须是默认的、可复制的以及可复制的。 (一个默认构造的迭代器称为单数,顺便说一句。)所以 Yakk 是正确的,这是关于 std::sort 的特定实现,而不是标准要求。 TBB 文档也是正确的,因为它指的是标准,而不是特定的实现。至少当前的文档还明确提到了随机访问迭代器的概念。
【解决方案3】:

我无法编译基本用例(即使用std::sort)。因此,该代码适用于在一种特定的编译器案例中成功编译。

顺便说一句,RandomAccessIterator 也满足ForwardIterator 的要求。如果我们查看ForwardIterator 的要求,我们会发现它应该是DefaultConstructible。 (参见最新的C++ standard's working draft 之一中的第 24.2.5 节前向迭代器,第 (1.2) 节)

【讨论】:

  • 关键是文档声称the same requirementsstd::sort 一样,但一个简单的测试揭示了不同之处。值得清楚地记录要求(和/或为tbb::parallel_for放宽它们)
【解决方案4】:

第一个错误是“没有合适的默认构造函数可用..”,但是 Iterators must be default constructible.

在一种算法或另一种算法中是否使用默认构造无关紧要,要求仍然成立。所以std::sort 编译成功和tbb::parallel_sort 失败并不意味着The requirements on the iterator and sequence are the same as for std::sort 不正确;这只是意味着您正在使用的std::sort 的实现不依赖于该先决条件。

如果您实现迭代器以符合两种算法所依赖的标准子集,那么根据文档,它应该可以工作。

【讨论】:

    猜你喜欢
    • 2019-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 2019-01-04
    • 2020-10-22
    • 2012-10-30
    • 1970-01-01
    相关资源
    最近更新 更多