【问题标题】:STL AlternativeSTL 替代方案
【发布时间】:2010-09-10 08:49:57
【问题描述】:

我真的很讨厌使用 STL 容器,因为它们会使我的代码的调试版本运行得非常缓慢。其他人使用什么来代替 STL 以进行调试构建具有合理的性能?

我是一名游戏程序员,这在我从事的许多项目中一直存在问题。使用 STL 容器处理所有内容时,很难达到 60 fps。

我的大部分工作都使用 MSVC。

【问题讨论】:

    标签: c++ performance stl debug-build


    【解决方案1】:

    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] 的性能。

    【讨论】:

    • 好吧,如果我可以再添加一个 +1,我会的。链接open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html 非常有启发性。
    • 假设数组是在堆上分配的,范围检查被禁用\编译器设置为优化,我不相信 C 数组的访问时间比 std::vector 快。
    【解决方案2】:

    我的经验是,设计良好的 STL 代码在调试版本中运行缓慢,因为优化器已关闭。 STL 容器向构造函数和 operator= 发出大量调用(如果它们是轻量级的)在发布版本中被内联/删除。

    此外,Visual C++ 2005 及更高版本在发布和调试版本中都启用了 STL 检查。对于重 STL 的软件来说,它是一个巨大的性能消耗者。可以通过为所有编译单元定义 _SECURE_SCL=0 来禁用它。请注意,在不同的编译单元中具有不同的 _SECURE_SCL 状态几乎肯定会导致灾难。

    您可以在关闭检查的情况下创建第三个构建配置,并使用它来调试性能。不过,我建议您保留调试配置并进行检查,因为捕获错误的数组索引和类似的东西非常有帮助。

    【讨论】:

      【解决方案3】:

      如果您正在运行视觉工作室,您可能需要考虑以下几点:

      #define _SECURE_SCL 0
      #define _HAS_ITERATOR_DEBUGGING 0
      

      这只是针对迭代器,您执行的是什么类型的 STL 操作?你可能想看看优化你的内存操作;即,使用 resize() 一次插入多个元素,而不是使用 pop/push 一次插入一个元素。

      【讨论】:

      • 这些常量应该是不言自明的吗?他们是做什么的?
      【解决方案4】:

      对于大型、性能关键的应用程序,构建自己的容器专门为您的需求量身定制可能值得花时间投资。

      我在这里谈论的是真正的游戏开发。

      【讨论】:

      • 是的,这实际上是我为所有我没有继承代码的游戏所做的。而且由于您控制分配器,这可能是最好的答案。我只是希望能做得更好!
      【解决方案5】:

      我敢打赌,您的 STL 使用经过检查的实现进行调试。这可能是一件好事,因为它会捕获迭代器溢出等。如果这对您来说是个大问题,可能有一个编译器开关可以将其关闭。检查您的文档。

      【讨论】:

        【解决方案6】:

        如果您使用的是 Visual C++,那么您应该看看这个:

        http://channel9.msdn.com/shows/Going+Deep/STL-Iterator-Debugging-and-Secure-SCL/

        以及该页面的链接,其中涵盖了 MS/Dinkware STL 所做的所有调试模式检查的各种成本和选项。

        如果您要问这样一个依赖于平台的问题,最好也提及您的平台...

        【讨论】:

          【解决方案7】:

          查看EASTL

          【讨论】:

          • 我不认为 EASTL 对公众开放。但是该文档涵盖了当前 STL 实现的许多问题。
          【解决方案8】:

          MSVC 在调试版本中使用了一个非常重量级的检查迭代器实现,其他人已经讨论过,所以我不会重复它(但从那里开始)

          您可能感兴趣的另一件事是,您的“调试构建”和“发布构建”可能涉及更改(至少)4 个只是松散相关的设置。

          1. 生成 .pdb 文件(cl /Zi 和链接 /DEBUG),它允许符号调试。您可能希望将 /OPT:ref 添加到链接器选项;链接器在不创建 .pdb 文件时会删除未引用的函数,但在 /DEBUG 模式下它会保留所有函数(因为调试符号会引用它们),除非您明确添加。
          2. 使用 C 运行时库的调试版本(可能是 MSVCR*D.dll,但这取决于您使用的运行时)。这归结为 /MT 或 /MTd(或其他如果不使用 dll 运行时)
          3. 关闭编译器优化 (/Od)
          4. 设置预处理器#defines DEBUG 或 NDEBUG

          这些可以独立切换。第一个在运行时性能上没有任何成本,尽管它增加了大小。第二个让一些函数更昂贵,但对 malloc 和 free 的影响巨大;调试运行时版本小心地用值“毒化”它们接触的内存,以清除未初始化的数据错误。我相信通过 MSVCP* STL 实现,它还消除了通常完成的所有分配池,因此泄漏准确地显示了您认为的块,而不是它一直在分配的更大的内存块;这意味着它会在更慢的情况下对 malloc 进行更多调用。第三;好吧,那个人做了很多事情(this question 对此主题进行了很好的讨论)。不幸的是,如果您希望单步运行顺利,则需要它。第四个以各种方式影响许多库,但最值得注意的是它编译或消除了 assert() 和朋友。

          因此,您可能会考虑使用这些选择的较少组合进行构建。我大量使用具有符号(/Zi 和链接/DEBUG)和断言(/DDEBUG)的构建,但仍然优化(/O1 或 /O2 或您使用的任何标志)但堆栈帧指针保留用于清除回溯 (/Oy-) 并使用正常的运行时库 (/MT)。这执行接近我的发布版本并且是半可调试的(回溯很好,单步执行在源代码级别有点古怪;组装级别当然可以正常工作)。你可以有很多你想要的配置;只需克隆您的第一个版本并打开调试的任何部分似乎有用。

          【讨论】:

            【解决方案9】:

            对不起,我不能发表评论,所以这里是一个答案:EASTL 现在在 github 上可用:https://github.com/paulhodge/EASTL

            【讨论】:

              【解决方案10】:

              Ultimate++ 有自己的一组容器 - 不确定您是否可以将它们与库的其余部分分开使用:http://www.ultimatepp.org/

              【讨论】:

                【解决方案11】:

                ACE library 呢?它是一个开源的面向对象的并发通信软件框架,但它也有一些容器类。

                【讨论】:

                • 套用 jwz 的话说:如果您认为“我知道,我会使用 ACE”,那么您现在有两个问题(请参阅 regex.info/blog/2006-09-15/247
                【解决方案12】:

                在 C++ 中使用面向对象的设计模式检查数据结构和算法 布鲁诺·普雷斯 http://www.brpreiss.com/

                【讨论】:

                  【解决方案13】:

                  Qt 用不同的接口重新实现了大多数 c++ 标准库的东西。它看起来不错,但对于商业许可版本来说可能很昂贵。

                  编辑:Qt 已经在LGPL 下发布,这通常可以在商业产品中使用它而无需商业版本(仍然存在)。

                  【讨论】:

                    【解决方案14】:

                    STL 容器不应在调试或其他任何地方运行“非常缓慢”。也许你在滥用它们。您在调试时没有针对 ElectricFence 或 Valgrind 之类的东西运行,是吗?它们会减慢执行大量分配的任何操作。

                    所有容器都可以使用自定义分配器,有些人使用它来提高性能 - 但我自己从来不需要使用它们。

                    【讨论】:

                    • 您显然从未使用 STL 编写过游戏。问题不在于游戏算法,而在于 stl 开销。当 stl 未优化时,它会调用 100 个函数,而它会完全内联,并且开销会降低游戏的帧速率。
                    • 我用 STL 写过几款游戏,但可能和你的规模不一样;我还没有发现这是一个问题(但想象一下这是可能的)
                    【解决方案15】:

                    还有 ETL https://www.etlcpp.com/。该库专门针对时间关键(确定性)应用程序

                    来自网页:

                    ETL 并非旨在完全取代 STL,而是补充 它。其设计目标涵盖四个主要领域。

                    • 创建一组容器,其中大小或最大大小在编译时确定。这些容器应在很大程度上 等同于 STL 中提供的那些,具有兼容的 API。
                    • 与 C++ 03 兼容,但要实现尽可能多的 C++ 11 新增功能。
                    • 具有确定性行为。
                    • 添加标准库中不存在的其他有用组件。

                    嵌入式模板库专为低资源而设计 嵌入式应用程序。它定义了一组容器、算法和 实用程序,其中一些模拟 STL 的某些部分。没有动态 内存分配。该库不使用堆。全部 容器(侵入式除外)具有固定容量,允许 所有内存分配在编译时确定。图书馆是 适用于任何支持 C++03 的编译器。

                    【讨论】:

                      猜你喜欢
                      • 2010-11-07
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-10-05
                      • 2012-12-05
                      • 2013-11-22
                      • 2020-11-23
                      相关资源
                      最近更新 更多