【问题标题】:std::set::insert, how bad can I hint?std::set::insert,我能暗示多糟糕?
【发布时间】:2011-07-08 16:02:19
【问题描述】:

我正在将大量的 std::pair<int, int> 插入到 std::set 中,而且花费的时间比我想要的要长。当我编写代码时,我想如果结果是瓶颈,我会考虑使用插入的提示迭代器形式。好吧,现在它已被分析并且它一个瓶颈。所以我想使用迭代器提示。

但是,我并不总是知道插入对子的好位置。我通常将它们分批插入(在这种情况下,一个批次大约是总输入大小的 0.01%,包括重复项)增加的集合顺序,但是当插入一个批次时,我不知道下一个应该在哪里开始。提示是如何使用的? insert 是否会从建议的位置执行类似于二进制搜索的操作?通常使用不好的提示会有多糟糕?

【问题讨论】:

  • 比我想要的更长?我知道O(n)O(log n),甚至O(n^2)...但是O(longer than I'd like)不在我的教科书中
  • 好吧,事情也很少需要O(log n) 秒...但是执行约 200.000 次插入(有重复项)大约需要 4 秒。这对用户来说是一个明显的延迟,如果可以的话,我想缩短它
  • 如果这是一个瓶颈,您可以使用unordered_set 进行基准测试。 Boost 或 STL,具体取决于您的编译器。

标签: c++


【解决方案1】:

我建议只阅读编译器读取的内容:#include <set> 的头文件。在我的系统(GNU libstdc++ 4.5.1)上,我可以阅读以下不言自明的文本:

  /**
   *  @brief Attempts to insert an element into the %set.
   *  @param  position  An iterator that serves as a hint as to where the
   *                    element should be inserted.
   *  @param  x  Element to be inserted.
   *  @return  An iterator that points to the element with key of @a x (may
   *           or may not be the element passed in).
   *
   *  This function is not concerned about whether the insertion took place,
   *  and thus does not return a boolean like the single-argument insert()
   *  does.  Note that the first parameter is only a hint and can
   *  potentially improve the performance of the insertion process.  A bad
   *  hint would cause no gains in efficiency.
   *
   *  For more on @a hinting, see:
   *  http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt07ch17.html
   *  
   *  Insertion requires logarithmic time (if the hint is not taken).
   */
  iterator
  insert(iterator __position, const value_type& __x)
  { return _M_t._M_insert_unique_(__position, __x); }

外卖:

  1. 不好的提示不会提高效率
  2. 插入是O(log n)
  3. 您可以阅读更多关于 insertion hints in the GNU libstdc++ manual 的信息。

【讨论】:

  • 嗯,所以如果提示不完全正确,可能会完全忽略?
  • 这里必须在字里行间阅读。如果提示结果不正确,它可能会转身并立即调用非提示版本 - 但这没有明确说明。
  • @carlpett:是的,可能。从理论上讲,您的 C++ 实现可以做一些更聪明的事情(不太可能,但可能);如果你指定你的实际编译器和版本,有人可能会给出明确的答案。
  • 链接坏了,是this吗?
  • @doug65536 看起来像。答案文本已被编辑为指向回程机器:)
【解决方案2】:

如果您检查文件bits/stl_tree.h(在 GNU libstdc++ 中),您会发现带有提示参数的 _M_insert_unique 成员函数在提示左侧查找一个节点,然后在右侧查找一个节点,然后默认调用普通的插入例程。

它至少调用一次key_compare(如果集合不为空),最多调用三次。从一个节点到下一个或上一个节点是跟随指针的问题,因为 (IIRC) std::set 和朋友是 threaded trees

所以,糟糕的提示有多糟糕取决于比较例程,以及您的std::set 的分配器是否将节点打包在内存中。

【讨论】:

    【解决方案3】:

    如果它是 right 提示 - 用于插入的位置,则提示是好的。例如,如果您按顺序插入对象,则可以使用。

    如果提示不正确,则无效,您会得到一个非提示插入。

    【讨论】:

      【解决方案4】:

      如果您在使用之前一次性构建集合,则可以使用向量代替,并在使用之前对其进行排序。您可以在排序的向量上使用binary_searchlower_boundupper_boundequal_range 算法进行快速查找。您还可以使用mergeinplace_merge 组合已排序的向量,并使用set_differenceset_intersectionset_union 进行其他常见的集合操作。

      【讨论】:

        猜你喜欢
        • 2021-01-20
        • 2010-11-24
        • 2021-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-26
        • 2013-02-04
        相关资源
        最近更新 更多