Clang 在跨平台上是否比 GCC 更具确定性?

发布于 11-19 05:13 字数 779 浏览 3 评论 0原文

我正在考虑用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

狂之美人2024-11-26 05:13:05

由于每个人都有不同的计算机,这似乎是一个难题。

它不是。事实上,这种网络非常简单,只要你不做任何规范中未定义的事情即可。 IEEE-754 非常清楚浮点数学是如何完成的,如何进行需要进行舍入等操作,并且跨平台的实现方式相同。

您需要避免做的最重要的事情是在需要确定性的代码中依赖 SIMD CPU 指令(注意:这是物理、人工智能等:游戏状态。而不是图形,这是您需要 SIMD 的地方)。这类指令与浮点规则的关系是快速且宽松的。所以你的游戏代码中没有SIMD;仅在“客户端”代码(图形、声音等)中。

另外,您需要确保您的游戏状态不依赖于时间等因素;每个游戏状态时钟滴答应该是固定的时间间隔,而不是基于 PC 的时钟或任何类似性质的东西。

显然,您应该避免任何您没有代码的随机函数。但同样,仅适用于您的主要游戏循环;图形内容可以是特定于客户的,因为它只是视觉效果,并不重要。

就保持两个游戏状态同步而言,就这样了。您使用的编译器对您来说不会是一个大问题。

请注意,《星际争霸》和《星际争霸 II》使用此作为其网络模型的基础。它们都可以在 Mac 和 PC 上运行,并且都可以相互对战。所以这是很有可能的,而且不需要 Clang。

不过如果你喜欢 Clang,你应该使用它。但这应该是因为你喜欢它,而不是为了社交。

Since everyone has a different computer, this seems like a hard problem.

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.

毁我热情2024-11-26 05:13:05

不要依赖未定义或未指定的行为(特别是不要使用浮点),并且使用什么编译器并不重要。

如果a为1,b为2,则a + b为3。这是由语言标准保证的。

C++ 并不是一个在某些编译器中某些事情是“确定性的”而在其他编译器中却不是的土地。 C++ 陈述了一些事实(例如 1 + 2 == 3),并将一些事情留给编译器(例如函数参数的求值顺序)。如果程序的输出仅取决于前者(以及用户的输入),并且您使用符合标准的编译器,那么对于给定相同的用户输入,您的程序将始终产生相同的输出。

如果程序的输出也取决于(比如说)用户的操作系统,那么程序仍然是确定性的,只是输出现在由用户的输入和操作系统共同决定。如果您希望输出仅依赖于用户的输入,则您需要确保用户的操作系统不是程序输出的影响因素。实现此目的的一种方法是仅依赖语言标准保证的行为,并使用符合该标准的编译器。

总之,所有代码都是确定性的,基于其输入。您只需确保输入仅由您想要的内容组成。

Don't rely on undefined or unspecified behaviour, (particularly, don't use floating-point), and it doesn't matter what compiler you use.

If a is 1, and b is 2, then a + b is 3. This is guaranteed by the language standard.

C++ isn't a land in which in some compilers some things are 'deterministic' and in other compilers they aren't. C++ states some facts (like 1 + 2 == 3) and leaves some things up to the compiler (like order of evaluation of function arguments). If the output of your program only depends on the former (and the user's input), and you use a standards-compliant compiler, then your program will always produce the same output given the same user input.

If the output of your program also depends on (say) the user's operating system, then the program's still deterministic, it's just that the output is now determined by both the user's input and their operating system. If you want the output only dependent on the user's input, it's up to you to ensure that the user's operating system is not a contributing factor to the output of your program. One way to do this is to only rely on behaviour guaranteed by the language standard, and to use a compiler that conforms to that standard.

In summary, all code is deterministic, based on its input. You just have to make sure that the input is made up only of the things you want it to be.

瘫痪情歌2024-11-26 05:13:05

我认为你使用哪个编译器并不重要。

例如,《毁灭战士》中就使用了这种完全确定性的方法。
他们使用固定的“随机”数字数组而不是随机数字生成器。游戏时间以游戏中的滴答声来衡量(如果我记得的话,大约是 1/30 秒)。

如果您通过游戏中的机制来衡量一切,而不是将您的工作卸载到某些可能有各种版本的标准库,我相信您应该在不同的机器上实现良好的可移植性。当然,前提是这些机器足够快来运行您的代码!

然而,网络通信本身也会产生问题:延迟、掉线等。
您的游戏应该能够处理延迟的消息,并在必要时自行重新同步。
例如,您可能希望不时发送完整(或至少:更详细)的游戏状态,而不是仅依赖于用户输入。

还要考虑可能的利用:

  • 客户端:我正在扔手榴弹
  • 服务器:你没有手榴弹
  • 客户端:我不在乎。尽管如此,我还是扔了一颗手榴弹

I think which compiler you are using does not matter that much.

Such fully-deterministic approach was used in Doom for example.
Instead of random-number generator they were using a fixed "random"-number array. Game time was measured in in-game ticks (which was about 1/30 of a second if I remember).

If you measure everything by in-game mechanics, rather than offloading your work to some standard libraries, which may come in various versions, I believe you should achieve good portability across different machines. Provided, of course, if those machines will be fast enough to run your code!

However, the network communication can create troubles on its own: latencies, drops, etc.
Your game should be able to handle delayed messages and, if necessary, resynchronise itself.
You might want, for example, send a full (or at least: more verbose) game state from time to time, rather than relying only on the user input.

Think also about possible exploits:

  • Client: I am throwing a grenade
  • Server: You have no grenades
  • Client: I don't care. I am throwing a grenade nevertheless
素染倾城色2024-11-26 05:13:05

这有点傻瓜式的差事。您的程序在大端机器与小端机器上,或者在 64 位机器与 32 位机器与其他随机机器上,都不会“完全确定”(无论这意味着什么)“到最后一位”。

说到随机性,许多游戏都有随机性的元素。如果您通过调用 c 标准函数 rand() 来实现这一点,那么一切都将失败。

This is somewhat of a fools errand. Your program will not be "fully deterministic" (whatever that means) "to the very last bit" on a big endian machine versus a little endian machine, nor on a 64 bit machine versus a 32 bit machine versus some other random machine.

Speaking of random, many games have an element of randomness. If you are achieving this by calling the c-standard function rand(), all bets are off.

昔梦2024-11-26 05:13:05

如果您开始使用浮点,那么您的所有赌注都会失败。即使在同一平台上,仅通过选择 Intel 或 AMD cpu,您也会遇到难以发现/修复的问题,即获得不同的值。

许多运行时库都针对不同芯片优化了代码路径。这些都在规范范围内,但有些比规范稍微精确一些。这会导致微妙的舍入误差,这些误差迟早会累积成可能会破坏事物的差异。

你的目标应该是在没有 100% 确定性的情况下离开。毕竟:如果对手比应有的位置向左偏左一个像素,这对玩家来说有什么影响吗?它不是。重要的是,客户端和服务器之间的微小差异不会破坏游戏玩法。

玩家在屏幕上看到的内容应该看起来是确定的,这样他就不会感到被欺骗,但这绝不是必需的。

我所开发的游戏通过不断地重新同步所有客户端之间所有实体的游戏状态来存档这一点。然而,我们几乎从未发送过整个游戏状态,而是发送了每帧几个对象的游戏状态,在几秒钟内分配了作业。

只要给予最重要的对象比其他对象更高的优先级就可以了。例如,在赛车游戏中,如果对手汽车离您很远,那么掌握对手汽车的确切位置并不重要,只需每 20 秒左右更新一次就可以了。

在这些更新之间,请相信小的舍入误差不会累积得太多而导致您遇到麻烦。

If you start using floating point all your bets are off. You will run into hard to find/fix problems where you get different values even on the same platform, just by choosing a Intel or AMD cpu.

Lots of runtime libraries have optimized code-paths for different chips. These are all within the spec, but some are slightly more precise than that. This results in subtle roundoff errors that sooner or later accumulate to a difference that may break things.

Your goal should be to get away without 100% determinisim. After all: Does it matter for the player if a opponent is a pixel more to the left than it should? It is not. What is important is, that the little differences between clients and server don't ruin the gameplay.

What the player sees on his screen should look deterministic, so he doesn't feel cheated, but it is in no way required.

The games I've worked on archived this by constantly resynchronizing the game-state of all entities between all clients. We did however almost never sent the entire gamestate but we sent the game state of few objects each frame distributing the job over several seconds.

Just give the objects where it matters most higher priority than other and it will be fine. In a car racing game for example it does not matter to have the exact position of an opponent car if it is far away from you, and it's fine to only update it every 20 seconds or so.

Inbetween these updates just trust that the little round-off errors don't accumulate so much that you run into troubles.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文