我非常讨厌使用STL容器,因为它们使我的代码的调试版本运行非常缓慢。有没有其他人使用STL以外的东西,在调试构建时具有合理的性能?
我是一名游戏程序员,在我参与的许多项目中都遇到了这个问题。如果你在所有地方都使用STL容器,要达到60帧每秒的速率就相当困难。
我大多数工作都使用MSVC。
我非常讨厌使用STL容器,因为它们使我的代码的调试版本运行非常缓慢。有没有其他人使用STL以外的东西,在调试构建时具有合理的性能?
我是一名游戏程序员,在我参与的许多项目中都遇到了这个问题。如果你在所有地方都使用STL容器,要达到60帧每秒的速率就相当困难。
我大多数工作都使用MSVC。
EASTL是一个可能的选择,但仍不完美。电子艺界的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状态的不同编译单元几乎肯定会导致灾难。
你可以创建第三个构建配置并关闭检查,然后使用该配置进行性能调试。我建议你保留带有检查的调试配置,因为它非常有助于捕捉错误的数组索引等问题。
如果您正在使用Visual Studio,可能需要考虑以下内容:
#define _SECURE_SCL 0
#define _HAS_ITERATOR_DEBUGGING 0
这只是针对迭代器的问题,你要执行哪种类型的STL操作?您可以考虑优化您的内存操作;例如,使用resize()一次插入多个元素,而不是使用pop/push一个接一个地插入元素。
对于大型、性能关键的应用程序,构建针对您需要的容器可能值得投入时间。
我在谈论这里的是真正的游戏开发。
我敢打赌你的STL在调试时使用了一个检查实现。这可能是一件好事,因为它可以捕捉迭代器超限等问题。如果这对你来说是个大问题,那么可能有一个编译器开关可以关闭它。请查阅文档。
在调试版本中,MSVC使用了非常重量级的检查迭代器实现,其他人已经讨论过了,所以我不会重复(但可以从那里开始)。
另一件可能对您有兴趣的事情是,您的“调试版本”和“发布版本”可能涉及更改(至少)4个仅松散相关的设置。
这些可以独立切换。第一个不会影响运行时性能,但会增加大小。第二个使许多函数变得更加昂贵,但对malloc和free有巨大影响;调试运行时版本会小心地将它们接触的内存“毒化”,以使未初始化的数据错误变得清晰。我相信在MSVCP* STL实现中,它还消除了通常执行的所有分配池,因此泄漏会显示您认为的确切块,而不是它正在子分配的某个更大的内存块;这意味着它会在它们变得更慢的同时进行更多的malloc调用。第三个;好吧,那个做了很多事情(这个问题对该主题进行了一些很好的讨论)。不幸的是,如果您想要单步运行平稳工作,则需要它。第四个以各种方式影响许多库,但最值得注意的是,它编译或消除了assert()和其它相关内容。
因此,您可能考虑创建某些较小组合的生成。我经常使用具有符号(/Zi和link /DEBUG)和断言(/DDEBUG)的生成,但仍然进行了优化(/O1或/O2或您使用的任何标志),但保留堆栈帧指针以进行清晰的回溯(/Oy-)并使用正常运行时库(/MT)。这执行接近我的发布生成,并且是半可调试的(回溯很好,单步运行在源级别上有点奇怪;当然,汇编级别可以正常工作)。您可以拥有任意数量的配置;只需克隆您的发布版本并打开看起来有用的任何调试部分即可。