【问题标题】:tbb: parallel find first elementtbb:并行查找第一个元素
【发布时间】:2011-10-11 13:31:54
【问题描述】:

我遇到了这个问题:

  • 查找列表中满足给定条件的第一个元素。

不幸的是,列表很长(100.000 个元素),并且使用单个线程评估每个元素的条件总共需要大约 30 秒。

有没有办法完全并行化这个问题?我浏览了所有的 tbb 模式,但找不到任何合适的。

更新:出于性能原因,我想在找到项目时尽早停止并停止处理列表的其余部分。这就是为什么我认为我不能使用parallel_whileparallel_do

【问题讨论】:

  • 随着更新,从根本上来说很难:如果您获得加速,您并行评估多个元素,因此不能尽早停止。不过,您可以在之后“很快”停止。
  • @MSalters 停止“不久之后正是我想要的
  • 链表数据结构的数据并行性从根本上来说是困难的。如果你已经有多个起点(即多个迭代器指向列表中),它就可以工作,但即使有这个(很难满足)约束,你也很难干净地并行化这段代码。

标签: c++ design-patterns parallel-processing tbb


【解决方案1】:

对此我对库不太熟悉,但只是大声想,你能不能让一组线程以不同的步幅从不同的凝视点迭代?

假设您决定拥有n 线程(= 内核数或其他),每个线程都应该被指定一个特定的起点,直到 n,所以第一个线程从 begin() 开始,下一个是它比较是begin() + n,等等等等。第二个线程从begin()+1开始,然后它的下一个比较也在n等等。

通过这种方式,您可以让一组线程在列表中并行迭代,迭代本身可能并不昂贵 - 只是比较。没有节点将被多次比较,并且您可以设置一些条件,当任何线程进行匹配时设置,并且所有人都应该在迭代/比较之前检查这个条件..

我认为实现起来非常简单(?)

【讨论】:

  • 请注意,这仅在列表不是链表时才有用。
  • @MooingDuck,怎么会这样?您也可以遍历链表(它不如随机访问迭代器快),但据我了解,比较成本很高...
  • 我想这取决于比较与迭代有多少时间。迭代一个不在缓存中的链表可能会很慢,但这取决于他的情况。
【解决方案2】:

我认为用 TBB 解决这个问题的最好方法是parallel_pipeline

管道中应该(至少)有两个阶段。第一阶段是串行;它只是从列表中读取下一个元素并将其传递到第二阶段。这第二阶段是平行的;它评估给定元素的感兴趣条件。一旦满足条件,第二阶段就会设置一个标志(应该是原子的或用锁保护的)以指示找到了解决方案。第一阶段必须检查这个标志并在找到解决方案后停止读取列表。

由于对少数元素并行执行条件评估,因此找到的元素可能不是列表中第一个合适的元素。如果这很重要,您还需要保留元素的索引,并在找到合适的解决方案时检测其索引是否小于先前已知解决方案的索引(如果有)。

HTH。

【讨论】:

    【解决方案3】:

    好的,我是这样做的:

    1. 将所有元素放入tbb::concurrent_bounded_queue<Element> elements
    2. 创建一个空的tbb::concurrent_vector<Element> results
    3. 创建一个boost::thread_group,并创建几个运行此逻辑的线程:

    并行运行的逻辑:

    Element e;
    while (results.empty() && elements.try_pop(e) {
        if (slow_and_painfull_check(e)) {
             results.push_back(e);
        }
    }
    

    所以当找到第一个元素时,所有其他线程将在下次检查results.empty()时停止处理。

    可能有两个或多个线程正在处理slow_and_painfull_check 返回true 的元素,所以我只是将结果放入一个向量并在并行循环之外处理。

    在线程组中的所有线程都完成后,我检查results 中的所有元素并使用第一个出现的元素。

    【讨论】:

      【解决方案4】:

      您可以查看http://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode.html 以了解并行算法实现。 特别是你需要 find_if 算法http://www.cplusplus.com/reference/algorithm/find_if/

      【讨论】:

      • GCC 并行模式也只适用于随机访问迭代器,这是有充分理由的。
      【解决方案5】:

      我在这里看到了两种并行性机会:在多个线程上评估一个元素,或者在不同的线程上一次评估多个元素。

      没有足够的信息来确定在多个线程上评估一个元素的难度和有效性。如果这很容易,每个元素 30 秒的时间可以减少。

      我认为 TBB 不能完全解决这个问题。列表没有随机访问迭代器、确定何时停止以及保证找到第一个元素存在问题。不过,您可能可以使用范围玩一些游戏来让它发挥作用。

      您也可以使用一些较低级别的线程构造来自己实现这一点,但是有很多地方会返回不正确的结果。为防止此类错误,我建议使用现有算法。您可以将列表转换为数组(或其他具有随机访问迭代器的结构),并使用引用的实验性 libstdc++ 并行模式 find_if 算法 user383522。

      【讨论】:

        【解决方案6】:

        如果是链表,并行搜索不会增加太多速度。然而,链表在缓存中往往表现不佳。如果您有两个线程,您可能会获得微小的性能提升:一个执行 find_first_element,一个简单地遍历列表,确保在第一个线程之前不会超过 X(100?)。第二个线程不进行任何比较,但会确保为第一个线程尽可能地缓存项目。这可能会帮助您节省时间,或者可能没什么不同,或者可能会妨碍您。测试一切。

        【讨论】:

          【解决方案7】:

          您不能将列表转换为平衡树或类似的吗?这样的数据结构更容易并行处理 - 通常你会收回你在第一次平衡时可能付出的开销......例如,如果你编写函数式代码,请查看这篇论文:Balanced trees inhabiting functional parallel programming

          【讨论】:

            【解决方案8】:

            如果您使用 GCC,GNU OpenMP 提供并行标准函数 link

            【讨论】:

              【解决方案9】:

              我从未听说过英特尔 tbb 库,但快速打开并浏览教程后,我找到了 parallel_for,这似乎可以解决问题。

              【讨论】:

              • 抱歉,不:它需要随机访问(技术上是blocked_range)。列表不是。
              猜你喜欢
              • 2021-06-16
              • 2017-12-27
              • 1970-01-01
              • 2013-02-09
              • 1970-01-01
              • 2014-07-04
              • 1970-01-01
              相关资源
              最近更新 更多