Clang 在跨平台上是否比 GCC 更具确定性?
我正在考虑用 C++ 编写多用户 RTS 游戏(部分)的可行性。我很快发现,一个硬性要求是游戏模拟必须完全确定服务器和所有客户端的最后一位,以便能够将网络通信限制为用户输入,而不是游戏状态本身。由于每个人都有不同的计算机,这似乎是一个难题。
那么,是否有一些“神奇”的方法可以让 C++ 编译器创建一个跨 Linux(服务器)、Windows 和 Mac 完全确定性的可执行文件?我认为两个主要的 OSS C++ 编译器是 GCC 和 Clang,所以我想知道在这方面一个是否比另一个表现更好。
我还对任何可用于验证 C++ 确定性的测试套件感兴趣。
[编辑] 通过确定性,我的意思是,在给定相同的初始状态和相同顺序的输入的情况下,编译后的程序将在任何运行的平台上始终产生相同的输出。所以,也通过网络。对我来说,一致听起来像是对这种行为的适当定义,但我不是母语人士,所以我可能会误解确切的含义。
[EDIT#2] 虽然关于确定性/一致性是否重要,以及我是否应该在游戏引擎中瞄准这一点,以及它在 C++ 中通常是一个多大的问题的讨论非常有趣,但它并没有以任何方式实际回答问题。到目前为止,没有人有任何事实告诉我是否应该使用 Clang 还是 GCC 来获得最可靠/确定性/一致的结果。
[EDIT#3] 我刚刚想到有一种方法可以在 C++ 中获得与 Java 中完全相同的结果。人们必须采用 JVM 的开源实现,并提取实现运算符和数学函数的代码。然后将其变成一个独立的库并在其中调用可内联函数,而不是直接使用运算符。手动完成会很痛苦,但如果生成代码,那么它就是一个完美的解决方案。也许这甚至可以通过类和运算符重载来完成,所以它看起来也很自然。
I'm considering the feasibility of programming a multi-user RTS game (partly) in C++. What I quickly discovered, is that one hard requirement is that the game simulation must be fully deterministic to the very last bit across the server and all clients, to be able to limit the network communication to the user input, and not the game state itself. Since everyone has a different computer, this seems like a hard problem.
So, is there some "magic" way of getting the C++ compiler to create an executable that will be fully deterministic across Linux (the server), Windows and Mac? I think the two main OSS C++ compilers are GCC and Clang, so I was wondering if one performs better than the other in this regard.
I would also be interested in any test-suite that could be used to validate C++ determinism.
[EDIT] By deterministic, I meant that the compiled program, given the same initial state, and the input in the same order, will always produce the same output, on any platform where it runs. So, also across the network. Consistent sounds like an appropriate definition of this behavior to me, but I'm not a native speaker, so I might misinterpret the exact meaning.
[EDIT#2] While discussions about whether determinism/consistency matters, and whether I should aim for that in a game engine, and how big a problem it generally is in C++, is quite interesting, it does not in any way actually answer the question. So far, no one had any fact telling me if I should use Clang or GCC to get the most reliable/deterministic/consistent results.
[EDIT#3] It just occurred to me that there IS a way to get exactly the same result in C++ as in Java. One has to take an open source implementation of the JVM, and extract the code that implements the operators and mathematical functions. Then you turn it into a stand-alone library and call inlineable functions in it, instead of using operators directly. It would be a pain to do by hand, but if the code is generated, then it's a perfect solution. Maybe that could even be done with classes and operator overloading, so it looks natural as well.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(5)
不要依赖未定义或未指定的行为(特别是不要使用浮点),并且使用什么编译器并不重要。
如果a
为1,b
为2,则a + b
为3。这是由语言标准保证的。
C++ 并不是一个在某些编译器中某些事情是“确定性的”而在其他编译器中却不是的土地。 C++ 陈述了一些事实(例如 1 + 2 == 3),并将一些事情留给编译器(例如函数参数的求值顺序)。如果程序的输出仅取决于前者(以及用户的输入),并且您使用符合标准的编译器,那么对于给定相同的用户输入,您的程序将始终产生相同的输出。
如果程序的输出也取决于(比如说)用户的操作系统,那么程序仍然是确定性的,只是输出现在由用户的输入和操作系统共同决定。如果您希望输出仅依赖于用户的输入,则您需要确保用户的操作系统不是程序输出的影响因素。实现此目的的一种方法是仅依赖语言标准保证的行为,并使用符合该标准的编译器。
总之,所有代码都是确定性的,基于其输入。您只需确保输入仅由您想要的内容组成。
我认为你使用哪个编译器并不重要。
例如,《毁灭战士》中就使用了这种完全确定性的方法。
他们使用固定的“随机”数字数组而不是随机数字生成器。游戏时间以游戏中的滴答声来衡量(如果我记得的话,大约是 1/30 秒)。
如果您通过游戏中的机制来衡量一切,而不是将您的工作卸载到某些可能有各种版本的标准库,我相信您应该在不同的机器上实现良好的可移植性。当然,前提是这些机器足够快来运行您的代码!
然而,网络通信本身也会产生问题:延迟、掉线等。
您的游戏应该能够处理延迟的消息,并在必要时自行重新同步。
例如,您可能希望不时发送完整(或至少:更详细)的游戏状态,而不是仅依赖于用户输入。
还要考虑可能的利用:
- 客户端:我正在扔手榴弹
- 服务器:你没有手榴弹
- 客户端:我不在乎。尽管如此,我还是扔了一颗手榴弹
如果您开始使用浮点,那么您的所有赌注都会失败。即使在同一平台上,仅通过选择 Intel 或 AMD cpu,您也会遇到难以发现/修复的问题,即获得不同的值。
许多运行时库都针对不同芯片优化了代码路径。这些都在规范范围内,但有些比规范稍微精确一些。这会导致微妙的舍入误差,这些误差迟早会累积成可能会破坏事物的差异。
你的目标应该是在没有 100% 确定性的情况下离开。毕竟:如果对手比应有的位置向左偏左一个像素,这对玩家来说有什么影响吗?它不是。重要的是,客户端和服务器之间的微小差异不会破坏游戏玩法。
玩家在屏幕上看到的内容应该看起来是确定的,这样他就不会感到被欺骗,但这绝不是必需的。
我所开发的游戏通过不断地重新同步所有客户端之间所有实体的游戏状态来存档这一点。然而,我们几乎从未发送过整个游戏状态,而是发送了每帧几个对象的游戏状态,在几秒钟内分配了作业。
只要给予最重要的对象比其他对象更高的优先级就可以了。例如,在赛车游戏中,如果对手汽车离您很远,那么掌握对手汽车的确切位置并不重要,只需每 20 秒左右更新一次就可以了。
在这些更新之间,请相信小的舍入误差不会累积得太多而导致您遇到麻烦。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
它不是。事实上,这种网络非常简单,只要你不做任何规范中未定义的事情即可。 IEEE-754 非常清楚浮点数学是如何完成的,如何进行需要进行舍入等操作,并且跨平台的实现方式相同。
您需要避免做的最重要的事情是在需要确定性的代码中依赖 SIMD CPU 指令(注意:这是物理、人工智能等:游戏状态。而不是图形,这是您需要 SIMD 的地方)。这类指令与浮点规则的关系是快速且宽松的。所以你的游戏代码中没有SIMD;仅在“客户端”代码(图形、声音等)中。
另外,您需要确保您的游戏状态不依赖于时间等因素;每个游戏状态时钟滴答应该是固定的时间间隔,而不是基于 PC 的时钟或任何类似性质的东西。
显然,您应该避免任何您没有代码的随机函数。但同样,仅适用于您的主要游戏循环;图形内容可以是特定于客户的,因为它只是视觉效果,并不重要。
就保持两个游戏状态同步而言,就这样了。您使用的编译器对您来说不会是一个大问题。
请注意,《星际争霸》和《星际争霸 II》使用此作为其网络模型的基础。它们都可以在 Mac 和 PC 上运行,并且都可以相互对战。所以这是很有可能的,而且不需要 Clang。
不过如果你喜欢 Clang,你应该使用它。但这应该是因为你喜欢它,而不是为了社交。
It's not. Really, this kind of networking is quite simple, so long as you don't do anything that is undefined by the specification. IEEE-754 is very clear on exactly how floating-point math is to be done, how rounding is to be done, etc, and it is implemented identically across platforms.
The biggest thing you need to not do is rely on SIMD CPU instructions in code that needs to be deterministic (note: this is physics, AI, and such: game state. Not graphics, which is where you need SIMD). These kinds of instructions play fast-and-loose with the floating-point rules. So no SIMD in your game code; only in the "client" code (graphics, sound, etc).
Also, you need to make sure that your game state doesn't depend on things like the time; each game state clock tick should be a fixed time interval, not based on the PC's clock or anything of that nature.
Obviously, you should avoid any random function you don't have the code to. But again, only for your main gameplay loop; the graphics stuff can be client-specific, since it's just visuals and doesn't matter.
That is pretty much it, as far as keeping the two game states in sync. The compiler you use isn't going to be a big issue for you.
Note that StarCraft and StarCraft II use this as the basis of their networking model. They both run on Macs and PCs, and both can play against each other. So it's very possible, and doesn't require Clang.
Though if you like Clang, you should use it. But that should be because you like it, not for networking.