게임 개발자를 위한 64비트 프로그래밍
프로세서 제조업체는 데스크톱 컴퓨터에서 64비트 프로세서를 독점적으로 배송하고 있으며, 대부분의 노트북 컴퓨터의 칩셋도 x64 기술을 지원합니다. 게임 개발자는 64비트 프로세서가 새 애플리케이션과 함께 제공하는 개선 사항을 활용하고 이전 애플리케이션이 새 프로세서 및 Windows Vista 및 Windows 7의 64비트 버전에서 올바르게 실행되도록 하는 것이 중요합니다. 이 문서에서는 호환성 및 포팅 문제를 해결하고 개발자가 64비트 플랫폼으로 쉽게 전환할 수 있도록 지원합니다.
Microsoft에는 현재 다음과 같은 64비트 운영 체제가 있습니다.
- Windows 10
- Windows 11
- Windows Server 2019 이상
이전 64비트 운영 체제:
- Windows Server 2003 서비스 팩 1
- Windows XP Professional x64 Edition(OEM 및 MSDN을 통해 개발자에게 제공)
- Windows Vista
- Windows 7
- Windows 8.0
- Windows 8.1
- Windows Server 2008 - 2016
참고
Windows Server 2008 R2 이상은 64비트 버전으로만 사용할 수 있습니다. Windows 11 64비트 또는 ARM64 버전으로만 사용할 수 있습니다.
- 주소 지정 가능한 메모리의 차이점
- 빌드할 때 큰 주소 인식 지정
- 64비트 플랫폼에서 32비트 애플리케이션의 호환성
- 64비트 플랫폼으로 애플리케이션 포팅
- 포팅된 애플리케이션의 프로파일링 및 최적화
- 64비트 운영 체제의 관리 코드
- 64비트 운영 체제 실행의 성능 영향
- 요약
주소 지정 가능한 메모리의 차이점
대부분의 개발자가 가장 먼저 알 수 있는 것은 64비트 프로세서가 해결할 수 있는 물리적 및 가상 메모리의 양에 큰 도약을 제공한다는 것입니다.
32비트 플랫폼의 32비트 애플리케이션은 최대 2GB를 처리할 수 있습니다.
특수 /3gb 부팅 옵션을 사용하여 32비트 Windows XP 또는 Windows Server 2003에서 /LARGEADDRESSAWARE:YES 링커 플래그로 빌드된 32비트 애플리케이션은 최대 3GB의 주소를 지정할 수 있습니다. 이렇게 하면 커널이 1GB로 제한되어 일부 드라이버 및/또는 서비스가 실패할 수 있습니다.
Windows Vista, Windows Server 2008 및 Windows 7의 32비트 버전에서 /LARGEADDRESSAWARE:YES 링커 플래그를 사용하여 빌드된 32비트 애플리케이션은 BCD(부팅 구성 데이터) 요소 IncreaseUserVa에 지정된 수까지 메모리를 처리할 수 있습니다. IncreaseUserVa는 기본값인 2048부터 3072까지의 값을 가질 수 있습니다(Windows XP의 /3gb 부팅 옵션으로 구성된 메모리 양과 일치). 나머지 4GB는 커널에 할당되며 드라이버 및 서비스 구성이 실패할 수 있습니다.
BCD에 대한 자세한 내용은 부팅 구성 데이터를 참조하세요.
64비트 플랫폼의 32비트 애플리케이션은 /LARGEADDRESSAWARE:YES 링커 플래그를 사용하여 최대 2GB 또는 최대 4GB의 주소를 지정할 수 있습니다.
64비트 애플리케이션은 주소 지정에 43비트 를 사용하며, 애플리케이션에 8TB의 가상 주소와 커널용으로 예약된 8TB를 제공합니다.
메모리뿐 아니라 메모리 매핑된 파일 I/O를 사용하는 64비트 애플리케이션은 증가된 가상 주소 공간의 이점을 크게 활용합니다. 또한 64비트 아키텍처를 통해 부동 소수점 성능이 향상되고 매개 변수 전달이 빨라졌습니다. 64비트 프로세서는 범용 및 스트리밍 SIMD 확장(SSE) 형식의 레지스터 수와 SSE 및 SSE2 명령 집합에 대한 지원의 두 배입니다. 많은 64비트 프로세서는 SSE3 명령 집합도 지원합니다.
빌드할 때 큰 주소 인식 지정
애플리케이션이 64비트 플랫폼용이 아니더라도 무료로 얻을 수 있는 장점 때문에 링커 플래그 /LARGEADDRESSAWARE를 사용하여 32비트 애플리케이션을 빌드할 때 큰 주소 인식을 지정하는 것이 좋습니다. 앞에서 설명한 대로 빌드에 이 플래그를 사용하도록 설정하면 32비트 프로그램이 32비트 OS 또는 64비트 OS에서 특수 부팅 옵션을 사용하여 더 많은 메모리에 액세스할 수 있습니다. 그러나 개발자는 상위 비트가 32비트 포인터에서 설정되지 않는다고 가정하는 등 포인터 가정이 이루어지지 않도록 주의해야 합니다. 일반적으로 /LARGEADDRESSAWARE 플래그를 사용하도록 설정하는 것이 좋습니다.
큰 주소를 인식하는 32비트 애플리케이션은 GlobalMemoryStatusEx를 호출하여 현재 OS 구성으로 사용할 수 있는 총 가상 주소 공간을 런타임에 확인할 수 있습니다. ullTotalVirtual 결과는 2147352576 바이트(2GB)에서 4294836224 바이트(4GB)까지 다양합니다. 3221094400(3GB)보다 큰 값은 64비트 버전의 Windows에서만 얻을 수 있습니다. 예를 들어 IncreaseUserVa의 값이 2560인 경우 결과는 ullTotalVirtual이고 값은 2684223488 바이트입니다.
64비트 플랫폼에서 32비트 애플리케이션의 호환성
64비트 Windows 운영 체제는 IA32 아키텍처와 이진 호환되며, 32비트 애플리케이션에서 사용하는 대부분의 API는 Windows 64비트 에뮬레이터 WOW64의 Windows 32비트에서 사용할 수 있습니다. WOW64는 이러한 API가 의도한 대로 작동하는지 확인하는 데 도움이 됩니다.
WOW64에는 32비트 데이터의 마샬링을 처리하는 실행 계층이 있습니다. WOW64는 DLL 파일 요청을 리디렉션하고, 32비트 애플리케이션에 대한 일부 레지스트리 분기를 리디렉션하고, 32비트 및 64비트 애플리케이션에 대한 일부 레지스트리 분기를 반영합니다.
WOW64에 대한 자세한 내용은 WOW64 구현 세부 정보에서 찾을 수 있습니다.
잠재적 호환성 문제
32비트 플랫폼용으로 개발된 대부분의 애플리케이션은 64비트 플랫폼에서 문제 없이 실행됩니다. 일부 애플리케이션에는 다음을 포함할 수 있는 문제가 있을 수 있습니다.
- Windows 운영 체제의 64비트 버전에 대한 모든 드라이버는 64비트 버전이어야 합니다. 새로운 64비트 드라이버를 요구하는 것은 이전 드라이버를 사용하는 복사 보호 체계에 영향을 미칩니다. 커널 모드 드라이버는 64비트 버전의 Windows에서 로드하려면 Authenticode로 서명되어야 합니다.
- 64비트 프로세스는 32비트 DLL을 로드할 수 없으며 32비트 프로세스는 64비트 DLL을 로드할 수 없습니다. 개발자는 개발을 진행하기 전에 64비트 버전의 타사 DLL을 사용할 수 있는지 확인해야 합니다. 64비트 프로세스에서 32비트 DLL을 사용해야 하는 경우 Windows IPC(프로세스 간 통신)를 사용할 수 있습니다. COM 구성 요소는 Out-of-process 서버를 사용하고 마샬링을 사용하여 경계 간에 통신할 수도 있지만 이렇게 하면 성능 저하가 발생할 수 있습니다.
- 많은 x64 프로세서도 다중 코어 프로세서이며 개발자는 이것이 레거시 애플리케이션에 미치는 영향을 테스트해야 합니다. 멀티 코어 프로세서 및 게임 애플리케이션에 미치는 영향에 대한 자세한 내용은 게임 타이밍 및 멀티 코어 프로세서에서 찾을 수 있습니다.
- 경우에 따라 일부 폴더 이름이 변경되었으므로 애플리케이션은 SHGetFolderPath 를 호출하여 파일 경로를 검색해야 합니다. 예를 들어 CSIDL_PROGRAM_FILES "C:\Program Files"가 아닌 64비트 플랫폼에서 실행되는 32비트 애플리케이션에 대해 "C:\Program Files(x86)"를 반환합니다. 개발자는 WOW64 에뮬레이터의 리디렉션 및 리플렉션 기능이 작동하는 방식을 염두에 두어야 합니다.
또한 개발자는 여전히 사용 중인 16비트 프로그램을 주의해야 합니다. WOW64는 16비트 애플리케이션을 처리할 수 없습니다. 여기에는 이전 설치 관리자 및 모든 MS-DOS 프로그램이 포함됩니다.
참고
가장 일반적인 호환성 문제는 16비트 코드를 실행하고 복사 보호 체계를 위한 64비트 드라이버가 없는 설치 관리자입니다.
다음 섹션에서는 레거시 프로그램이 64비트 플랫폼에서 작동하도록 하려는 개발자를 위해 코드를 64비트 네이티브로 포팅하는 것과 관련된 문제에 대해 설명합니다. 또한 64비트 프로그래밍에 익숙하지 않은 개발자를 위한 것입니다.
64비트 플랫폼으로 애플리케이션 포팅
적절한 도구와 라이브러리를 사용하면 32비트에서 64비트 개발로 쉽게 전환할 수 있습니다. DirectX 9 SDK에는 x86 및 x64 기반 프로젝트를 모두 지원하는 라이브러리가 있습니다. Microsoft Visual Studio 2005 및 Visual Studio 2008은 x86 및 x64 모두에 대한 코드 생성을 지원하며 x64 코드 생성에 최적화된 라이브러리와 함께 제공됩니다. 그러나 개발자가 Visual C 런타임을 애플리케이션과 배포해야 합니다. Visual Studio 2005 및 Visual Studio 2008의 Express Edition에는 x64 컴파일러가 포함되지 않지만 Standard, Professional 및 Team System 버전이 모두 포함됩니다.
32비트 플랫폼을 대상으로 하는 개발자는 나중에 더 쉽게 전환할 수 있도록 64비트 개발을 준비할 수 있습니다. 32비트 프로젝트를 컴파일할 때 개발자는 /Wp64 플래그를 사용해야 하며, 이로 인해 이식성에 영향을 주는 문제에 대한 경고가 생성됩니다. 64비트 도구 및 라이브러리로 전환하면 처음에는 많은 새 빌드 오류가 발생할 수 있습니다. 따라서 64비트 빌드로 전환하기 전에 비트 중립적 도구 및 라이브러리를 전환하고 경고를 수정하는 것이 좋습니다.
그러나 도구를 변경하고, 라이브러리를 변경하고, 특정 컴파일러 플래그를 사용하는 것만으로는 충분하지 않습니다. 현재 코딩 표준에서 이식성 문제를 허용하지 않도록 코딩 표준의 가정을 다시 평가해야 합니다. 이식성 문제에는 포인터 잘림, 데이터 형식의 크기 및 맞춤, 32비트 DLL에 대한 의존도, 레거시 API 사용, 어셈블리 코드 및 이전 이진 파일이 포함될 수 있습니다.
참고
Visual C++ 2010 이상에는 표준 이식성 유형 int32_t, uint32_t, int64_t, uint64_t, intptr_t 및 uintptr_t 정의하는 stdint.h 및 cstdint C99 헤더가 포함되어 있습니다. 표준 ptrdiff_t 및 size_t 데이터 형식과 함께 이러한 데이터 형식을 사용하는 것이 코드의 이식성을 개선하기 위해 아래에서 사용되는 Windows portabilty 형식보다 선호될 수 있습니다.
주요 포팅 문제는 다음과 같습니다.
-
포인터 잘림
-
포인터는 64비트 OS에서 64비트이므로 다른 데이터 형식에 포인터를 캐스팅하면 잘림이 발생할 수 있으며 포인터 산술 연산으로 인해 손상될 수 있습니다. 일반적으로 /Wp64 플래그를 사용하면 이러한 종류의 문제에 대한 경고가 표시되지만 포인터 형식을 캐스팅할 때 다형 형식(INT_PTR, DWORD_PTR, SIZE_T, UINT_PTR 등)을 사용하는 것이 이 문제를 완전히 방지하는 데 도움이 됩니다. 포인터는 새 플랫폼에서 64비트이므로 개발자는 패딩을 줄이거나 제거하기 위해 포인터의 순서와 클래스 및 구조의 데이터 형식을 검사 합니다.
-
데이터 형식 및 이진 파일
-
포인터는 64비트 플랫폼에서 32비트에서 64비트로 증가하지만 다른 데이터 형식은 증가하지 않습니다. 고정 정밀도 데이터 형식(DWORD32, DWORD64, INT32, INT64, LONG32, LONG64, UINT32, UINT64)은 데이터 형식의 크기를 알고 있어야 하는 위치에서 사용할 수 있습니다. 예를 들어 이진 파일 구조에서 입니다. 포인터 크기 및 데이터 맞춤의 변경은 32비트에서 64비트로의 호환성을 보장하기 위해 특별한 처리가 필요합니다. 자세한 내용은 새 데이터 형식에서 찾을 수 있습니다.
-
이전 Win32 API 및 데이터 정렬
-
일부 Win32 API는 사용되지 않으며 SetWindowLong 대신 SetWindowLongPtr과 같은 보다 중립적인 API 호출로 대체되었습니다.
정렬되지 않은 액세스에 대한 성능 저하는 x86 플랫폼보다 x64 플랫폼에서 더 큽니다. TYPE_ALIGNMENT(t) 및 FIELD_OFFSET(t, 멤버) 매크로를 사용하여 코드에서 직접 사용할 수 있는 맞춤 정보를 확인할 수 있습니다. 앞서 언급한 매크로를 올바르게 사용하면 정렬되지 않은 액세스 페널티가 발생할 수 있습니다.
TYPE_ALIGNMENT 매크로, FIELD_OFFSET 매크로 및 일반적인 64비트 프로그래밍 정보에 대한 자세한 내용은 64비트 Windows 프로그래밍: 마이그레이션 팁:포인터 사용에 대한 추가 고려 사항 및 규칙에서 찾을 수 있습니다.
-
어셈블리 코드
-
인라인 어셈블리 코드는 64비트 플랫폼에서 지원되지 않으며 교체해야 합니다. 아키텍처의 변경으로 인해 애플리케이션 병목 현상이 변경되었을 수 있으며 C/C++ 또는 내장 함수는 읽기 쉬운 코드를 사용하여 유사한 결과를 얻을 수 있습니다. 가장 권장되는 방법은 모든 어셈블리 코드를 C 또는 C++로 전환하는 것입니다. 내장 함수는 어셈블리 코드 대신 사용할 수 있지만 전체 프로파일링 및 분석이 수행된 후에만 사용해야 합니다.
x87, MMX 및 3DNow! 명령 집합은 64비트 모드에서 더 이상 사용되지 않습니다. 지침 집합은 32비트 모드의 이전 버전과의 호환성을 위해 여전히 존재합니다. 그러나 향후 호환성 문제를 방지하려면 현재 및 향후 프로젝트에서 사용하지 않는 것이 좋습니다.
-
사용되지 않는 API
-
64비트 네이티브 애플리케이션인 DirectPlay 4 이하, DirectDraw 6 이하, Direct3D 8 이하, DirectInput 7 이하의 일부 이전 DirectX API가 삭제되었습니다. 또한 DirectMusic의 핵심 API는 네이티브 64비트 애플리케이션에서 사용할 수 있지만 성능 계층과 DirectMusic 생산자는 더 이상 사용되지 않습니다.
Visual Studio에서 사용 중단 경고가 발생하며 이러한 변경 내용은 최신 API를 사용하는 개발자에게는 문제가 되지 않습니다.
포팅된 애플리케이션의 프로파일링 및 최적화
모든 개발자는 새 아키텍처로 이식되는 모든 애플리케이션을 다시 프로파일해야 합니다. 64비트 플랫폼으로 이식되는 많은 애플리케이션에는 32비트 버전과 다른 성능 프로필이 있습니다. 개발자는 최적화해야 하는 사항을 평가하기 전에 64비트 성능 테스트를 실행해야 합니다. 이에 대한 좋은 소식은 많은 기존 최적화가 64비트 플랫폼에서 작동한다는 것입니다. 또한 64비트 컴파일러는 컴파일러 플래그 및 코딩 힌트를 올바르게 사용하여 많은 최적화를 수행할 수도 있습니다.
일부 구조체는 메모리 공간을 절약하고 캐싱을 개선하기 위해 내부 데이터 형식을 다시 정렬할 수 있습니다. 경우에 따라 전체 64비트 포인터 대신 배열 인덱스를 사용할 수 있습니다. /fp:fast 플래그는 부동 소수점 최적화 및 벡터화를 향상시킬 수 있습니다. __restrict, declspec(restrict) 및 declspec(noalias)를 사용하면 컴파일러가 별칭을 resolve 레지스터 파일의 사용을 개선하는 데 도움이 될 수 있습니다.
/fp:fast에 대한 자세한 내용은 /fp(Floating-Point 동작 지정)에서 찾을 수 있습니다.
__restrict 대한 자세한 내용은 Microsoft 특정 한정자에서 찾을 수 있습니다.
declspec(restrict)에 대한 자세한 내용은 최적화 모범 사례에서 찾을 수 있습니다.
declspec(noalias)에 대한 자세한 내용은 __declspec(noalias)에서 찾을 수 있습니다.
64비트 운영 체제의 관리 코드
관리 코드는 도구 체인의 많은 게임 개발자가 사용하므로 64비트 OS에서 작동하는 방식을 이해하는 것이 도움이 될 수 있습니다. 관리 코드는 명령 집합 중립이므로 64비트 OS에서 관리되는 애플리케이션을 실행할 때 CLR(공용 언어 런타임)은 32비트 또는 64비트 프로세스로 실행할 수 있습니다. 기본적으로 CLR은 관리되는 애플리케이션을 64비트로 실행하며 문제 없이 제대로 작동해야 합니다. 그러나 애플리케이션이 네이티브 32비트인 DLL에 의존하는 경우 이 DLL을 호출하려고 할 때 애플리케이션이 실패합니다. 64비트 프로세스에는 완전히 64비트 코드가 필요하며 64비트 프로세스에서는 32비트 DLL을 호출할 수 없습니다. 가장 좋은 장기 솔루션은 네이티브 코드를 64비트로 컴파일하는 것이지만, 완벽하게 합리적인 단기 솔루션은 관리되는 애플리케이션을 /platform:x86 빌드 플래그를 사용해야만 x86용으로 표시하는 것입니다.
64비트 운영 체제 실행의 성능 영향
AMD64 및 Intel 64 아키텍처를 사용하는 프로세서는 기본적으로 32비트 명령을 실행할 수 있으므로 64비트 OS에서도 32비트 애플리케이션을 고속으로 실행할 수 있습니다. 운영 체제 함수를 호출할 때 매개 변수를 32비트와 64비트 간에 변환하는 데 약간의 비용이 들지만 이 비용은 일반적으로 무시할 수 있습니다. 즉, 64비트 OS에서 32비트 애플리케이션을 실행할 때 속도가 느려지지 않습니다.
애플리케이션을 64비트로 컴파일하면 계산이 더 복잡해집니다. 64비트 프로그램은 64비트 포인터를 사용하며 해당 지침은 약간 더 크므로 메모리 요구 사항이 약간 증가합니다. 이로 인해 성능이 약간 저하될 수 있습니다. 반면에 레지스터가 두 배나 많고 단일 명령에서 64비트 정수 계산을 수행할 수 있는 기능은 종종 보정보다 더 많습니다. 결과적으로 64비트 애플리케이션은 32비트로 컴파일된 동일한 애플리케이션보다 약간 느리게 실행될 수 있지만, 종종 약간 더 빠르게 실행됩니다.
요약
64비트 아키텍처를 사용하면 개발자가 게임의 모양, 소리 및 플레이 방식에 대한 제한을 적용할 수 있습니다. 그러나 32비트 프로그래밍에서 64비트 프로그래밍으로 전환하는 것은 간단하지 않습니다. 둘 사이의 차이점을 이해하고 최신 도구를 사용하면 64비트 플랫폼으로의 전환이 더 쉽고 빨라질 수 있습니다.
64비트 프로그래밍에 대한 자세한 내용은 Visual C++ 개발자 센터: 64비트 프로그래밍에서 찾을 수 있습니다.