【问题标题】:Where is the performance gain of the erase-remove idiom coming from擦除删除习语的性能增益来自哪里
【发布时间】:2017-08-07 16:04:14
【问题描述】:

我需要从满足特定条件的向量中删除所有元素。

我的第一种方法是遍历向量并在所有满足条件的元素上调用 vector::erase。

据我了解,vector::erase 在此用例中的性能很差,因为它会从底层数组中删除该项目,并将向量的其余部分向前移动一个元素(或者如果您删除一个范围则更多元素)。 当您移除多个元素时,后面的元素将在每次移除时移动。

remove 算法将所有要移除的元素移到向量的末尾,因此您只需移除向量的后部,不涉及移位。

但是为什么这比擦除更快?(是不是更快?)

将元素移动到末尾是否意味着将所有后续元素向前移动,就像在vector::erase 中一样?

怎么会,那个remove只有O(n)的复杂度?

【问题讨论】:

  • 据我了解,vector::erase 在这个用例中表现不佳 -- 如果编码器不重新安装在导致删除的每次迭代中正确迭代。这只是应该使用remove / erase 的另一个原因。

标签: c++ vector stl erase-remove-idiom


【解决方案1】:

这里的性能问题不是关于删除要删除的元素,或者将它们移动到最后(这实际上不会发生),而是关于 移动要保留的元素 .

如果您在要删除的每个元素上使用erase,则需要移动这些元素之后的所有元素......每次调用erase。通常,如果要删除 k 元素,您会将元素移动到最新的(在向量中)k 次之后,而不是仅移动一次。

但是如果你打电话给remove,你只会移动一次(见下面的例子)。

一个小例子可以更好地理解这两种方法的工作原理:

假设您有一个大小为 1000 的向量,并且您要删除的元素位于位置 17 和 37。

erase 作用于要移除的两个元素:

  • 当您为第 17 个元素调用 erase() 时,您需要将第 18 个元素移动到第 999、982 个元素。
  • 当您为第 36 个元素调用 erase() 时(现在是第 36 个!),您需要将第 37 个元素移动到第 998、962 个元素。

总共移动了 962 + 982 = 1944 个元素,其中 962 个元素被移动了两次。

使用remove,会发生如下情况:

element 0 does not change;
element 1 does not change;
...
element 17 is "discarded";
element 18 is moved at position 17;
element 19 is moved at position 18;
...
element 36 is moved at position 35;
element 37 is "discarded";
element 38 is moved at position 36;
...
element 999 is moved at position 997.

您总共移动了 998 个元素(1000 减去您删除的两个元素),这比之前方法的 1943 个元素要好得多。如果要删除 2 个以上的元素,这会更好。

您可以查看 en.cppreference.com 上的 possible implementation,以更好地了解 std::remove 的工作原理。

【讨论】:

    【解决方案2】:

    优势在于std::remove 不只是一次删除一个元素。例如,如果对 std::remove 的调用导致删除了向量的前 10 个元素,它会将第 11 个元素直接移动到第一个位置,将第 12 个元素直接移动到第二个位置等等......前 10 个元素一次一个,它会将你擦除的元素之后的每个元素移回 1。然后你会擦除下一个元素,每个元素都必须再次移动。这会在每个被删除的元素上重复。

    此外,删除的元素不必是连续的,这种优势就会发生。例如,如果调用 remove 导致从第一个开始的所有其他元素都被删除。首先,第二个元素将被移动到第一个位置,这将留下两个元素的间隙,直到下一个可保留元素。那么第4个元素会直接移动到第2个位置,留下3个元素的间隙,以此类推。

    另外,稍微修正一下:

    remove 算法将所有要移除的元素移到向量的末尾

    删除算法不这样做。它不关心要删除的元素会发生什么。它们只是被要保留的元素替换。在调用 remove 之后,最后元素的值是未指定的。您描述的算法是分区(具有反向比较功能)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多