跨平台浮点一致性

 夜笙_ciel 发布于 2023-02-06 10:19

我正在开发一个使用锁步模型在网络上播放的跨平台游戏.作为简要概述,这意味着只传达输入,并且在每个客户端的计算机上模拟所有游戏逻辑.因此,一致性和决定性非常重要.

我正在编译使用GCC 4.8.1的MinGW32上的Windows版本,而在Linux上我正在使用GCC 4.8.2进行编译.

最近让我感到震惊的是,当我的Linux版本连接到我的Windows版本时,即使在两台机器上编译相同的代码,程序也会立即发生分歧或解除同步!原来问题是Linux构建是通过64位编译的,而Windows版本是32位.

在编译Linux 32位版本之后,我很幸运地解决了问题已经解决的问题.然而,它让我思考和研究浮点决定论.

这就是我收集的内容:

如果是,程序通常是一致的:

在相同的架构上运行

使用相同的编译器编译

因此,如果我假设,针对PC市场,每个人都有一个x86处理器,那么解决了一个要求.但是,第二个要求似乎有点傻.

MinGW,GCC和Clang(分别是Windows,Linux,Mac)都是基于/兼容GCC的不同编译器.这是否意味着实现跨平台决定论是不可能的?或者它只适用于Visual C++ vs GCC?

同样,优化标志-O1或-O2会影响这种决定论吗?让他们离开会更安全吗?

最后,我有三个问题要问:

1)在编译器中使用MinGW,GCC和Clang时,跨平台确定性是否可行?

2)应在这些编译器中设置哪些标志以确保操作系统/ CPU之间的最大一致性?

3)浮点精度对我来说并不重要 - 重要的是它们是一致的.有没有任何方法可以将浮点数减少到较低的精度(如3-4个小数位),以确保跨系统的小圆整错误不存在?(到目前为止我尝试写的每个实现都失败了)

编辑:我做过一些跨平台的实验.

使用浮点数表示速度和位置,我保持Linux Intel笔记本电脑和Windows AMD台式电脑同步,最多可以浮动值的15位小数.但是,这两个系统都是x86_64.测试很简单 - 它只是通过网络移动实体,试图确定任何可见的错误.

如果x86计算机连接到x86_64计算机,假设相同的结果会成立是否有意义?(32位与64位操作系统)

1 个回答
  • 跨平台和交叉编译器的一致性当然是可能的.只要有足够的知识和时间,任何事都有可能!但它可能非常困难,或者非常耗时,或者确实不切实际.

    以下是我可以预见的问题,没有特别的顺序:

      请记住,即使是正负1/10 ^ 15的极小误差也可能会变得非常重要(您将该数字与该误差范围相乘10亿,现在您有一个加或减0.000001错误,可能很重要.)这些错误会在很多帧上累积,直到你有一个去同步的模拟.或者,当你比较值时,它们就会显现出来(即使在浮点比较中使用"epsilons"也是天真的,也可能没有帮助;只能取代或延迟表现.)

      上述问题并不是分布式确定性模拟(如你的)所特有的.关于" 数值稳定性 " 问题的讨论,这是一个困难且经常被忽视的主题.

      不同的编译器优化开关和不同的浮点行为确定开关可能导致编译器为相同的语句生成稍微不同的CPU指令序列.显然,使用完全相同的编译器,这些编译必须相同,否则生成的代码必须经过严格的比较和验证.

      32位和64位程序(注意:我说的是程序而不是CPU)可能会表现出略微不同的浮点行为.默认情况下,32位程序不能依赖比CPU更高级的任何东西(没有SSE,SSE2,AVX等),除非您在编译器命令行中指定它(或使用ininsics/inline汇编指令)你的代码.)另一方面,64位程序保证在支持SSE2的CPU上运行,因此默认情况下编译器将使用这些指令(同样,除非被用户覆盖.)x87和SSE2浮点型数据类型对它们的操作是相似的,它们是--AFAIK--不完全相同.如果一个程序使用一个指令集而另一个程序使用另一个程序集,则会导致模拟中的不一致.

      x87指令集包括一个"控制字"寄存器,它包含控制浮点运算某些方面的标志(例如精确的舍入行为等).这是运行时的事情,你的程序可以做一组计算,然后更改此寄存器,然后执行完全相同的计算并获得不同的结果.显然,必须检查和处理该寄存器并在不同的机器上保持相同.编译器(或您在程序中使用的库)可以生成在运行时在程序中不一致地更改这些标志的代码.

      同样,在x87指令集的情况下,英特尔和AMD在历史上实现的方式略有不同.例如,一个供应商的CPU可能在内部使用更多位进行一些计算(因此得到更准确的结果),另一个,这意味着如果您碰巧在两个不同供应商的两个不同CPU(两个x86)上运行,简单计算的结果可能不一样.我不知道这些更高精度的计算是如何以及在什么情况下启用的,以及它们是否在正常操作条件下发生,或者您必须具体询问它们,但我确实知道存在这些差异.

      随机数和在程序中一致且确定地生成它们与浮点一致性无关.它是许多错误的重要和来源,但最后它只需要保持同步状态的几个位.

    以下是一些可能有用的技术:

      有些项目使用" 定点 "数字和定点算法来避免舍入误差和浮点数的一般不可预测性.阅读Wikipedia文章以获取更多信息和外部链接.

      在我自己的一个项目中,在开发期间,我曾经在游戏的所有实例中散列所有相关状态(包括许多浮点数),并在每个帧中通过网络发送散列以确保甚至一位那种状态在不同的机器上并没有什么不同.这也有助于调试,而不是相信我的眼睛,看看存在何时何地出现不一致(这不会告诉我它们起源于哪里,反正)我会知道一台机器上游戏状态的某些部分开始出现分歧从其他人那里,并确切知道它是什么(如果哈希检查失败,我将停止模拟并开始比较整个状态.)
      此功能从一开始就在该代码库中实现,并且仅在开发过程中使用帮助调试(因为它有性能和内存成本.)

    更新(回答下面的第一条评论):正如我在第1点所说,其他人在其他答案中说过,这并不保证任何事情.如果这样做,您可能会降低发生不一致的概率和频率,但可能性不会变为零.如果您没有仔细和系统地分析代码中发生的事情以及可能的问题来源,那么无论您多少"完成"数字,仍然可能会遇到错误.

    例如,如果您有两个数字(例如,两个计算的结果应该产生相同的结果),即1.111499999和1.111500001,并将它们舍入到三个小数位,则它们分别变为1.111和1.112.原始数字的差异仅为2E-9,但现在已变为1E-3.事实上,你的错误增加了500'000倍.即使是四舍五入,他们仍然不平等.你加剧了这个问题.

    确实,这并没有发生太多,我给出的例子是两个不吉利的数字来处理这种情况,但仍然可以找到这些数字.当你这样做时,你就遇到了麻烦.唯一可靠的解决方案,即使您使用定点算术或其他任何方法,也要对所有可能的问题区域进行严格而系统的数学分析,并证明它们在各个程序中保持一致.

    除此之外,对于我们凡人来说,你需要有一个防水的方式来监控情况,找到确切的时间和方式出现最轻微的差异,以便能够在事后解决问题(而不是依靠你的眼睛)查看游戏动画或对象移动或物理行为中的问题.)

    2023-02-06 10:21 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有