Windows 타이틀의 주요 문제
Microsoft Windows 게임 및 그래픽 기술 개발자 관계 그룹은 매년 많은 Windows 게임에 대한 성능 분석을 수행합니다. 이러한 세션 중에는 매일 받는 개발자 피드백 및 쿼리에 연결하는 실습 경험을 얻을 수 있습니다. 경우에 따라 타이틀에서 신비한 충돌 또는 기타 문제를 추적하는 데 도움을 주므로 개발자가 발생하는 문제에 대한 추가 인사이트를 얻을 수 있습니다.
이 문서에서는 현재 세대 PC 게임에서 볼 수 있는 많은 일반적인 문제를 중점적으로 설명합니다.
- CPU 제한 성능
- 잘못된 Batch 관리
- 과도한 메모리 복사
- 동적 그리기 제출의 과도한 사용
- 파일 처리의 높은 오버헤드
- 느리고 실망스러운 설치
- 실제 메모리 고려 사항 부족
- Real-Time 오디오 샘플 속도 변환에 대한 과잉 의존
- 가상 메모리 조각화
- Floating-Point 컨트롤 Word 조작
- DirectX 런타임의 선택적 설치
- 과도한 스레드 동기화 사용
- RDTSC 사용
CPU-Limited 성능
대부분의 게임은 GPU(고성능 그래픽 처리 장치)가 있는 시스템의 CPU 성능에 의해 제한됩니다. 이는 경우에 따라 그리기 제출에 일괄 처리를 사용하지 못하기 때문에 발생하지만, 일반적으로 다른 게임 시스템에서 사용 가능한 CPU 주기의 상당 부분을 소비하기 때문입니다. GPU를 제한으로 본 몇 가지 경우, 원인은 매우 높은 채우기 속도 또는 픽셀 셰이더 수요, 고해상도 설정 또는 비디오 카드 의한 낮은 꼭짓점 셰이더 성능입니다.
대부분의 타이틀은 CPU 제한이므로 CPU를 많이 사용하는 게임 시스템을 최적화하면 가장 큰 성능이 향상됩니다. 일반적으로 AI 또는 물리학 시스템 및 관련 충돌 감지 논리는 잘 동작하는 Microsoft Direct3D 애플리케이션에서 CPU 주기의 기본 소비자입니다. 이러한 시스템을 개선하기 위한 모든 작업은 게임의 전반적인 성능을 향상시킬 수 있습니다.
잘못된 Batch 관리
GPU를 사용하여 적절한 병렬 처리를 수행하려면 배치 그리기에 충분한 기하 도형이 포함되어 있어야 하며 셰이더는 명령 버퍼가 플러딩되는 일괄 처리를 너무 많이 사용하지 않으면서 비디오 카드 사용할 수 있도록 적절한 복잡성을 가져야 합니다. 현재 세대 하드웨어에서는 드라이버의 명령 버퍼 처리가 성능 병목 상태가 되지 않도록 프레임당 약 300개 이하의 그리기 일괄 처리 제출(성능이 낮은 CPU의 경우 적음)을 권장합니다. 다른 API 상태 호출 및 드라이버 조합으로 인해 비용이 많이 드는 CPU 처리(예: 셰이더의 드라이버 컴파일)가 발생할 수 있으므로 일상적인 성능 분석을 사용하는 것이 좋습니다.
과도한 메모리 복사
대부분의 PC 타이틀을 개발하는 동안 개발자는 콘텐츠 관리에 편리한 데이터 구조와 문자열을 사용합니다. 문자열 비교, 복사 및 기타 조작에 필요한 CPU 작업에는 특히 캐시 및 메모리 하위 시스템과 관련된 성능 적중을 고려할 때 측정 가능한 오버헤드가 있는 경우가 많습니다. 제품이 기본 테스트 및 릴리스 단계에 진입하면 문자열 처리에 대한 의존도를 제거하거나 최소화하기 위해 이러한 시스템을 개발할 때 계획을 수립해야 합니다.
동적 그리기 제출의 과도한 사용
최신 비디오 하드웨어는 정적 데이터를 처리할 때 잘 수행됩니다. 하이 엔드 어댑터는 대개 비디오 메모리가 매우 많지만 동적 데이터로는 이 메모리를 효과적으로 활용할 수 없습니다.
동적 콘텐츠에 대해 동적 꼭짓점 버퍼/인덱스 버퍼의 합리적으로 효율적인 사용 패턴을 구현할 수 있지만, 많은 타이틀이 이 관용구를 정적 콘텐츠에 과용합니다. 하드웨어에 매핑되지 않고 모든 프레임에 대한 버퍼로 처리되어야 하는 데이터 구조에 기하 도형을 저장하는 BSP(이진 공간 분할) 트리 및 포털 기반 시스템을 사용하는 경우가 많습니다. 가능한 한 많은 콘텐츠를 정적 리소스에 배치하면 데이터를 비디오 카드 전송하는 대역폭 오버헤드를 크게 줄이고, 온보드 VRAM을 더 잘 활용하며, 이 콘텐츠 처리와 관련된 CPU/캐시 오버헤드를 줄일 수 있습니다.
파일 처리의 높은 오버헤드
PC 게임은 특히 엄격한 로딩 시간 요구 사항이 있는 콘솔 타이틀과 비교할 때 장시간 로드하는 것으로 명성을 얻었습니다. 많은 타이틀이 파일 하위 시스템을 사용하는 방식을 분석한 결과 몇 가지 일반적인 문제가 드러났습니다.
파일을 여는 오버헤드는 일반적으로 개발자가 기대하는 것보다 훨씬 높습니다. 주문형 바이러스 스캐너를 광범위하게 사용하고 NTFS의 추가 기능을 사용하면 파일을 여는 것은 상당히 비용이 많이 드는 작업입니다. 한 번에 많은 파일을 열거나 동일한 파일을 반복적으로 열고 닫는 것은 파일 관리를 처리하는 잘못된 방법입니다. 일부 게임은 파일을 열기 전에 파일의 존재를 테스트하여 이 성능 비용을 완화하려고 시도했습니다. 실제로 NTFS에 파일이 있는지 테스트하려면 파일을 열어야 하므로 열기 전에 테스트하면 비용이 두 번 지불됩니다.
추가 기능 수정 또는 모드를 허용하거나 데이터 파일 재정의를 위해 검사 개발 스캐폴딩을 포함하는 게임은 해당 파일이 없는 경우에도 이러한 파일을 확인하여 게임을 로드하는 데 상당한 지연이 있을 수 있습니다. 이 기능을 사용하는 사용자만 실제로 이러한(종종 광범위한) 검사의 성능 비용을 지불할 수 있도록 특별한 명령줄 스위치 또는 기타 모드 표시기를 사용하여 실행할 때 게임이 이러한 파일에 대해서만 검사 것이 좋습니다.
다음을 통해 파일 시스템에서 추가 성능을 얻을 수 있습니다.
- 파일 시스템 힌트의 적절한 사용은 FILE_FLAG_RANDOM_ACCESS 및 FILE_FLAG_SEQUENTIAL_SCAN
- OS의 읽기/쓰기 API에 대한 대량의 호출을 방지하기 위한 버퍼 크기 조정
- 비동기적으로 파일 액세스
- 백그라운드에서 스레드 로드
또한 게임을 처음 실행할 때 변환에 의존하지 않고(빌드 또는 설치 시) 데이터를 오프라인으로 변환하는 것이 좋습니다. 이렇게 하면 모든 사용자에게 상당한 성능 세금이 부과됩니다.
느리고 실망스러운 설치
우리가 본 또 다른 일반적인 문제는 많은 현대 PC 게임에 필요한 매우 긴 설치 시간입니다. 설치 관리자는 사용자에게 "DirectX를 설치할 필요가 없습니다"라는 메시지를 여러 번 표시하기만 하면 됩니다. 일반적으로 이러한 위반 설치 관리자는 사용자가 게임 설치가 실제로 시작되기 전에 다음 또는 확인을 여러 번 선택해야 합니다. 시작되면 사용자가 게임을 플레이할 수 있는 기회를 가져오기까지 일부 타이틀이 한 시간 이상 걸리는 것을 보았습니다. 우리는 게임 플레이 경험의 첫 번째 시간이 설치되어서는 안된다고 강하게 생각합니다.
설치를 처리하기 위한 다양한 방법을 권장합니다. 먼저 프롬프트를 단순하고 최소한으로 유지합니다. 둘째, 가능한 경우 배포 디스크에서 직접 일부 또는 모든 데이터 파일을 사용할 수 있도록 게임 데이터를 설계합니다. 최신 DVD 드라이브는 대역폭이 매우 높습니다. 셋째, 타이틀에서 주문형 설치를 구현하여 설치 프로세스를 줄이거나 없애고 사용자가 가능한 한 빨리 게임에 들어갈 수 있도록 하는 것이 좋습니다. (주문형 설치에 대한 자세한 내용은 게임용 주문형 설치를 참조하세요.)
게임 설치에 대한 자세한 권장 사항은 게임 설치 간소화를 참조하세요.
실제 메모리 고려 사항 부족
시장에서 PC 하드웨어의 광범위한 가변성으로 인해 타이틀은 일반적으로 임시 구성 테스트를 사용하여 그래픽 세부 정보 수준에 대한 기본 설정을 선택합니다. 우리가 본 타이틀 중 일부는 이러한 테스트에서 비디오 메모리 크기를 사용하지만 실제 메모리 크기와 상관 관계를 지정하지 못합니다. 디바이스 손실 상황을 처리하기 위해 대부분의 비디오 메모리(카드 로컬 VRAM 및 비 로컬 AGP 메모리 조리개 모두)는 관리되는 리소스 또는 사용자 지정 데이터 구조를 사용하여 실제 메모리에 의해 지원되어야 합니다. 일부 고급 비디오 카드에는 저가형 CPU 메모리의 크기에 필적하는 VRAM 크기가 있습니다. 시스템이 비디오 카드 비해 물리적 메모리가 제한된 상황에서는 대부분의 VRAM을 효과적으로 활용할 수 없으며 더 낮은 세부 정보 설정을 구성해야 합니다.
Real-Time 오디오 샘플 속도 변환에 대한 Over-Reliance
본 CPU 주기 굽기의 또 다른 일반적인 소스는 혼합 중에 재생 속도를 하드웨어 버퍼로 변환하기 위해 오디오 시스템이 필요할 때 발생합니다. WDM(Windows 드라이버 모델) 드라이버를 사용하면 하드웨어 버퍼 형식이 커널 수준 리소스이므로 직접 애플리케이션 제어를 받지 않습니다. 대신 모든 원본의 최고 품질 형식과 하드웨어 기능에 따라 형식이 선택됩니다. 기본적으로 Windows XP는 이 프로세스에 고품질 샘플 속도 변환을 사용하며, 대부분의 오디오 샘플에 속도 변환이 필요한 경우 CPU 주기의 상당 부분이 사용됩니다.
동일한 샘플 속도로 모든 DirectSound 버퍼를 만드는 것이 좋습니다. Microsoft Win32 waveOut 함수를 사용하는 경우 이와 함께 일관된 샘플 속도도 사용해야 합니다. WDM 드라이버를 사용하면 버퍼가 모두 커널에 의해 혼합되며, 일부 드라이버에서 더 높은 샘플링 속도를 사용하는 경우 나머지 모든 샘플 속도는 일치하도록 변환됩니다. 이는 스트리밍 오디오 압축 해제 버퍼를 포함하여 모든 오디오 샘플에 대해 동일한 재생 속도를 사용하는 것을 의미합니다. Windows 98 또는 Windows Millennium Edition을 대상으로 지정하지 않는 한 기본 버퍼 속도를 설정하는 것은 효과가 없습니다.
참고
Windows Vista 이상 버전의 운영 체제에서 DirectSound 및 waveOut은 모든 오디오 출력에 WASAPI(Windows 오디오 세션 API) 를 사용합니다.
가상 메모리 조각화
프로세스 메모리 공간의 32비트 제한과 관련된 최근 여러 문제를 살펴보았습니다. 사용자 모드 프로세스에 대한 2GB의 가상 주소 공간은 지금까지 적절한 것보다 더 많지만, 대용량 메모리 매핑 파일, 사용자 지정 메모리 할당자 및 VRAM 크기(프로세스 공간에 매핑해야 하는)의 증가로 인해 가상 메모리 공간 할당이 실패하는 상황이 발생하기 시작했습니다. 일부 비 Microsoft DLL은 가상 주소 공간 중간에 고정 시작 위치를 사용하고 있으며, 이로 인해 조각화가 발생하여 할당이 실패합니다.
이러한 문제는 게임에서 가상 메모리 공간의 큰 연속 청크를 할당하려고 시도하는 사용자 지정 메모리 할당 체계를 사용할 때 가장 자주 나타납니다. 필요에 따라 가상 주소 공간의 보다 합리적인 크기의 부분을 요청하도록 할당자를 작성하는 것이 좋습니다. 예를 들어 한 번에 64 또는 256MB를 요청하지만 1GB는 요청하지 않습니다. 그러나 추가 조각화를 일으키지 않도록 주의해야 합니다. 64비트 운영 체제 및 하드웨어의 출현은 향후 이러한 문제에 큰 도움이 될 것이지만 현재 세대 32비트 시스템에서는 주의해야 합니다.
Floating-Point 컨트롤 Word 조작
디버깅을 지원하기 위해 일부 개발자는 부동 소수점 제어 단어의 조작을 통해 FPU(부동 소수점 단위)에서 예외를 사용하도록 설정했습니다. 이렇게 하면 매우 문제가 되며 프로세스가 충돌할 수 있습니다. 호출 규칙에서 ebx 레지스터를 보존해야 하는 것과 마찬가지로 시스템의 대부분은 FPU가 기본 상태이고 적절한 결과를 제공하며 예외를 생성하지 않는다고 가정합니다. 드라이버 및 기타 시스템 구성 요소는 종종 표준 오류 값이 잘못된 조건에 대한 레지스터에 표시된다는 가정하에 결과를 계산하지만 예외를 사용하도록 설정하면 처리되지 않고 크래시가 발생합니다.
Direct3D는 D3DCREATE_FPU_PRESERVE 플래그를 사용하지 않는 한 부동 소수점 단위를 호출 스레드에 대한 초기화의 일부로 단정밀도로 설정하고, 이 경우 부동 소수점 제어 단어는 그대로 유지됩니다. 컨트롤 단어는 스레드별 설정이므로 모든 애플리케이션 스레드를 단일 정밀도 모드로 설정하면 성능이 최적화됩니다. _control87 호출하는 것은 SSE를 단독으로 사용하는 x64 네이티브 코딩에는 유효하지 않으며 Xbox 360 CPU의 PowerPC 기반 아키텍처에서는 비용이 매우 많이 듭니다.
참고
컨트롤 단어를 수정하는 경우 _controlfp_s 사용하고 x64 플랫폼의 경우 컨트롤 단어를 통해 부동 소수점 정밀도를 변경할 수 없다는 점에 유의하세요.
소프트웨어 꼭짓점 셰이더 또는 컴파일 처리와 같은 다른 반올림 규칙 또는 기타 동작이 필요한 모든 라이브러리에서 컨트롤 단어를 저장하고 복원합니다. 게임이 비표준 반올림 또는 FPU 예외를 사용해야 하는 경우 부동 소수점 제어 단어를 저장하고 복원해야 하며 시스템 API를 포함하여 이러한 문제로부터 안전한 것으로 입증되지 않은 외부 코드를 호출하지 않아야 합니다.
DirectX 런타임의 선택적 설치
많은 게임에서 DirectX를 설치할지 여부를 사용자에게 묻습니다. 사용자가 시스템에 최신 DirectX 재배포 가능 패키지가 설치되어 있고 설치를 건너뛰고 설치가 성공적으로 계속된다고 가정하면 문제가 발생할 수 있습니다. 게임에 특정 버전의 D3DX 또는 설치되지 않은 다른 업데이트된 기능이 필요한 경우 게임이 작동하지 않으며 사용자가 매우 좌절하게 됩니다.
게임의 설치 관리자가 게임이 빌드된 DirectX 재배포 가능 패키지는 자동으로 설치하는 것이 좋습니다. DirectX 설치 프로세스는 업데이트해야 하는지 여부를 확인하고 업데이트하지 않으면 신속하게 반환하도록 설계되었습니다. 따라서 DirectX 설치에 대해 사용자에게 요청할 필요가 없습니다.
설치 패키지에서 다음 명령을 실행하여 DirectX 자동 설치를 수행할 수 있습니다.dxsetup.exe /silent
또한 재배포 가능 폴더의 실제 크기는 게임의 대상 플랫폼 및 사용에 실제로 필요한 캐비닛 파일(.cab)만 포함하도록 구성할 수 있습니다.
참고
dxsetup을 사용하기 전에 직접 설정 안 을 읽어보세요.
스레드 동기화의 과도한 사용
게임을 프로파일링할 때 주요 핫스팟은 종종 중요한 섹션을 입력하고 나가는 작업과 관련이 있는 것으로 확인됩니다. 다중 코어 CPU의 보급으로 게임에서 다중 스레딩의 사용이 크게 증가했으며 많은 구현에서 스레드 동기화를 많이 사용합니다. 경합 없이 중요한 섹션을 사용하는 CPU 시간은 매우 중요하며 다른 모든 형태의 스레드 동기화는 훨씬 더 비쌉니다. 따라서 이러한 기본 형식의 사용을 최소화하려면 주의해야 합니다.
게임에서 과도한 동기화의 일반적인 소스는 D3DCREATE_MULTITHREADED 사용합니다. 이 플래그는 여러 스레드에서 렌더링하기 위해 Direct3D 스레드로부터 안전하지만 매우 보수적인 접근 방식을 취하므로 동기화 오버헤드가 높습니다. 게임은 이 플래그를 피해야 합니다. 대신 Direct3D와의 모든 통신이 단일 스레드에서 시작되고 스레드 간의 모든 통신이 직접 처리되도록 엔진을 설계합니다. 다중 스레드 게임을 디자인하는 방법에 대한 자세한 내용은 Xbox 360 및 Microsoft Windows의 다중 코어용 코딩 문서를 참조하세요.
RDTSC 사용
x86 명령 RDTSC 는 사용하지 않는 것이 좋습니다. RDTSC 는 CPU 빈도를 동적으로 변경하는 일부 전원 관리 체계와 코어 간에 주기 카운터가 동기화되지 않는 여러 다중 코어 CPU에서 타이밍을 올바르게 계산하지 못합니다. 대신 게임은 QueryPerformanceCounter API를 사용해야 합니다. RDTSC 문제 및 QueryPerformanceCounter를 사용하여 고해상도 타이밍 구현에 대한 자세한 내용은 게임 타이밍 및 멀티코어 프로세서 문서를 참조하세요.