【问题标题】:What's the fastest way to clear a std::list of dynamic objects?清除动态对象的 std::list 的最快方法是什么?
【发布时间】:2012-02-23 07:12:32
【问题描述】:

我有一个定义如下的列表:

std::list<int *> m_ilist;

我将ints 添加到列表中:

m_ilist.push_back (new int (x));

我想破坏向量并删除为每个元素分配的内存。

哪个更好:

  1. 循环遍历列表,在每个迭代器上调用 delete。在此调用 clear() 之后:

    for (...) { delete *it; } m_ilist.clear ();
    
  2. 执行循环但在迭代器上调用擦除:

    for (...) { delete *it; m_ilist.erase (); }
    

更好将被定义为更快/更快/更少的处理。

谢谢。

【问题讨论】:

  • 或者使用std::shared_ptr/std::unique_ptr 直接拨打clear?
  • @JoachimPileborg:我在那个问题中没有看到 C++11 标记,所以会出现编译器错误。
  • 为什么要动态分配ints?除非您希望能够使用 NULL 作为标记值,​​并且不能使用任何合法的 int 值来代替,否则只会浪费时间和内存。即使您需要哨兵,使用 boost 的可选库或 std::pair&lt;bool, int&gt; 也会更有效。然后你可以在完成后简单地清除列表。
  • @NicolBolas 我们大多数人都认为“C++”的意思是“C++ 的最新稳定/官方版本”,就像 SO 上讨论的所有其他技术一样。
  • @Kos:C++11 还没有完全可以从任何编译器供应商处获得,更不用说被证明是稳定的了。但是,Joachim 的评论可能受益于“(如果这些 C++11 功能可用,否则 boost 或其他等价物)”。

标签: c++ performance list stl


【解决方案1】:

对其进行基准测试。更重要的是,在您的特定应用程序中对其进行基准测试。我们测试两个版本的合成运行时毫无意义,因为您的里程会有所不同,具体取决于您之后的操作。

瓶颈是幕后进行的令人讨厌的释放和分配。根据程序其余部分展示的内存分配模式,您很可能会得到不同的结果。

话虽如此;差异很可能可以忽略不计,所以我的直觉是,如果有疑问,请选择第二个循环并保存一个循环。


编辑:请注意,如果您真的关心速度,请使用std::vector&lt;int&gt; 而不是std::list&lt;int*&gt;。差异将明显大于您列出的选项之间的差异。

编辑 2:如果您使用 std::vector,请确保使用变体 1(使用 clear 而不是许多 erases)。在这种情况下,它确实会产生巨大的影响。如果元素真的很大(而不是int),您可能希望将指针(或智能指针,就此而言)实际放入向量中以最小化复制开销。

【讨论】:

  • 谢谢 - 我们通常使用std::list 主要是因为我们需要list
  • 顺便说一句,您实际使用具有强所有权的 std::list 整数的场景是什么,这实际上需要您持有引用而不是对象?我的意思是,我可以想象为什么 list 不是 vector (因为您需要 O(1) 插入和维护顺序),但为什么不只是 std::list&lt;int&gt;
  • list 使用简单。数据按到达的顺序放入(因此 push_back),然后数据的用户迭代所有数据(无需修改或随机访问)。上面int的使用只是一个例子。
  • @user626201:是的,vector::erase 会很慢,因为它必须移动所有后面的元素。对于vector,您需要在之后用clear() 清空它。
  • @user626201:请注意,如果您在插入元素之前知道(或可以粗略猜测)元素的数量,则可以进一步提高 .39s。看看std::vector::reserve。在您的场景中,没有理由使用std::list 而非std::vector!这也使您的问题消失了,因为对于矢量,您必须使用版本 1(如您的实验所示)。
【解决方案2】:

最快的方法是根本不删除它。这可能是一个奇怪的答案,但有一些方法可以不删除它,但仍然没有内存泄漏。

您可以尝试合并整数。与其分配和释放整数,不如创建一个整数池。这使您不必一遍又一遍地分配和释放内存,代价是您自己进行管理(哪些整数已经在使用,哪些没有)。这种管理的复杂程度取决于具体情况。在某些情况下,只需一个一个地分发整数并在最后删除所有整数就足够了。看看这个技巧是否对你有帮助。

您也可以尝试不在主线程中清理列表,而是在单独的线程中。在应用程序的开头启动一个“清理线程”,当需要清理列表时,将列表传递给另一个线程(使用事件、临界区和队列将列表从主线程传递给清理线程。现在清理线程可以清理列表并释放内存,而主线程继续运行并执行重要的工作。

最后,发挥你的想象力。

【讨论】:

  • +1 表示池化建议;你可能想重新表述一下后台线程的想法,因为这基本上是一个 GC,并且有很多可以直接用于 C++。
  • 谢谢 - 但是,用例是在嵌入式内存受限系统中。因此,一旦数据结构关闭,就需要为其他进程释放所有内存。不过,我确实喜欢这个主意。
  • 感谢 CAFxX。你能给我一些在第二个线程中删除的 C++ GC 的例子吗?我喜欢这个想法,但我对 C++ 中的这些 GC 没有经验。
猜你喜欢
  • 1970-01-01
  • 2012-03-03
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多