为啥从 std::vector 中随机删除比 std::list 快?

Posted

技术标签:

【中文标题】为啥从 std::vector 中随机删除比 std::list 快?【英文标题】:How come random deletion from a std::vector is faster than a std::list?为什么从 std::vector 中随机删除比 std::list 快? 【发布时间】:2009-05-14 20:46:15 【问题描述】:

为什么从 std::vector 中随机删除比 std::list 快?我正在做的加速它的是将随机元素与最后一个交换,然后删除最后一个。 我原以为该列表会更快,因为它是为随机删除而构建的。

for(int i = 500; i < 600; i++)
    swap(vector1[i], vector1[vector1.size()-1]);
    vector1.pop_back();


for(int i = 0; i < 100; i++)
        list1.pop_front();

结果(以秒为单位): Vec 交换删除:0.00000909461232367903 列表正常删除:0.00011785102105932310

【问题讨论】:

【参考方案1】:

不过,您所做的并不是随机删除。您要从最后删除,这就是构建向量的目的(除其他外)。

并且在交换时,您正在执行单个随机索引操作,这也是向量擅长的。

【讨论】:

确实如此。我没有看到任何随机性。 那么当一个单一的交换向量更快并且提供随机访问时,std::list 的目的是什么? 这取决于您是否要保持向量有序 当您需要能够在任何位置插入和/或删除时,std::list 特别有用。你没有这样做。 考虑向量/列表中的对象具有极其昂贵的复制构造函数的情况。【参考方案2】:

std::liststd::vector 之间的区别不仅仅在于性能。它们还具有不同的迭代器失效语义。如果您从 std::list 中删除一个项目,则所有指向列表中其他项目的迭代器仍然有效。 std::vector 不是这样,擦除一个项目会使指向该项目的所有迭代器无效。 (在某些实现中,它们可能仍然充当有效的迭代器,但根据标准,它们现在不可用,如果您尝试使用它们,检查实现应该断言。)

所以你对容器的选择也与你需要的语义有关。

【讨论】:

【参考方案3】:

这不是随机的。试试 vector1.erase(vector.begin() + rand() % vector.size());而是。

【讨论】:

我知道这是真正的随机,但目的是查看列表或向量是否最适合具有大量真正随机删除的程序。我知道这种方式比列表慢。【参考方案4】:

列表erase 将导致删除已删除的列表元素,这将调用delete 运算符。矢量擦除只会导致交换,然后是整数递减 - 这要快得多。

【讨论】:

写擦除时你的意思是 pop_back 吗? AFAIK pop_back() 没有交换底层数组(这意味着它之前已经创建了它,这很耗时)。它只会减小向量的大小然后调用析构函数? 实际上,据我所知,向量永远不会自我修剪。您需要实现 Scott Meyer 所调用的“交换技巧”,这次使用另一个向量实例,其 容量 已减少到最佳状态。 是的,对不起,我的意思是 pop_back 和 pop_front 调用。在列表的情况下,它是根据擦除实现的【参考方案5】:

实际上,如果您想进一步加快速度,您应该通过 迭代器 为向量中的元素编制索引。众所周知,它们对于某些架构具有更好的性能。

【讨论】:

如果您的架构没有指针+索引内存访问模式,和/或缺少用于您正在做的事情的寄存器,那么提出通过索引访问需要的情况是合理的通过指针/迭代器访问的额外指令左右。当然,如果你在循环条件中调用 end() 并且编译器没有(或不能)在循环外对其进行优化,你可能会丢失这个。

以上是关于为啥从 std::vector 中随机删除比 std::list 快?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::fill(0) 比 std::fill(1) 慢?

如何从 std::string 中删除 +

为啥在 C++11 中更改了 std::vector::resize 签名?

从 std::vector 中删除多个对象?

从 C++ std::vector 中删除元素

从 std::vector 中删除前 N 个元素