gpt4 book ai didi

c# - .NET Core 中 gamedev 的浮点确定性

转载 作者:太空宇宙 更新时间:2023-11-03 12:07:51 27 4
gpt4 key购买 nike

背景

我们正在使用 C# 和 .NET Core 处理 RTS game engine。与大多数其他实时多人游戏不同,RTS 游戏倾向于将玩家输入同步给其他玩家,并同时在所有客户端上同步运行游戏模拟。这要求游戏逻辑是确定性的,这样游戏就不会失去同步。

不确定性的一个潜在来源是浮点运算。据我所知,主要问题是旧的 x87 FPU 指令 - 它们使用内部 80 位寄存器,而 IEEE-754 浮点值是 32 位或 64 位,因此从寄存器移动时值会被截断到内存中。对代码和/或编译器的小改动可能会导致在不同时间发生截断,从而导致结果略有不同。不确定性也可能是由于意外使用不同的 FP 舍入模式引起的,但如果我理解正确的话,这基本上是一个已解决的问题。

我还有 gotten the impression,SSE(2) 指令不会遇到截断问题,因为它们在没有更高精度寄存器的情况下以 32 位或 64 位执行所有浮点运算。

最后,据我所知,CLR 在 x86 上使用 x87 FPU 指令(或者至少在 RyuJIT 之前是这样),在 x86-64 上使用 SSE 指令。我不确定这是否意味着所有或大多数操作。

.NET Core 最近添加了对准确 single precision math 的支持,如果这很重要的话。

但是在研究是否可以在 .NET 中确定性地使用 float 时,有很多答案是否定的,尽管它们主要涉及运行时的旧版本。

  • 在 2013 年的 StackOverflow answer 中,Eric Lippert 说如果你想保证 .NET 中的可重现算法,你应该“使用整数”。
  • 在 2017 年 Roslyn 的 GitHub 页面上关于该主题的讨论中,一位游戏开发者 said in a comment 表示他们无法在 C# 中实现可重复的浮点运算,尽管他没有具体说明他们使用了哪些运行时。
  • 在 2011 年游戏开发堆栈交换 answer 中,作者得出结论,他无法在 .NET 中获得可靠的 FP 算法。他为 .NET 提供了基于软件的浮点实现,与 IEEE754 浮点二进制兼容。

问题

那么,如果 CoreCLR 在 x86-64 上使用 SSE FP 指令,是否意味着它不会遇到截断问题和/或任何其他与 FP 相关的非确定性问题?我们将 .NET Core 与引擎一起发布,因此每个客户端都将使用相同的运行时,并且我们会要求玩家使用完全相同版本的游戏客户端。限制引擎仅在 x86-64(在 PC 上)上工作也是一个可接受的限制。

如果运行时仍然使用结果不可靠的 x87 指令,使用软件浮点实现(如上面答案中链接的那个)来计算单个值是否有意义,并使用新的 hardware intrinsics 加速 SSE 的矢量运算?我已经对此进行了原型(prototype)设计,它似乎可行,但没有必要吗?

如果我们只能使用普通的浮点运算,有什么我们应该避免的,比如三角函数吗?

最后,如果到目前为止一切正常,当不同的客户端使用不同的操作系统甚至不同的 CPU 架构时,这将如何工作?假设实现没有错误,现代 ARM CPU 是否会遇到 80 位截断问题,或者相同的代码是否会以与 x86 相同的方式运行(如果我们排除三角函数等更棘手的东西)?

最佳答案

So, if CoreCLR uses SSE FP instructions on x86-64, does that mean that it doesn't suffer from the truncation issues, and/or any other FP-related non-determinism?

如果您继续使用 x86-64 并且在任何地方都使用完全相同的 CoreCLR 版本,那么它应该是确定性的。

If the runtime still uses x87 instructions with unreliable results, would it make sense to use a software float implementation [...] I've prototyped this and it seems to be work, but is it unnecessary?

这可能是解决 JIT 问题的解决方案,但您可能必须开发一个 Roslyn 分析器,以确保您在不经过这些操作的情况下不使用浮点运算......或者编写一个 IL 重写器,它将为您执行此操作(但这会使您的 .NET 程序集依赖架构......根据您的要求,这可能是可以接受的)

If we can just use normal floating point operations, is there anything we should avoid, like trigonometric functions?

据我所知,CoreCLR 正在将数学函数重定向到编译器 libc,因此只要您保持在同一版本、同一平台上,就应该没问题。

Finally, if everything is OK so far how would this work when different clients use different operating systems or even different CPU architectures? Do modern ARM CPUs suffer from the 80-bit truncation issue, or would the same code run identically to x86 (if we exclude trickier stuff like trigonometry), assuming the implementation has no bugs?

您可能会遇到一些与额外精度无关的问题。例如,对于 ARMv7,次正规 float 被刷新为零,而 aarch64 上的 ARMv8 将保留它们。

假设您继续使用 ARMv8,我不太清楚 ARMv8 的 JIT CoreCLR 在这方面是否表现良好;您可能应该直接在 GitHub 上询问。还有 libc 的行为可能会破坏确定性结果。

我们正致力于在 Unity 中解决这个问题,使用我们的“突发”编译器将 .NET IL 转换为 native 代码。我们在所有机器上使用 LLVM codegen,禁用一些可能破坏确定性的优化(所以在这里,总体上我们可以尝试保证跨平台编译器的行为),我们还使用 SLEEF 库来提供确定性计算数学函数(例如参见 https://github.com/shibatch/sleef/issues/187 )……所以可以做到。

在你的位置上,我可能会尝试调查 CoreCLR 是否真的对 x64 和 ARMv8 之间的普通浮点运算具有确定性......如果它看起来没问题,你可以调用这些 SLEEF 函数而不是 System.Math 它可以开箱即用,或者建议 CoreCLR 从 libc 切换到 SLEEF。

关于c# - .NET Core 中 gamedev 的浮点确定性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53989518/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com