Является ли Clang более детерминированным, чем GCC для разных платформ? - PullRequest
6 голосов
/ 10 июля 2011

Я рассматриваю возможность программирования многопользовательской RTS-игры (частично) на C ++.Я быстро обнаружил, что одно жесткое требование состоит в том, что игровая симуляция должна быть полностью детерминированной до самого последнего бита на сервере и на всех клиентах, чтобы иметь возможность ограничивать сетевую связь для пользовательского ввода, а не самого игрового состояния,Поскольку у каждого свой компьютер, это кажется сложной проблемой.

Итак, есть ли какой-то «волшебный» способ заставить компилятор C ++ создать исполняемый файл, который будет полностью детерминированным в Linux (на сервере),Windows и Mac?Я думаю, что двумя основными компиляторами OSS C ++ являются GCC и Clang, поэтому мне было интересно, будет ли один работать лучше, чем другой в этом отношении.

Мне также будет интересен любой набор тестов, который можно использовать для проверкиДетерминизм в C ++.

[EDIT] Под детерминизмом я имел в виду, что скомпилированная программа с заданным одинаковым начальным состоянием и входом в одинаковом порядке будет всегда выводить один и тот же вывод прилюбая платформа, где она работает.Так же и по сети.Последовательность звучит для меня как подходящее определение этого поведения, но я не являюсь носителем языка, поэтому я могу неверно истолковать точное значение.

[РЕДАКТИРОВАТЬ # 2] Во время дискуссий о том, имеет ли значение детерминизм / согласованность, иДолжен ли я стремиться к этому в игровом движке, и насколько велика проблема, как правило, в C ++, довольно интересно, но это никоим образом не отвечает на этот вопрос.До сих пор никто не знал, должен ли я использовать Clang или GCC для получения наиболее надежных / детерминированных / последовательных результатов.

[РЕДАКТИРОВАТЬ # 3] Мне просто пришло в голову, что есть способполучить точно такой же результат в C ++, как в Java.Нужно взять реализацию JVM с открытым исходным кодом и извлечь код, который реализует операторы и математические функции.Затем вы превращаете ее в автономную библиотеку и вызываете в ней встроенные функции вместо непосредственного использования операторов.Это было бы трудно сделать вручную, но если код сгенерирован, то это идеальное решение.Может быть, это даже можно сделать с помощью классов и перегрузки операторов, поэтому это тоже выглядит естественно.

Ответы [ 5 ]

1 голос
/ 26 февраля 2012

Не полагайтесь на неопределенное или неопределенное поведение (в частности, не используйте с плавающей запятой), и не имеет значения, какой компилятор вы используете.

Если a равно 1, иb равно 2, затем a + b равно 3. Это гарантируется стандартом языка.

C ++ не является областью, в которой в некоторых компиляторах некоторые вещи являются «детерминированными», а в других компиляторах они«т.C ++ констатирует некоторые факты (например, 1 + 2 == 3) и оставляет некоторые вещи на усмотрение компилятора (например, порядок вычисления аргументов функции).Если вывод вашей программы зависит только от первого (и ввода пользователя), и вы используете совместимый со стандартами компилятор, то ваша программа всегда будет выдавать один и тот же вывод при одинаковом вводе пользователя.

ЕслиВывод вашей программы также зависит, скажем, от операционной системы пользователя, тогда программа все еще детерминирована, просто вывод теперь определяется как вводом пользователя, так и его операционной системой.Если вы хотите, чтобы вывод зависел только от ввода пользователя, вы должны убедиться, что операционная система пользователя не влияет на вывод вашей программы.Один из способов сделать это - полагаться только на поведение, гарантированное языковым стандартом, и использовать компилятор, соответствующий этому стандарту.

В итоге весь код является детерминированным на основе его входных данных.Вам просто нужно убедиться, что вход состоит только из того, что вы хотите, чтобы это было.

1 голос
/ 10 июля 2011

Поскольку у каждого свой компьютер, это кажется сложной проблемой.

Это не так.На самом деле, этот вид сетей довольно прост, если вы не делаете ничего, что не определено спецификацией. IEEE-754 очень четко представляет, как именно нужно выполнять математику с плавающей запятой, как выполнять округление и т. Д., И она реализована одинаково на разных платформах.

Самое важноевам не нужно полагаться на инструкции процессора SIMD в коде, который должен быть детерминированным (примечание: это физика, ИИ и т. д. состояние игры. Не графика, где вам нужно SIMD).Эти виды команд быстро и свободно играют с правилами с плавающей запятой.Так что нет SIMD в вашем игровом коде;только в «клиентском» коде (графике, звуке и т. д.).

Также вам необходимо убедиться, что состояние вашей игры не зависит от таких вещей, как время;каждый тик часов состояния игры должен быть фиксированным временным интервалом, не зависящим от часов ПК или чего-либо подобного.

Очевидно, вам следует избегать любой случайной функции, к которой у вас нет кода.Но опять же, только для вашего основного игрового цикла;графические элементы могут быть специфичными для клиента, поскольку это просто визуальные эффекты и не имеет значения.

Это в значительной степени важно, если поддерживать синхронизацию двух состояний игры.Компилятор, который вы используете, не станет для вас большой проблемой.

Обратите внимание, что StarCraft и StarCraft II используют это в качестве основы своей сетевой модели.Они оба работают на Mac и ПК, и оба могут играть друг против друга.Так что это очень возможно и не требует Clang.

Хотя, если вам нравится Clang, вы должны его использовать.Но это должно быть потому, что вам это нравится, а не для работы в сети.

0 голосов
/ 10 июля 2011

Если вы начнете использовать плавающую точку, все ваши ставки выключены.Вы столкнетесь с трудностями при поиске / устранении проблем, когда вы получите разные значения даже на одной и той же платформе, просто выбрав процессор Intel или AMD.

Многие библиотеки времени выполнения оптимизировали пути кода для разных чипов.Все это в рамках спецификации, но некоторые чуть точнее.Это приводит к незначительным ошибкам округления, которые рано или поздно накапливаются до разницы, которая может сломать вещи.

Ваша цель должна состоять в том, чтобы уйти без 100% детерминизма.В конце концов: имеет ли значение для игрока, если противник на пиксель больше влево, чем должен?Это не.Важно то, что небольшие различия между клиентами и сервером не портят игровой процесс.

То, что видит игрок на своем экране, должно выглядеть детерминированным, поэтому он не чувствует себя обманутым, но это ни в коем случаетребуется путь.

Игры, над которыми я работал, архивировали это путем постоянной ресинхронизации состояния игры всех сущностей между всеми клиентами.Однако мы почти никогда не отправляли все игровое состояние, но мы отправляли состояние игры из нескольких объектов в каждом кадре, распределяя задание в течение нескольких секунд.

Просто дайте объектам, которые имеют значение, более высокий приоритет, чем другим, и все будет хорошо.,Например, в гоночных автомобильных играх не имеет значения иметь точное местоположение автомобиля соперника, если он находится далеко от вас, и его можно обновлять только каждые 20 секунд или около того.

Между этими обновлениямипросто верьте, что маленькие ошибки округления не накапливаются так сильно, что вы сталкиваетесь с неприятностями.

0 голосов
/ 10 июля 2011

Это что-то вроде глупого поручения.Ваша программа не будет «полностью детерминированной» (что бы это ни значило) «до последнего бита» на машине с прямым порядком байтов по сравнению с машиной с прямым порядком байтов, ни на 64-битной машине, ни на 32-битной машине, а на какой-то другой случайной машине.

Говоря о случайности, во многих играх есть элемент случайности.Если вы достигаете этого, вызывая стандартную функцию c rand (), все ставки выключены.

0 голосов
/ 10 июля 2011

Я думаю, какой компилятор вы используете, не имеет большого значения.

Такой полностью детерминированный подход использовался, например, в Doom.Вместо генератора случайных чисел они использовали фиксированный массив случайных чисел.Время игры измерялось в игровых тиках (которые, как я помню, составляли около 1/30 секунды).

Если вы измеряете все по игровой механике, а не выкладываете свою работу в некоторые стандартные библиотеки,которые могут быть в различных версиях, я считаю, что вы должны добиться хорошей переносимости между различными машинами.Разумеется, если эти машины будут достаточно быстрыми для запуска вашего кода!

Однако сетевое взаимодействие может создавать проблемы самостоятельно: задержки, пропадания и т. Д. Ваша игра должна быть способна обрабатывать задержанные сообщенияи, при необходимости, ресинхронизировать себя.Например, вы можете время от времени отправлять полное (или, по крайней мере: более подробное) состояние игры, а не полагаться только на пользовательский ввод.

Подумайте также о возможных подвигах:

  • Клиент: Я бросаю гранату
  • Сервер: У вас нет гранат
  • Клиент: Мне все равно.Я все же бросаю гранату
...