【发布时间】:2010-09-10 08:49:57
【问题描述】:
我真的很讨厌使用 STL 容器,因为它们会使我的代码的调试版本运行得非常缓慢。其他人使用什么来代替 STL 以进行调试构建具有合理的性能?
我是一名游戏程序员,这在我从事的许多项目中一直存在问题。使用 STL 容器处理所有内容时,很难达到 60 fps。
我的大部分工作都使用 MSVC。
【问题讨论】:
标签: c++ performance stl debug-build
我真的很讨厌使用 STL 容器,因为它们会使我的代码的调试版本运行得非常缓慢。其他人使用什么来代替 STL 以进行调试构建具有合理的性能?
我是一名游戏程序员,这在我从事的许多项目中一直存在问题。使用 STL 容器处理所有内容时,很难达到 60 fps。
我的大部分工作都使用 MSVC。
【问题讨论】:
标签: c++ performance stl debug-build
EASTL 是一种可能性,但仍不完美。 Electronic Arts 的 Paul Pedriana 对各种 STL 实现在游戏应用程序中的性能进行了调查,其摘要可在此处找到: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html
其中一些调整正在审核中以纳入 C++ 标准。
请注意,即使 EASTL 也不会针对未优化的情况进行优化。前段时间我有一个 excel 文件,但我想我已经把它弄丢了,但要访问它是这样的:
debug release
STL 100 10
EASTL 10 3
array[i] 3 1
我取得的最大成功是滚动自己的容器。您可以将它们降低到接近数组 [x] 的性能。
【讨论】:
我的经验是,设计良好的 STL 代码在调试版本中运行缓慢,因为优化器已关闭。 STL 容器向构造函数和 operator= 发出大量调用(如果它们是轻量级的)在发布版本中被内联/删除。
此外,Visual C++ 2005 及更高版本在发布和调试版本中都启用了 STL 检查。对于重 STL 的软件来说,它是一个巨大的性能消耗者。可以通过为所有编译单元定义 _SECURE_SCL=0 来禁用它。请注意,在不同的编译单元中具有不同的 _SECURE_SCL 状态几乎肯定会导致灾难。
您可以在关闭检查的情况下创建第三个构建配置,并使用它来调试性能。不过,我建议您保留调试配置并进行检查,因为捕获错误的数组索引和类似的东西非常有帮助。
【讨论】:
如果您正在运行视觉工作室,您可能需要考虑以下几点:
#define _SECURE_SCL 0
#define _HAS_ITERATOR_DEBUGGING 0
这只是针对迭代器,您执行的是什么类型的 STL 操作?你可能想看看优化你的内存操作;即,使用 resize() 一次插入多个元素,而不是使用 pop/push 一次插入一个元素。
【讨论】:
对于大型、性能关键的应用程序,构建自己的容器专门为您的需求量身定制可能值得花时间投资。
我在这里谈论的是真正的游戏开发。
【讨论】:
我敢打赌,您的 STL 使用经过检查的实现进行调试。这可能是一件好事,因为它会捕获迭代器溢出等。如果这对您来说是个大问题,可能有一个编译器开关可以将其关闭。检查您的文档。
【讨论】:
如果您使用的是 Visual C++,那么您应该看看这个:
http://channel9.msdn.com/shows/Going+Deep/STL-Iterator-Debugging-and-Secure-SCL/
以及该页面的链接,其中涵盖了 MS/Dinkware STL 所做的所有调试模式检查的各种成本和选项。
如果您要问这样一个依赖于平台的问题,最好也提及您的平台...
【讨论】:
查看EASTL。
【讨论】:
MSVC 在调试版本中使用了一个非常重量级的检查迭代器实现,其他人已经讨论过,所以我不会重复它(但从那里开始)
您可能感兴趣的另一件事是,您的“调试构建”和“发布构建”可能涉及更改(至少)4 个只是松散相关的设置。
这些可以独立切换。第一个在运行时性能上没有任何成本,尽管它增加了大小。第二个让一些函数更昂贵,但对 malloc 和 free 的影响巨大;调试运行时版本小心地用值“毒化”它们接触的内存,以清除未初始化的数据错误。我相信通过 MSVCP* STL 实现,它还消除了通常完成的所有分配池,因此泄漏准确地显示了您认为的块,而不是它一直在分配的更大的内存块;这意味着它会在更慢的情况下对 malloc 进行更多调用。第三;好吧,那个人做了很多事情(this question 对此主题进行了很好的讨论)。不幸的是,如果您希望单步运行顺利,则需要它。第四个以各种方式影响许多库,但最值得注意的是它编译或消除了 assert() 和朋友。
因此,您可能会考虑使用这些选择的较少组合进行构建。我大量使用具有符号(/Zi 和链接/DEBUG)和断言(/DDEBUG)的构建,但仍然优化(/O1 或 /O2 或您使用的任何标志)但堆栈帧指针保留用于清除回溯 (/Oy-) 并使用正常的运行时库 (/MT)。这执行接近我的发布版本并且是半可调试的(回溯很好,单步执行在源代码级别有点古怪;组装级别当然可以正常工作)。你可以有很多你想要的配置;只需克隆您的第一个版本并打开调试的任何部分似乎有用。
【讨论】:
对不起,我不能发表评论,所以这里是一个答案:EASTL 现在在 github 上可用:https://github.com/paulhodge/EASTL
【讨论】:
Ultimate++ 有自己的一组容器 - 不确定您是否可以将它们与库的其余部分分开使用:http://www.ultimatepp.org/
【讨论】:
ACE library 呢?它是一个开源的面向对象的并发通信软件框架,但它也有一些容器类。
【讨论】:
在 C++ 中使用面向对象的设计模式检查数据结构和算法 布鲁诺·普雷斯 http://www.brpreiss.com/
【讨论】:
STL 容器不应在调试或其他任何地方运行“非常缓慢”。也许你在滥用它们。您在调试时没有针对 ElectricFence 或 Valgrind 之类的东西运行,是吗?它们会减慢执行大量分配的任何操作。
所有容器都可以使用自定义分配器,有些人使用它来提高性能 - 但我自己从来不需要使用它们。
【讨论】:
还有 ETL https://www.etlcpp.com/。该库专门针对时间关键(确定性)应用程序
来自网页:
ETL 并非旨在完全取代 STL,而是补充 它。其设计目标涵盖四个主要领域。
- 创建一组容器,其中大小或最大大小在编译时确定。这些容器应在很大程度上 等同于 STL 中提供的那些,具有兼容的 API。
- 与 C++ 03 兼容,但要实现尽可能多的 C++ 11 新增功能。
- 具有确定性行为。
- 添加标准库中不存在的其他有用组件。
嵌入式模板库专为低资源而设计 嵌入式应用程序。它定义了一组容器、算法和 实用程序,其中一些模拟 STL 的某些部分。没有动态 内存分配。该库不使用堆。全部 容器(侵入式除外)具有固定容量,允许 所有内存分配在编译时确定。图书馆是 适用于任何支持 C++03 的编译器。
【讨论】: