【问题标题】:Inconsistent C++ <random> behavior between different computers不同计算机之间的 C++ <random> 行为不一致
【发布时间】:2014-05-08 17:18:01
【问题描述】:

我已经实现了一个 C++ 解决方案,用于测试针对一系列随机事件的各种策略。我正在汇总在几台计算机上运行的多线程模拟的结果。

单个模拟会产生一个整数结果,通常需要生成大约 100 个均匀随机整数,并且在保存一大块聚合数据(平均值、标准差、最小值、最大值)之前重复 1,000,000 次。尽管这些块的结果在给定架构上最多 6 位有效数字是一致的,但运行完全相同的程序的两台计算机之间的差异要大几个数量级。

到目前为止,我在两台配备 Intel 处理器和一个 AWS c3.8xlarge Windows Server 实例的个人 Windows 笔记本上运行了该程序(相同的可执行文件)。在每台计算机上,正在进行的模拟迅速接近不同的值。均值之间的相对差异约为 10^-3。在单台计算机上,100 万个块之间均值的相对差异很少超过 10^-6。

程序使用来自&lt;random&gt;mt19937 随机数生成器。我使用time(NULL) 播种。

我想不出这种不一致的原因。 Mersenne Twister 被认为是 Monte Carlo 模拟的声音发生器,我多次使用它,通常能够分析验证结果。由于生成器的缺陷和底层架构,我可以理解细微的差异和对​​一致性的偏离,但是对于这样的数量级,很难理解。

【问题讨论】:

  • 不知道您是如何使用随机性的,也不知道您如何准确地测量差异(10e-3 数字是相对的还是绝对的?)我们只能猜测。您是否尝试保存在一台机器上使用的种子,并尝试在另一台机器上复制它?会不会是你的笔记本上有 directx 调用改变了你背后的浮点标志?您是在程序启动时只为 PRNG 播种一次,还是多次播种?
  • 让我感到困惑,为什么您将这个 C++ 问题标记为 c
  • 我提供了相对差异。我只播种一次 PRNG。
  • 抱歉打错了标签。
  • @alethus 你能用更新编辑问题吗?使用最简单的情况:固定(相同)种子和单线程。对于下一次调查,确定您的模拟在哪里出现分歧。第一个分歧点。我会担心使用 80 位浮点寄存器或其他东西:分歧可能真的很早,我们可以从中得到 sscce.org

标签: c++ random simulation probability


【解决方案1】:

看来您已经能够解决您的问题。尽管如此,让我指出您的代码中的一两个(可能的)问题。如果没有真正看到任何源代码,很难为您做更多的事情。

  • 不要不要使用time(NULL)作为你的种子。这是一个非常低的熵源——这是相当糟糕的。首先,在不同机器上运行的程序的两个实例很可能会选择相同的种子,其次,两次连续运行只会有稍微不同的种子,这可能会导致相似或至少相关的随机数。如果您为每个线程创建一个 prng(建议这样做!),那么第二点尤其糟糕,因为那样所有线程可能只是创建相同的数字。至少使用 seed_seq,但更好的是,甚至是真正随机的来源 (random_device)。
  • 虽然标准保证,生成器mt19937 将在其 32 位变体和(显着)更快的 64 变体以及不同的硬件和软件版本中给出相同的结果,但发行版确实不会 给你同样的保证。如果你想重现相同的结果,你应该编写自己的发行版(恕我直言,这是标准的一个非常糟糕的设计缺陷,因为编写一个好的发行版是非常重要的......尽管在你的情况下这不应该是必要的,只要您只需要随机分布的数字,而无需重新创建特定的数字序列)。

虽然(正如我已经说过的)分布差异不太可能导致您的问题,但这是两个符合标准的实现之间唯一的差异。鉴于此,我建议彻底检查您的其余代码,因为&lt;random&gt; 库似乎不太可能在此处实际出错。

【讨论】:

  • 哦,我希望您在进行数字运算时或实际上在做任何事情时都以最高警告级别运行。我不知道 VC 标志,但是在 gcc 中,即使使用“-Wall -pedantic -Wextra -Wconversion -Wshadow”,每个程序都可以在没有警告的情况下进行编译,那么为什么不使用它们呢?
【解决方案2】:

在重构程序并消除不必要的操作后,结果在不同主机之间变得一致。似乎舍入误差在各种看似相似的 64 位架构之间存在显着差异,并且由于某些设计缺陷,它们的累积导致我的模拟结果出现严重分歧。我要感谢@DanielKO、@TonyD、@amdn 和@Yakk 提出的宝贵建议。

有趣的一点:从一开始,c3.8xlarge AWS 实例就始终提供相同(正确)的结果。相反,Core 2 遭受了最严重的打击。

【讨论】:

  • 理论上如果编译器设置为严格的 IEEE 754 一致性模式 FP 操作在任何编译器上都应该以相同的方式工作......你确定你没有启用一些“不安全”的优化(比如 @ 987654321@ 在 gcc 上)?
  • 该程序是在Visual Studio 2013 中使用/O2 和/MT 编译的。我没有使用任何自定义设置,并且仅限于标准库。该代码在我的 Core 2 笔记本上编译一次,并在另一台英特尔笔记本以及 AWS 的 c3.8xlarge 实例上进行了测试。它们都提供了不同的结果,只有后者始终是正确的。
  • 可能缺乏对long double 的支持(除了映射到double)造成了损失。
猜你喜欢
  • 1970-01-01
  • 2016-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多