【问题标题】:C++ "delete" is slow. Where should I look first?C++“删除”很慢。我应该先看哪里?
【发布时间】:2011-01-20 21:19:20
【问题描述】:

我有一个 C++ 应用程序,其中“删除”功能运行缓慢。什么可能导致这种情况,我应该从哪里开始寻找解决方案?

背景:

此 C++ 代码位于 AutoCAD 内部运行的 ARX 文件中,基本上只是一个 DLL。

删除速度较慢的特定计算机正在运行 AutoCAD 2011、Windows 7、64 位。 AutoCAD 2011 的 ARX 必须使用 Visual Studio 2008 Service Pack 1 进行编译。

有问题的计算机是客户的计算机。它没有安装任何版本的 Visual Studio。

在我的开发计算机上,代码在 AutoCAD 2011 中没有任何问题。

为了测试,我有一些删除链表的代码。在有问题的计算机上,删除列表需要 0.7 秒。在没有问题的计算机和配置上,相同的代码需要 0.02 秒。具体时间并不重要——两个数字之间的差异很大。

我确保在两台计算机上运行相同版本的代码,因此这不是发布与调试构建问题。

【问题讨论】:

  • 列表中有多少项,它们的大小是多少?
  • 分享您用于基准测试的测试代码。
  • 您确定不比较调试和发布版本吗?相差35倍!
  • 我已为问题添加了更新以响应 cmets。是的,链表很糟糕,但这就是 AutoCAD 库用于传递数据的方法,所以我要处理它。
  • 您需要在问题计算机上安装分析器,无论是侵入式(在程序中)还是使用外部程序。无论哪种方式,对假程序进行基准测试都是没有用的,尤其是您应该使用的std::liststd::string 的手写实现。 (当然,std::list 可以说是更糟糕的容器,但它确实证明了delete 的问题,如果这确实是问题的话。)

标签: c++ memory-management


【解决方案1】:

大致按照我检查它们的顺序:

  • 其他插件:这种行为可能是由其他 ARX 文件引起的吗?可以在坏系统上禁用它们吗?
  • PerfMon:检查删除期间是否出现软/硬页面错误或缓存未命中高峰(希望您可以在客户计算机上进行设置)。
  • HeapQueryInformation:好/坏环境中的值相同?
  • 堆锁:是否有其他线程在后台高度活跃,紧紧抓住堆的锁?您可以通过将循环包装在 HeapLock / HeapUnlock 中进行测试(当然还有时间在锁内)。
  • Hooks:各自的代码可以被钩住吗? (例如,第 3 方应用程序连接到 C++/Win32 堆函数以做任何想做的事情)
  • 抓住稻草:各自的new 花费的时间是否异常长?个人delete 次如何分布?

【讨论】:

    【解决方案2】:

    可能是由于工作/失败系统之间的缓存效率不同。失败的系统上可能有更多的内存碎片,这会导致大量删除破坏缓存。在静止系统上,数据最终可能会更加连续,并在大删除期间获得更多缓存命中。

    试试英特尔性能计数器监视器?

    【讨论】:

      【解决方案3】:

      如果可以接受且可行,请尝试在客户的计算机上使用分析器。

      您可以尝试AMD CodeAnalyst 或英特尔分析器(虽然那个不是免费的)。

      如果这不可能,则将分析代码添加到您的发布版本中并从客户那里收集结果。即使是简单的分析代码也可以帮助您找到真正的瓶颈。

      看起来删除本身不是问题,但问题可能是代码的其他部分。

      例如- head->resval.rstring 的类型是什么?

      【讨论】:

        【解决方案4】:

        我们经常遇到这种情况。您的代码没有问题,即使在发布模式下,删除数千个项目也可能需要几秒钟(我也看到过几分钟)。

        答案是不删除。给自己一个真正的内存分配器,而不是单独分配每个对象,而是创建一个内存池(或自定义堆或任何你想调用的东西)。 Nedmalloc 是我们使用和推荐的,您可以创建一个“nedpool”。基本上,池是从其中分配对象的一块内存。您仍然为每个对象分配内存,但它是从池中获取的,而不是直接从操作系统中获取的。

        到了删除的时候,你只需删除整个池,而不是一个一个地删除对象。为将同时到期的每批对象使用不同的池。您不需要预先为整个池分配内存,但您只能一次删除整个池。

        【讨论】:

        • 如果您这样做,大多数 C++ 对象都会有未定义的行为。您至少需要安排调用所包含对象的析构函数。
        • 当然。需要使用 in-place new 然后自己手动调用析构函数,但不要释放内存。
        【解决方案5】:

        重新列表是如何生成的?此外,您是否有理由手动分配和删除 resbuf,而不是使用 acutNewRb 和 acutRelRb?

        此外,您可能已经检查过这些,但用户是否在 AutoCAD 2009 和 2011 中都加载了默认图形?如果不是,图纸是否相同(ACAD 版本除外),它们是位于本地还是位于网络驱动器上?您还可以查看用户是否在两个实例中运行相同的 lisp/.Net/objectARX 应用程序。另外,AutoCAD 2011 是网络安装还是本地安装?

        最后,您可能想在问题中添加 autocad 和 objectarx 标签。

        【讨论】:

        • retList 正在使用 acutBuildList 生成。我正在使用 acutRelRb 来释放内存,但是在分析代码时,我发现该函数是问题所在。我手动重写了它以查看是否可以解决问题(它没有),以便我更好地了解导致问题的原因。在所有情况下,我都在同一张图纸上运行代码。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多