부동 소수점 비결정론에 직면하여 결정 론적 게임이 어떻게 가능합니까?


52

RTS 네트워크와 같은 게임을 만들기 위해 게임을 완전히 결정 론적으로 만드는 많은 답변을 보았습니다. 그런 다음 사용자의 작업을 서로에게 전달하고 다음 프레임이 렌더링되기 전에 모든 사람의 입력을 "고정"하기 위해 표시되는 내용을 약간 지연시킵니다. 그러면 모든 플레이어의 시뮬레이션이 정확히 동일하기 때문에 유닛의 위치, 건강 등과 같은 것들이 네트워크를 통해 지속적으로 업데이트 될 필요가 없습니다. 나는 또한 재생을 위해 제안 된 것과 같은 것을 들었다.

그러나 부동 소수점 계산은 머신간에, 또는 동일한 머신에서 동일한 프로그램의 서로 다른 컴파일간에 결정적이지 않기 때문에 실제로 가능합니까? 이 사실 이 게임 전반에 파급되는 플레이어 (또는 리플레이) 사이에 작은 차이를 일으키는 것을 어떻게 방지 합니까?

일부 사람들은 부동 소수점 숫자를 모두 피하고 int분수의 몫을 나타내는 데 사용한다고 제안 했지만 실제로는 들리지 않습니다. 전체 수학 라이브러리를 심각하게 다시 작성해야합니까?

내가 말할 수있는 한 C #에 주로 관심이 있습니다.이 점에서 C ++과 정확히 같은 문제가 있습니다.


5
이것은 귀하의 질문에 대한 답변이 아니지만 RTS 네트워킹 문제를 해결하기 위해 드리프트를 수정하기 위해 때때로 장치 위치와 상태를 동기화하는 것이 좋습니다. 정확히 어떤 정보를 동기화해야하는지 게임에 따라 다릅니다. 상태 효과와 같은 것을 동기화하지 않아도 대역폭 사용을 최적화 할 수 있습니다.
jhocking

@jhocking 그러나 아마도 가장 좋은 대답 일 것입니다.
Jonathan Connell

starcraft II infacti는 매번 정확히 동기화됩니다. 나는 각 컴퓨터에서 나란히 친구들과 게임 플레이를 보았고, 특히 높은 지연 (1 초)의 차이 (중요한 부분)가있는 곳을 보았습니다. 화면에서 6/7지도 사각형이 자신의 화면과 먼 곳에있는 일부 장치가있었습니다
GameDeveloper

답변:


15

부동 소수점은 결정적입니까?

몇 년 전과 동일한 잠금 단계 아키텍처를 사용하여 RTS를 작성하려고 할 때이 문제에 대해 많이 읽었습니다.

하드웨어 부동 소수점에 대한 나의 결론은 다음과 같습니다.

  • 부동 소수점 플래그 및 컴파일러 설정에주의를 기울이면 동일한 기본 어셈블리 코드가 결정적 일 가능성이 높습니다.
  • 랩퍼 라이브러리를 사용하여 여러 컴파일러에서 결정적인 C / C ++ 컴파일을 받았다고 주장하는 오픈 소스 RTS 프로젝트가 하나있었습니다. 나는 그 주장을 확인하지 않았다. (내가 올바르게 기억하면 STREFLOP라이브러리 에 관한 것입니다 )
  • .net JIT는 약간의 여유가 허용됩니다. 특히 필요한 것보다 높은 정확도를 사용할 수 있습니다. 또한 x86과 AMD64에서 다른 명령 세트를 사용합니다 (x86에서는 x87을 사용한다고 생각합니다. AMD64는 규범에 따라 동작이 다른 SSE 명령을 사용합니다).
  • 복잡한 명령어 (삼각 함수, 지수, 로그 포함)가 특히 문제가됩니다.

.net에서 내장 부동 소수점 유형을 결정적으로 사용하는 것이 불가능하다는 결론을 내 렸습니다.

가능한 해결 방법

따라서 해결 방법이 필요했습니다. 나는 고려했다 :

  1. FixedPoint32C #에서 구현하십시오 . 이것이 너무 어렵지는 않지만 (반쯤 완성 된 구현이 있습니다) 매우 작은 범위의 값은 사용하기가 성가 시게합니다. 항상주의를 기울여야하므로 오버 플로우 나 정밀도가 떨어질 수 있습니다. 결국 나는 정수를 직접 사용하는 것보다 쉽지 않다는 것을 알았습니다.
  2. FixedPoint64C #에서 구현하십시오 . 나는 이것이 오히려 어렵다는 것을 알았다. 일부 연산의 경우 128 비트의 중간 정수가 유용합니다. 그러나 .net은 그러한 유형을 제공하지 않습니다.
  3. 하나의 플랫폼에서 결정적인 수학 연산에 기본 코드를 사용하십시오. 모든 수학 연산에서 델리게이트 호출의 오버 헤드가 발생합니다. 크로스 플랫폼을 실행할 수있는 능력을 잃습니다.
  4. 사용하십시오 Decimal. 그러나 속도가 느리고 많은 메모리가 필요하며 쉽게 예외를 던집니다 (0으로 나누기, 오버플로). 재정적으로는 좋지만 게임에는 적합하지 않습니다.
  5. 사용자 정의 32 비트 부동 소수점을 구현하십시오. 처음에는 다소 어려움을 겪었습니다. BitScanReverse 내장 함수가 없으면이를 구현할 때 몇 가지 문제가 발생합니다.

내 SoftFloat

StackOverflow대한 귀하의 게시물에서 영감을 얻어 소프트웨어에서 32 비트 부동 소수점 유형을 구현하기 시작했으며 결과는 유망합니다.

  • 메모리 표현은 IEEE float과 이진 호환되므로 그래픽 코드로 출력 할 때 캐스트를 재 해석 할 수 있습니다.
  • SubNorms, 무한대 및 NaN을 지원합니다.
  • 정확한 결과는 IEEE 결과와 동일하지 않지만 일반적으로 게임에는 중요하지 않습니다. 이런 종류의 코드에서는 결과가 모든 사용자에 대해 동일하고 마지막 자릿수에 정확하지 않다는 것만 중요합니다.
  • 성능은 괜찮습니다. 사소한 테스트는 220-260MFLOPS와 float덧셈 / 곱셈 (2.66GHz i3의 단일 스레드)을 비교하여 약 75MFLOPS를 수행 할 수 있음을 보여주었습니다 . 누구든지 .net에 대한 부동 소수점 벤치 마크가 있다면 현재 테스트가 매우 기초 적이기 때문에 나에게 보내주십시오.
  • 반올림을 향상시킬 수 있습니다. 현재는 잘 리며 대략 0으로 반올림합니다.
  • 여전히 매우 불완전합니다. 현재 부서, 캐스트 및 복잡한 수학 연산이 누락되었습니다.

테스트에 기여하거나 코드를 개선하려는 사람이 있으면 저에게 연락하거나 github에서 pull 요청을 발행하십시오. https://github.com/CodesInChaos/SoftFloat

결정론의 다른 출처

.net에는 다른 결정 론적 출처도 있습니다.

  • 이상 반복 Dictionary<TKey,TValue>또는 HashSet<T>정의되지 않은 순서로 요소를 반환합니다.
  • object.GetHashCode() 실행마다 다릅니다.
  • 내장 Random클래스 의 구현 은 지정되어 있지 않으므로 직접 사용하십시오.
  • 순진한 잠금 기능이있는 멀티 스레딩은 순서를 바꾸고 다른 결과를 가져옵니다. 스레드를 올바르게 사용하도록주의하십시오.
  • WeakReferenceGC는 언제든지 실행될 수 있기 때문에 목표를 잃을 때 결정적이지 않습니다.

23

이 질문에 대한 답변은 게시 한 링크에서 온 것입니다. 특히 Gas Powered Games의 인용문을 읽어야합니다.

저는 Gas Powered Games에서 일하고 있으며 부동 소수점 수학은 결정 론적이라고 말할 수 있습니다. 동일한 명령어 세트와 컴파일러가 필요하며 사용자 프로세서는 모든 PC 및 360 고객을 포함하는 IEEE754 표준을 준수합니다. DemiGod, Supreme Commander 1 및 2를 실행하는 엔진은 IEEE754 표준을 따릅니다. 시장의 다른 모든 RTS P2P 게임은 말할 것도 없습니다.

그리고 그 아래에 이것이 있습니다 :

재생을 컨트롤러 입력으로 저장하면 다른 CPU 아키텍처, 컴파일러 또는 최적화 설정을 가진 머신에서 재생할 수 없습니다. MotoGP에서는 Xbox와 PC간에 저장된 재생 내용을 공유 할 수 없었습니다.

결정 론적 게임은 동일하게 컴파일 된 파일을 사용하고 IEEE 표준을 준수하는 시스템에서 실행될 때만 결정 론적입니다. 크로스 플랫폼 동기화 네트워크 시뮬레이션 또는 재생은 불가능합니다.


2
앞서 언급했듯이 C #에 주로 관심이 있습니다. JIT는 실제 컴파일을 수행하기 때문에 동일한 머신에서 동일한 실행 파일을 실행할 때도 "같은 최적화 설정으로 컴파일 됨"을 보장 할 수 없습니다 !
BlueRaja-대니 Pflughoeft

2
때로는 반올림 모드가 fpu에서 다르게 설정됩니다. 일부 아키텍처에서는 fpu가 계산을 위해 정밀보다 큰 내부 레지스터를 가지고 있습니다 ... IEEE754는 FP 작업의 작동 방식과 관련하여 여유가 없습니다. t 특정 수학 함수를 어떻게 구현해야하는지 지정하십시오. 이는 구조적 차이를 노출시킬 수 있습니다.
Stephen

4
@ 3nixios 이런 종류의 설정으로 게임은 결정적이지 않습니다. 정해진 시간 간격을 가진 게임 만이 결정론적일 수 있습니다. 단일 머신에서 시뮬레이션을 다시 실행할 때 이미 결정적인 것이 아니라고 주장하고 있습니다.
AttackingHobo

4
@ 3nixios, "고정 된 타임 스텝을 사용해도 타임 스텝이 항상 고정되어 있다고 보장 할 수있는 것은 없습니다." 당신은 전적으로 틀 렸습니다. 고정 된 타임 스텝의 요점은 업데이트시 각 틱에 대해 항상 동일한 델타 시간을 유지하는 것입니다
AttackingHobo

3
@ 3nixios 고정 된 시간 간격을 사용하면 개발자가 달리 허용하지 않으므로 시간 간격이 고정됩니다. 고정 시간 단계를 사용할 때 지연을 어떻게 보상해야하는지 잘못 해석하고 있습니다. 각 게임 업데이트 동일한 업데이트 시간을 사용해야합니다. 따라서 피어의 연결이 지연 될 때마다 누락 된 각 개별 업데이트를 계산해야합니다.
Keeblebrox

3

고정 소수점 산술을 사용하십시오. 또는 신뢰할 수있는 서버를 선택하고 가끔 게임 상태를 동기화하도록 MMORTS가하는 일입니다. (적어도 Elements of War는 이런 식으로 작동합니다. C #으로 작성되었습니다.) 이런 방식으로 오류가 누적 될 수는 없습니다.


예, 클라이언트 서버로 만들고 클라이언트 측 예측 및 외삽의 두통을 처리 할 수 ​​있습니다. 이 질문은 보다 쉬운 P2P 아키텍처에 관한 것입니다 .
BlueRaja-Danny Pflughoeft

"모든 클라이언트가 동일한 코드를 실행"시나리오에서 예측할 필요는 없습니다. 서버의 역할은 동기화 권한 만있는 것입니다.
Nevermind

서버 / 클라이언트는 네트워크 게임을 만드는 한 가지 방법 일뿐입니다. 또 다른 대중적인 방법 (특히 C & C 또는 Starcraft와 같은 RTS 게임의 경우)은 인증 서버 없는 피어 투 피어 입니다. 이것이 가능 하려면 모든 계산이 모든 클라이언트에서 완전히 결정적이고 일관성이 있어야하므로 내 질문입니다.
BlueRaja-대니 Pflughoeft

서버 / 상승 클라이언트가없는 게임을 엄격히 p2p로 만드는 것은 절대 아닙니다. 그래도 꼭해야한다면 고정 소수점을 사용하십시오.
Nevermind

3

편집 : 고정 소수점 클래스에 대한 링크 (구매자 조심하십시오-사용하지 않았습니다 ...)

항상 고정 소수점 산술 에 빠질 수 있습니다. 누군가 (rts를 만들거나 더 이상)는 이미 stackoverflow 에서 다리 작업을 수행했습니다 .

.net은 simd 명령을 사용하지 않기 때문에 성능이 떨어질 수 있으므로 성능 저하가 발생하지만 문제가 될 수도 있고 아닐 수도 있습니다. 기준!

NB 인텔의 누군가 가 C #에서 인텔 성능 프리미티브 라이브러리를 사용할 수있는 솔루션을 가지고있는 것 같습니다. 이렇게하면 고정 소수점 코드를 벡터화하여 성능 저하를 보완 할 수 있습니다.


decimal그것도 잘 작동합니다. 그러나 이것은 여전히 ​​사용하는 것과 동일한 문제가 있습니다. int어떻게 처리합니까?
BlueRaja-대니 Pflughoeft

1
가장 쉬운 방법은 조회 테이블을 작성하여 애플리케이션과 함께 전송하는 것입니다. 정밀도가 문제인 경우 값을 보간 할 수 있습니다.
LukeN

또는 삼각 통화 호출을 허수 또는 쿼터니언 (3D 인 경우)으로 대체 할 수 있습니다. 이것은 훌륭한 설명입니다
LukeN

도움 될 수도 있습니다.
LukeN

내가 일했던 회사에서 고정 소수점 수학을 사용하여 초음파 기계를 만들었으므로 확실히 도움이 될 것입니다.
LukeN

3

나는 많은 큰 타이틀을 작업했습니다.

짧은 대답 : 당신이 미친 듯이 엄격하지만 가능하지 않을 경우 가능합니다. 고정 된 아키텍처 (읽기 : 콘솔)를 사용하지 않으면 까다 롭고 부서지기 쉬우 며 늦은 조인과 같은 많은 보조 문제가 발생합니다.

언급 한 기사 중 일부를 읽으면 CPU 모드를 설정할 수 있지만 동일한 주소 공간에 있기 때문에 프린터 드라이버가 CPU 모드를 전환하는 경우와 같은 기괴한 경우가 있습니다. 응용 프로그램이 외부 장치에 프레임 잠금 된 경우가 있었지만 결함이있는 구성 요소로 인해 CPU가 열과 오후에 다르게 스로틀 링되어 아침과 오후에 다르게보고하여 점심 식사 후 다른 시스템으로 적용했습니다.

이러한 플랫폼 차이는 소프트웨어가 아닌 실리콘에 있으므로 C #에도 영향을 미칩니다. FMA (fused multiply-add) 대 ADD + MUL과 같은 명령어는 내부에서 두 번이 아니라 한 번만 반올림되므로 결과가 변경됩니다. C는 FMA와 같은 작업을 배제하여 "표준"으로 만들지 만 속도를 희생하여 CPU가 기본적으로 원하는 작업을 수행하도록 더 많은 제어 기능을 제공합니다. 본질은 가장 다른 것으로 보인다. 한 프로젝트에서 나는 150 년 된 acos 테이블을 발굴하여 어떤 CPU가 "적절한"지를 결정하기위한 값을 비교해야했습니다. 많은 CPU가 삼각 함수에 다항식 근사를 사용하지만 항상 같은 계수를 갖는 것은 아닙니다.

내 추천 :

정수 잠금 단계 또는 동기화 여부에 관계없이 프레젠테이션과 별도로 핵심 게임 메커니즘을 처리하십시오. 게임이 정확하게 이루어 지지만 프레젠테이션 레이어의 정확성에 대해서는 걱정하지 마십시오. 또한 모든 네트워크 세계 데이터를 동일한 프레임 속도로 전송할 필요는 없습니다. 메시지의 우선 순위를 지정할 수 있습니다. 시뮬레이션이 99.999 % 일치하면 페어링 상태를 유지하기 위해 자주 전송할 필요가 없습니다. (차단 방지)

동기화에 관한 한 가지 방법을 설명하는 소스 엔진에 대한 좋은 기사가 있습니다. https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

늦은 가입에 관심이 있다면 총알을 물고 어쨌든 동기화해야합니다.


1

내가 말할 수있는 한 C #에 주로 관심이 있습니다.이 점에서 C ++과 정확히 같은 문제가 있습니다.

예, C #은 C ++과 같은 문제가 있습니다. 그러나 더 많은 것도 있습니다.

예를 들어 Shawn Hawgraves에서이 진술을 보자.

재생을 컨트롤러 입력으로 저장하면 다른 CPU 아키텍처, 컴파일러 또는 최적화 설정을 가진 머신에서 재생할 수 없습니다.

이것이 C ++에서 일어나도록하는 것은 "충분히 쉽다". 그러나 C # 에서는 다루기가 훨씬 어려울 것입니다. JIT 덕분입니다.

인터프리터가 코드 해석을 한 번 실행 한 다음 두 번째로 JIT하면 어떻게 될까요? 아니면 다른 사람의 컴퓨터에서 두 번 해석하지만 그 후에 JIT입니까?

JIT는 제어 할 수있는 기능이 거의 없기 때문에 결정적이지 않습니다. 이것은 CLR 사용을 포기하는 것 중 하나입니다.

그리고 한 사람이 .NET 4.0을 사용하여 게임을 실행하고 다른 사람이 모노 CLR을 사용하는 경우 (물론 .NET 라이브러리 사용) 하나님은 당신을 도와줍니다. .NET 4.0과 .NET 5.0도 다를 수 있습니다. 이런 종류의 것을 보장 하기 위해 플랫폼의 하위 레벨 세부 사항을 더 잘 제어해야합니다 .

고정 소수점 수학으로 벗어날 수 있어야합니다. 그러나 그것은 그것에 관한 것입니다.


수학 외에도 다른 점이 무엇입니까? 아마도 "입력"액션은 rts에서 논리적 액션으로 전송되고있을 것입니다. 예 : "a"를 "b"로 이동하십시오. 난수 생성에 문제가 있음을 알 수 있지만 시뮬레이션 입력으로 보내야합니다.
LukeN

@LukeN : 문제는 컴파일러의 차이점이 부동 소수점 수학에 실제 영향을 미칠 수 있다는 Shawn의 입장을 강력히 시사한다는 것입니다. 그는 나보다 더 많이 알 것입니다. 나는 단순히 그의 경고를 JIT 및 C # 컴파일 / 해석 영역으로 외삽하고 있습니다.
Nicol Bolas

C #에는 연산자 오버로드 가 있으며 고정 소수점 연산을위한 내장 유형 도 있습니다 . 그러나 이것은 사용하는 것과 같은 문제가 있습니다 int-어떻게 처리합니까?
BlueRaja-대니 Pflughoeft

1
> 인터프리터가 코드를 한 번 해석 한 다음 한 번만 해석 한 다음 두 번째로 JIT하면 어떻게 될까요? 아니면 다른 사람의 컴퓨터에서 그것을 두 번 해석하지만 JIT가 끝난 것입니까? 1. CLR 코드는 실행 전에 항상 지터 (jitt)됩니다. 2. .net은 물론 수레에 IEEE 754를 사용합니다. > JIT는 제어 할 수있는 기능이 거의 없으므로 결정적이지 않습니다. 당신은 결론이 허위 진술의 아주 약한 원인입니다. > 한 사람이 .NET 4.0을 사용하여 게임을 실행하고 다른 사람이 Mono CLR을 사용하는 동안 (도움말의 .NET 라이브러리 사용) 신은 당신을 도와줍니다. 심지어 .NET
Romanenkov Andrey

@Romanenkov : "당신의 결론은 허위 진술의 아주 약한 원인입니다." 어떤 진술이 틀렸는 지, 왜 그런지 설명하여 정정 할 수 있도록하십시오.
Nicol Bolas

1

이 답변을 작성한 후에 실제로 부동 소수점 비 결정론에 관한 질문에 대답하지 않는다는 것을 깨달았습니다. 그러나 이런 식으로 네트워크 게임을 만들려는 경우 어쨌든 도움이 될 것입니다.

모든 플레이어에게 브로드 캐스트하는 입력 공유와 함께 플레이어 위치, 건강 등과 같은 중요한 게임 상태의 체크섬을 생성하고 브로드 캐스트하는 것이 매우 유용 할 수 있습니다. 입력을 처리 할 때 게임 상태 체크섬이 모든 원격 플레이어가 동기화되어 있습니다. 동기화되지 않은 버그 (OOS) 버그를 수정해야하며이를 통해보다 쉽게 ​​해결할 수 있습니다. 문제가 발생했다는 사실을 미리 알 수 있습니다 (복제 단계를 알아내는 데 도움이 됨). 더 많은 것을 추가 할 수 있어야합니다. 의심스러운 코드로 게임 상태를 기록하여 OOS를 유발하는 모든 것을 브라켓 할 수 있습니다.


0

블로그에 연결된 아이디어는 여전히 실행 가능한주기적인 동기화가 필요하다고 생각합니다. 네트워크 RTS 게임에서 이러한 접근 방식을 취하지 않는 버그가 충분히 발견되었습니다.

네트워크는 손실이 많고 느리고 지연 시간이 있으며 데이터를 오염시킬 수도 있습니다. "부동 점 결정론"은 회의론을 만들 정도로 충분히 화려하게 들립니다. 고정 시간 단계를 사용하는 경우 특히 걱정할 필요가 없습니다. 가변 시간 단계를 사용하면 결정 성 문제를 피하기 위해 고정 시간 단계 사이를 보간해야합니다. 나는 이것이 보통 결정적이지 않은 "부동 소수점"행동의 의미라고 생각합니다. 단지 가변 시간 단계로 인해 통합이 분기됩니다. 수학 라이브러리 나 저수준 함수와는 아무 관련이 없습니다.

그래도 동기화가 핵심입니다.


-2

당신은 그것들을 결정 론적으로 만듭니다. 좋은 예를 보려면 Dungeon Siege GDC 프레젠테이션 을 통해 전 세계 위치를 네트워크로 연결하는 방법을 참조하십시오 .

또한 결정론은 '무작위'사건에도 적용된다는 것을 기억하십시오!


1
이것이 플로팅 포인트를 통해 OP가 수행 방법을 알고 싶어하는 것입니다.
공산주의 오리
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.