게임 타이밍 및 멀티 코어 프로세서
오늘날의 컴퓨터에서 전원 관리 기술이 더 일반화됨에 따라 고해상도 CPU 타이밍을 얻기 위해 일반적으로 사용되는 방법인 RDTSC 명령이 더 이상 예상대로 작동하지 않을 수 있습니다. 이 문서에서는 Windows API QueryPerformanceCounter 및 QueryPerformanceFrequency를 사용하여 고해상도 CPU 타이밍을 가져오는 보다 정확하고 신뢰할 수 있는 솔루션을 제안합니다.
배경
x86 P5 명령 집합이 도입된 이후 많은 게임 개발자들이 RDTSC 명령인 읽기 타임스탬프를 사용하여 고해상도 타이밍을 수행했습니다. Windows 멀티미디어 타이머는 사운드 및 비디오 처리에 충분히 정확하지만 프레임 시간이 12밀리초 이하인 경우 델타 시간 정보를 제공하기에 충분한 해상도가 없습니다. 많은 게임은 여전히 시작 시 멀티미디어 타이머를 사용하여 CPU의 빈도를 설정하고, 해당 빈도 값을 사용하여 RDTSC의 결과를 확장하여 정확한 시간을 얻습니다. RDTSC의 제한 사항으로 인해 Windows API는 QueryPerformanceCounter 및 QueryPerformanceFrequency의 루틴을 통해 이 기능에 액세스하는 보다 올바른 방법을 노출합니다.
타이밍에 RDTSC를 사용하는 경우 다음과 같은 근본적인 문제가 발생합니다.
- 불연속 값입니다. RDTSC를 직접 사용하면 스레드가 항상 동일한 프로세서에서 실행되고 있다고 가정합니다. 다중 프로세서 및 이중 코어 시스템은 코어 간의 주기 카운터 동기화를 보장하지 않습니다. 이는 다양한 시간에 다양한 코어를 유휴 상태로 복원하는 최신 전원 관리 기술과 결합되어 일반적으로 코어가 동기화되지 않는 경우 악화됩니다. 애플리케이션의 경우 스레드가 프로세서 간에 점프하고 큰 델타, 음의 델타 또는 중지된 타이밍을 초래하는 타이밍 값을 가져오면 일반적으로 결함이 발생하거나 잠재적인 충돌이 발생합니다.
- 전용 하드웨어의 가용성. RDTSC는 애플리케이션이 프로세서의 주기 카운터에 요청하는 타이밍 정보를 잠가줍니다. 수년 동안 이것은 정밀도 타이밍 정보를 가져오는 가장 좋은 방법이었지만, 최신 마더보드에는 RDTSC의 단점 없이 고해상도 타이밍 정보를 제공하는 전용 타이밍 디바이스가 포함됩니다.
- CPU 빈도의 가변성입니다. CPU의 빈도가 프로그램 수명 동안 고정된다는 가정이 종종 있습니다. 그러나 최신 전원 관리 기술을 사용하면 잘못된 가정입니다. 처음에는 노트북 컴퓨터 및 기타 모바일 장치로 제한되지만 CPU 빈도를 변경하는 기술은 많은 고급 데스크톱 PC에서 사용되고 있습니다. 일관된 빈도를 유지하기 위해 함수를 사용하지 않도록 설정하는 것은 일반적으로 사용자에게 허용되지 않습니다.
권장 사항
게임에는 정확한 타이밍 정보가 필요하지만 RDTSC 사용과 관련된 문제를 방지하는 방식으로 타이밍 코드를 구현해야 합니다. 고해상도 타이밍을 구현하는 경우 다음 단계를 수행합니다.
RDTSC 대신 QueryPerformanceCounter 및 QueryPerformanceFrequency 를 사용합니다. 이러한 API는 RDTSC를 사용할 수 있지만 대신 마더보드 또는 고품질의 고해상도 타이밍 정보를 제공하는 다른 시스템 서비스에서 타이밍 디바이스를 사용할 수 있습니다. RDTSC는 QueryPerformanceCounter보다 훨씬 빠르지만 후자는 API 호출이므로 눈에 띄는 영향 없이 프레임당 수백 번 호출할 수 있는 API입니다. (그럼에도 불구하고 개발자는 성능 저하를 방지하기 위해 게임이 QueryPerformanceCounter 를 최대한 적게 호출하도록 시도해야 합니다.)
델타를 계산할 때 타이밍 값의 버그가 크래시 또는 불안정한 시간 관련 계산을 유발하지 않도록 값을 고정해야 합니다. 클램프 범위는 음수 델타 값을 방지하기 위해 0에서 가장 낮은 예상 프레임 속도에 따라 적절한 값까지여야 합니다. 클램핑은 애플리케이션의 디버깅에 유용할 수 있지만 성능 분석을 수행하거나 최적화되지 않은 모드에서 게임을 실행하는 경우 유의해야 합니다.
단일 스레드에서 모든 타이밍을 계산합니다. 여러 스레드(예: 특정 프로세서와 연결된 각 스레드)에서 타이밍을 계산하면 다중 코어 시스템의 성능이 크게 저하됩니다.
Windows API SetThreadAffinityMask를 사용하여 단일 스레드를 단일 프로세서에 유지하도록 설정합니다. 일반적으로 기본 게임 스레드입니다. QueryPerformanceCounter 및 QueryPerformanceFrequency는 일반적으로 여러 프로세서에 대해 조정되지만, BIOS 또는 드라이버의 버그로 인해 스레드가 한 프로세서에서 다른 프로세서로 이동할 때 이러한 루틴이 다른 값을 반환할 수 있습니다. 따라서 스레드를 단일 프로세서에 유지하는 것이 가장 좋습니다.
다른 모든 스레드는 자체 타이머 데이터를 수집하지 않고 작동해야 합니다. 동기화 병목 상태가 되므로 작업자 스레드를 사용하여 타이밍을 계산하지 않는 것이 좋습니다. 대신 작업자 스레드는 기본 스레드에서 타임스탬프를 읽어야 하며 작업자 스레드는 타임스탬프만 읽기 때문에 중요한 섹션을 사용할 필요가 없습니다.
시스템이 실행되는 동안 빈도가 변경되지 않으므로 QueryPerformanceFrequency 를 한 번만 호출합니다.
응용 프로그램 호환성
많은 개발자가 수년 동안 RDTSC의 동작에 대해 가정해 왔기 때문에 타이밍 구현으로 인해 여러 프로세서 또는 코어가 있는 시스템에서 실행될 때 일부 기존 애플리케이션이 문제를 나타낼 가능성이 큽니다. 이러한 문제는 일반적으로 결함 또는 슬로우 모션 움직임으로 나타납니다. 전원 관리를 인식하지 못하는 애플리케이션에 대한 쉬운 해결 방법은 없지만, 애플리케이션이 항상 다중 프로세서 시스템의 단일 프로세서에서 실행되도록 강제하는 기존 shim이 있습니다.
이 shim을 만들려면 Windows 애플리케이션 호환성에서 Microsoft 애플리케이션 호환성 도구 키트 를 다운로드합니다.
도구 키트의 일부인 호환성 관리자를 사용하여 애플리케이션 및 관련 수정 사항의 데이터베이스를 만듭니다. 이 데이터베이스에 대한 새 호환성 모드를 만들고 호환성 수정 SingleProcAffinity 를 선택하여 애플리케이션의 모든 스레드가 단일 프로세서/코어에서 실행되도록 합니다. 명령줄 도구 Fixpack.exe(도구 키트의 일부이기도 함)를 사용하여 이 데이터베이스를 설치, 테스트 및 배포를 위한 설치 가능한 패키지로 변환할 수 있습니다.
호환성 관리자 사용에 대한 지침은 도구 키트의 설명서를 참조하세요. Fixpack.exe 사용에 대한 구문 및 예제는 명령줄 도움말을 참조하세요.
고객 지향 정보는 Microsoft 도움말 및 지원의 다음 기술 자료 문서를 참조하세요.
- QueryPerformanceCounter 함수를 사용하는 프로그램은 Windows Server 2003 및 Windows XP에서 제대로 수행되지 않을 수 있습니다 (문서 895980).