【问题标题】:RNG crashing c++ programRNG 崩溃 c++ 程序
【发布时间】:2011-04-04 21:28:11
【问题描述】:

我目前正在编写roguelike,自然会使用大量随机数生成。

我遇到的问题是,如果我“过热” rand();我的程序会崩溃。

如果我每帧只生成 20 个左右的整数,那很好……但是当随机数的数量达到数百个时,程序就会崩溃。我制作的每一帧越多,它崩溃的越早......这让我相信发生了一些堆积。

我已经完成了测试,并且在 20 rand();每帧调用一次,它将以最大速度连续运行 24 小时而不会崩溃。三倍,它没有十分钟。

如果我把 srand();在初始化中,我可以在锁定之前生成数千个随机数 - 但如果我输入 srand();在框架本身内,我制作了大约 2-8 帧。如果重要的话,我会使用 time(null) 来播种。

我调用 rand() 的频率越高;越早崩溃。

帮助?

【问题讨论】:

  • 您确定它在rand() 实现中崩溃了吗?您是否使用调试器来确认此行为?即使是这样,除非您可以在一个简单的案例中重现该问题 (int main(void) {while(1) rand(); }),否则我不会怀疑 rand 是您的问题。
  • 向我们展示最小的失败示例
  • 你是从多个线程调用 rand 吗?
  • 您是否能够在调试器下重现此问题并查看崩溃的位置/原因?或者,如果使用调试器不切实际,您是否已将其配置为创建故障转储,然后检查以查看故障在哪里?您是否正在注销数组的末尾,或者有一个悬空指针等可能会破坏内存并间接导致这些库例程崩溃?
  • 您可能在代码的其他地方做了一些调用未定义行为的事情。你能在一段代码中重现崩溃,除了重复调用 rand() 和 srand() 之外什么都不做?

标签: c++ random overflow srand


【解决方案1】:

函数 rand() 不是可重入的或线程安全的,因为它使用在每次调用时修改的隐藏状态。这可能只是种子值 被下一次调用使用,或者它可能更复杂。为了在线程应用程序中获得可重现的行为,此状态必须 明确。函数 rand_r() 提供了一个指向 unsigned int 的指针,用作状态。这是一个非常少量的状态, 所以这个函数将是一个弱伪随机生成器。请改用 drand48_r(3)。

【讨论】:

  • 我也有同样的想法,但显然这是一个单线程程序。
【解决方案2】:

关于如何缩小问题根源的一些方法和想法:

  • 几乎可以肯定不是srand()rand() 函数导致崩溃/锁定。有可能是一个或多个随机数组合使您的引擎进入了一个坏事发生的状态。
  • 第一步应该是复制问题,使其始终在时间/地点发生。不要使用srand(NULL),而是尝试使用像srand(12345) 这样的常量种子。根据您的引擎使用的其他因素(如用户输入),这可能足以让它每次都在同一个位置崩溃。
  • 如果使用调试器出现问题(这很可疑,可能是缓冲区溢出破坏了堆栈),请使用已尝试且真实的方法将消息输出到文本日志文件。我建议输出所有生成的随机数,也许您可​​能会在它崩溃时看到一种模式(即,每当生成“42”时它就会崩溃)。另一种选择是开始在各种功能中添加一些日志消息(从游戏更新循环等高级功能开始)。崩溃后检查日志并开始添加更多日志消息,直到将其缩小到一行/功能。这不像使用调试器那样快,但有时是更好的选择,尤其是当您真的不知道从哪里开始查找时。
  • 一旦您能够可靠地复制崩溃,就开始删除内容,直到崩溃点更改或消失。这可能涉及#ifdefs、注释代码、设置应用程序选项,甚至创建项目的临时副本,以便您可以简单地删除代码、编译和测试。如果项目很大/很复杂,这可能会很困难。
  • 有关“崩溃”类型的更多信息会有所帮助。通常程序不会只是一般性地崩溃,而是会发生某些异常、锁定等。异常详细信息可以帮助您通过一些努力缩小问题的根源。

【讨论】:

  • 我发现了错误,这是我什至没有想到的——敌人最终被其他敌人包围,这将导致他们的移动脚本无限循环并崩溃。虽然如果被墙壁包围,它们会保持原样,由于某种原因被其他实体包围并不会触发“静止”默认情况,因此“检查相邻方块是否有碰撞循环将永远持续” - 因此总是- 崩溃前的不同时间长度。这是一个特定的、独特的、挑剔的案例——但不可避免,而且更有可能有更多的敌人。
【解决方案3】:

尝试在调试器下运行它

$ gdb myprog
(gdb) break main
(gdb) run
(gdb) record

例如

(gdb) break abort
(gdb) break exit

因为是c++:

(gdb) catch throw
(gdb) catch exception

最后 (gdb) 继续

当它停止时,反向继续,直到找到罪魁祸首


选项 2:

valgrind --tool=massif --massif-out-file="massif.out.%p" myprog
ms_print massif.out.*

检查堆分析。您不太可能有内存泄漏

【讨论】:

    【解决方案4】:

    对 rand 的大量调用可能会产生一个您的代码无法处理的相对较小范围内的数字。尝试用一个只增加一个数字并返回它的函数替换你对 rand 的调用,看看它是否最终会失败。

    【讨论】:

      【解决方案5】:
      1. 您可能不应该使用 rand()。那里有更好的 PRNG。看看 Boost.Random。
      2. 您应该只 srand() 一次,而不是每一帧。
      3. 找出您的代码崩溃的地方。使用相当简单的调试器,只需启动附加了调试器的程序,然后等待它崩溃。
      4. 找出崩溃的位置后,找出崩溃的原因。
      5. 找出原因后,修复它。它可能与 rand() 没有任何关系。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-28
        • 1970-01-01
        • 1970-01-01
        • 2021-03-02
        • 2012-04-16
        相关资源
        最近更新 更多