【问题标题】:Is deleting an element in the middle of a std::vector still expensive with movable types?使用可移动类型删除 std::vector 中间的元素是否仍然很昂贵?
【发布时间】:2011-09-27 06:45:14
【问题描述】:

通常认为删除std::vector 中间的元素代价高昂,因为它需要复制它之后的每个元素以填充孔。

在 C++11 中,std::vector 会将所有元素移动,这应该非常快(如果仅与副本相关),至少我认为是这样。当然,它在时间上仍然是线性的,但总的来说应该比旧版本更快。

这是真的吗?我不再担心删除中间的一些对象了吗?

【问题讨论】:

  • 在 C++03 中不能交换对象吗?如果是这样,性能差异将非常小。'
  • @jalf:嗯,这就是unafe_erase 的工作原理——swap 与 last 和 pop_back,但如果你想保持订单,你不能只是交换。
  • @Xeo:与从中间移除然后将元素移动一个到向量末尾的方式相同,您可以swap 与下一个元素一起移除的元素,移动到向量的末尾,然后调用pop_back
  • @jalf:据我所知,只有 VC9 在其 STL 中执行“交换”,因为容器不需要有效地保存可交换元素。
  • @David:在您对@Xeo 的回复中,您似乎在描述一种更复杂、更随意的方式来实现同样的目标?

标签: c++ vector c++11 move-semantics


【解决方案1】:

这取决于向量中的内容。如果它是 POD 或指针,我无法想象它会有什么不同。如果是复制繁重的类实例,但可以非常快速地移动,我希望 C++0x 可以加快速度。

但是,我认为如果从 std::vectors 中间删除元素是代码中的瓶颈,那么 C++0x 可能不是正确的解决方法。考虑使用可以更好地处理此类情况的数据结构,或者如果元素的顺序无关紧要,请考虑使用std::iter_swap 加上std::vector::pop_back

【讨论】:

    【解决方案2】:

    如果您考虑到标准使用的成本,它的成本将完全一样。标准规定了对包含类型执行的操作的成本,并且操作的数量仍然相同,只是每个操作都会更快。

    例如,考虑在 C++03 中在 vector<string> 中间插入元素的成本。标准调用O(N),其中N 是向量的大小,但实际成本是O(N * M),其中M 是字符串的大小。在分析容器中的操作成本时忽略M 的原因是它取决于包含的元素。在 C++0x 中,带有可移动类型的成本将是 O(N)(字符串可以移动到新位置),但在这两种情况下,宣传的复杂性都是 O(N)

    举一个简单的反例,如果你认为在 C++03 中插入​​向量中间是一项昂贵的操作,并且你考虑std::vector<int>,那么在 C++ 中插入向量中间0x 一样昂贵,在这种情况下没有加速。

    另外请注意,任何潜在的改进都取决于您的对象是否可移动(它们不需要是),并且当前的一些 STL 实现已经以类似的方式进行了优化(没有语言支持),例如,Dinkumware 实现(我认为是这个)有一些优化,当std::vector<std::vector<T> > 增长时,它会创建新的存储并使用 empty 向量进行初始化(即没有分配内存,因此成本最低),然后swaps 分配新旧区域中的向量,有效地实现 move 语义。

    【讨论】:

    • 如果字符串都包含少于 16 个字符,则它们的移动(比复制)不会更快。因为它们被存储在原地。
    【解决方案3】:

    实际上,在绝大多数情况下,移动要比复制快得多。任何通过引用存储信息的类型都可以防止复制,例如,几乎所有容器、智能指针等,以及任何涉及这些类型的类。

    当然这仍然是线性时间,所以如果你有一百万个整数,它不会更快。但是,移动容器和智能指针之类的东西可能比复制它们快几个数量级。

    【讨论】:

    • 对于智能指针,您肯定说的是shared_ptr,因为unique 不可复制,因此无法与移动相提并论。在shared 情况下,唯一可能的加速是跳过引用计数原子添加和减量,以及比较。但你不能避免 2 指针的副本。 (参考 cnt 对象和指针)
    【解决方案4】:

    我仍然是 C++0x 移动的新手,但我真的不知道您将如何在这里获得 vector 固有的任何有用的加速。

    这一切都取决于你的元素类型:我无法想象你会得到任何加速,除非你的元素类型的对象移动速度比复制速度快

    【讨论】:

    • 对于几乎所有昂贵的类型基本上都是如此。
    • @DeadMG:是的,但是OP没有说明他的元素类型是否昂贵。
    • 如果不贵,那么从中间删除也不贵,反正这是过早的优化。
    • @DeadMG:如果向量很大,那么即使复制/移动每个元素的成本很低,从中间删除也会很昂贵。
    • @DeadMG:这里的过早优化是什么?将移动语义引入 C++?
    【解决方案5】:

    第一点,要决定你需要一个向量还是列表?如果您不希望基于索引访问数据结构,列表会很好,因为您的删除发生在容器中间。此外,您还必须考虑其他变体,例如树来决定最适合您的。这可能不会对您的性能产​​生太大影响,但仍然只是为了共享信息,列表中的内容可能会分布在多个页面文件中,因此在使用大量数据时性能会受到影响。

    Rvalue reference and move constructor 可以提高容器的性能。它可以避免一些不必要的复制操作等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-03
      • 2013-03-29
      • 1970-01-01
      • 2021-01-10
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      相关资源
      最近更新 更多