Детерминизм с плавающей точкой для gamedev в .NET Core - PullRequest
0 голосов
/ 31 декабря 2018

Фон

Мы работаем над игровым движком RTS с использованием C # и .NET Core.В отличие от большинства других многопользовательских игр в реальном времени, игры RTS, как правило, работают путем синхронизации входов игроков с другими игроками и одновременного запуска симуляции игры на всех клиентах.Это требует, чтобы игровая логика была детерминированной, чтобы игры не выходили из синхронизации.

Одним из потенциальных источников недетерминизма являются операции с плавающей запятой.Из того, что я понял, основная проблема связана со старыми инструкциями x87 FPU - они используют внутренний 80-разрядный регистр, в то время как значения с плавающей запятой IEEE-754 являются 32-разрядными или 64-разрядными, поэтому значения усекаются при перемещении из регистров.в память.Небольшие изменения в коде и / или компиляторе могут привести к усечению в разное время, что приведет к несколько иным результатам.Недетерминизм также может быть вызван случайным использованием различных режимов округления FP, хотя, если я правильно понял, это в основном решенная проблема.

У меня также сложилось впечатление , что SSE (2) инструкции не страдают от проблемы усечения, так как они выполняют всю арифметику с плавающей запятой в 32- или 64-битном формате без регистра с более высокой точностью.

Наконец, насколько мне известно, CLR использует инструкции FPU x87 дляx86 (или, по крайней мере, так было до RyuJIT) и инструкции SSE для x86-64.Я не уверен, что это означает для всех или для большинства операций.

Поддержка точной математики с одинарной точностью была недавно добавлена ​​в .NET Core, если это имеет значение.

Но при исследовании возможности детерминированного использования с плавающей запятой в .NET есть много ответов, которые говорят «нет», хотя в основном они касаются более старых версий среды выполнения.

  • В Ответ StackOverflow от 2013 г. Эрик Липперт сказал, что если вы хотите гарантировать воспроизводимую арифметику в .NET, вам следует «Использовать целые числа».
  • В обсуждении этой темы на странице Roslyn GitHub разработчик игры сказал в комментарии в 2017 году, что им не удалось достичь повторяющихся операций с плавающей запятой в C #, хотя он не указал, какие среды выполнения они использовали.
  • В обмене стеками разработки игр 2011 года answer автор приходит к выводу, что ему не удалось получить надежную арифметику FP в .NET.Он предоставляет программную реализацию с плавающей запятой для .NET, которая двоично совместима с плавающей запятой IEEE754.

Вопрос

Итак, если CoreCLR использует инструкции SSE FP на x86-64, означает ли это, что он не страдает от проблем усечения и / или любого другого недетерминизма, связанного с FP?Мы поставляем .NET Core с движком, чтобы каждый клиент использовал одну и ту же среду выполнения, и мы бы требовали, чтобы игроки использовали точно такую ​​же версию игрового клиента.Ограничение работы ядра только на x86-64 (на ПК) также является приемлемым ограничением.

Если во время выполнения все еще используются инструкции x87 с ненадежными результатами, имеет ли смысл использовать программную реализацию с плавающей запятой (например,один связан в ответе выше) для вычислений, касающихся отдельных значений, и ускорения векторных операций с SSE с использованием новой аппаратной встроенной функции ?Я прототипировал это, и это похоже на работу, но не нужно ли это?

Если мы можем просто использовать обычные операции с плавающей запятой, есть ли что-то, чего нам следует избегать, например тригонометрические функции?

Наконец, если все в порядке, как это будет работать, когда разные клиенты используют разные операционные системы или даже разные архитектуры ЦП?Страдают ли современные процессоры ARM от проблемы 80-битного усечения, или же тот же код выполняется идентично x86 (если исключить более сложные вещи, такие как тригонометрия), при условии, что в реализации нет ошибок?

Ответы [ 2 ]

0 голосов
/ 14 марта 2019

Больше похоже на пищу для размышлений, чем на определенный ответ: вы можете захотеть взглянуть на типы чисел, отличные от встроенных в .NET.Недостатком, очевидно, является то, что то, что есть в .NET, не только хорошо понято (хмм), но аппаратная поддержка также присутствует практически на каждой платформе.Но все же, возможно, проверьте posits , новый, все еще работающий формат чисел с плавающей запятой.

Стандарт posit не оставляет места для интерпретации способом, который вызывает вашу проблему,и есть встроенный внутренний аккумулятор.Таким образом, операции posit дают детерминированные результаты на разных платформах - теоретически, потому что аппаратные реализации редки (но существуют!), И ни один стандартный процессор не поддерживает его изначально.Таким образом, вы можете использовать его только в качестве мягкого числового типа, хотя это может быть проблемой для вас, только если такие вычисления выполняются по чувствительному к задержке пути выполнения.

Для этого также есть библиотека .NET, которую выздесь можно найти (предназначено для .NET Framework, но его очень легко переключить на стандарт .NET), которое также можно превратить в аппаратную реализацию FPGA.Более подробная информация - здесь .

Отказ от ответственности: Я из компании, которая занимается библиотекой .NET (но posit не был изобретен нами).

0 голосов
/ 01 января 2019

Итак, если CoreCLR использует инструкции SSE FP на x86-64, означает ли это, что он не страдает от проблем усечения и / или любого другого недетерминизма, связанного с FP?

Если вы остаетесь на x86-64 и везде используете одну и ту же версию CoreCLR, она должна быть детерминированной.

Если во время выполнения все еще используются инструкции x87 с ненадежнымрезультаты, имеет ли смысл использовать программную реализацию с плавающей запятой [...] Я прототипировал это, и это похоже на работу, но не нужно ли это?

Это может быть решением обходного путипроблема JIT, но вам, вероятно, придется разработать анализатор Roslyn, чтобы удостовериться, что вы не используете операции с плавающей запятой, не проходя через них ... или написать переписывающее устройство IL, которое выполнит это для вас (но это сделает вашСборки .NET зависят от арки ... что может быть приемлемо в зависимости от ваших требований)

Если мы можем просто использовать обычные операции с плавающей запятой, есть ли что-то, чего нам следует избегать, например тригонометрические функции?

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

Наконец, если все в порядке, как это будет работать, когда разные клиенты используют разные операционные системы или даже разные архитектуры ЦП?Страдают ли современные процессоры ARM от проблемы 80-битного усечения, или же тот же код будет работать идентично x86 (если исключить более сложные вещи, такие как тригонометрия), при условии, что в реализации нет ошибок?

Вы можетеесть некоторые проблемы, не связанные с повышенной точностью.Например, для ARMv7 субнормальные значения с плавающей запятой сбрасываются в ноль, в то время как ARMv8 на aarch64 будет их сохранять.

Если предположить, что вы остановились на ARMv8, я не очень хорошо знаю, работает ли JIT CoreCLR для ARMv8 в этом отношении;Вы должны, вероятно, спросить на GitHub напрямую.Также существует поведение libc, которое может нарушить детерминированные результаты.

Мы работаем над решением этой проблемы в Unity на нашем "пакетном" компиляторе для перевода .NET IL в собственный код.Мы используем код LLVM на всех машинах, отключив несколько оптимизаций, которые могут нарушить детерминизм (поэтому здесь, в целом, мы можем попытаться гарантировать поведение компилятора на разных платформах), и мы также используем библиотеку SLEEF для обеспечения детерминированного вычисленияматематические функции (см., например, https://github.com/shibatch/sleef/issues/187)…, чтобы это можно было сделать.

В вашей ситуации я бы, вероятно, попытался бы выяснить, действительно ли CoreCLR действительно детерминирован для простых операций с плавающей запятой между x64 и ARMv8… И если все выглядит хорошо, вы можете вызвать эти функции SLEEF вместо System.Math, и это может сработать из коробки, или предложить CoreCLR переключиться с libc на SLEEF.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...