ゲーム開発者向けの 64 ビット プログラミング
プロセッサメーカーは、デスクトップコンピュータに64ビットプロセッサのみを出荷しており、ほとんどのラップトップコンピュータのチップセットもx64技術をサポートしています。 ゲーム開発者は、64 ビット プロセッサが新しいアプリケーションで提供する機能強化を活用し、以前のアプリケーションが新しいプロセッサと Windows Vista および Windows 7 の 64 ビット エディションで正しく実行されるようにすることが重要です。 この記事では、互換性と移植の問題に対処し、開発者が 64 ビット プラットフォームへの移行を容易にするのに役立ちます。
現在、Microsoft には次の 64 ビット オペレーティング システムがあります。
- Windows 10
- Windows 11
- Windows Server 2019 以降
過去 64 ビット オペレーティング システム:
- Windows Server 2003 Service Pack 1
- Windows XP Professional x64 Edition (OEM および MSDN を通じて開発者が利用できます)
- Windows Vista
- Windows 7
- Windows 8.0
- Windows 8.1
- Windows Server 2008 - 2016
Note
Windows Server 2008 R2 以降は、64 ビット エディションとしてのみ使用できます。 Windows 11は、64 ビットまたは ARM64 エディションとしてのみ使用できます。
- アドレス指定可能メモリの違い
- ビルド時の大アドレス対応の指定
- 64 ビット プラットフォームでの 32 ビット アプリケーションの互換性
- アプリケーションを 64 ビット プラットフォームに移植する
- 移植されたアプリケーションのプロファイリングと最適化
- 64 ビット オペレーティング システムのマネージド コード
- 64 ビット オペレーティング システムの実行によるパフォーマンスへの影響
- まとめ
アドレス指定可能メモリの違い
ほとんどの開発者が最初に気付くのは、64 ビット プロセッサによって、対処できる物理メモリと仮想メモリの量が大幅に進歩することです。
32 ビット プラットフォーム上の 32 ビット アプリケーションは、最大 2 GB に対応できます。
32 ビット Windows XP または Windows Server 2003 で /LARGEADDRESSAWARE:YES リンカー フラグを使用してビルドされ、特殊な /3 gb ブート オプションを使用して構築された 32 ビット アプリケーションは、最大 3 GB に対応できます。 これにより、カーネルが 1 GB のみに制限されるため、一部のドライバーやサービスが失敗する可能性があります。
Windows Vista、Windows Server 2008、および Windows 7 の 32 ビット エディションで /LARGEADDRESSAWARE:YES リンカー フラグを使用してビルドされた 32 ビット アプリケーションは、ブート構成データ (BCD) 要素 IncreaseUserVa で指定された数までメモリをアドレス指定できます。 IncreaseUserVa には、既定値である 2048 から 3072 までの値を指定できます (これは、Windows XP の /3gb ブート オプションによって構成されたメモリの量と一致します)。 残りの 4 GB はカーネルに割り当てられ、ドライバーとサービスの構成が失敗する可能性があります。
BCD の詳細については、「 ブート構成データ」を参照してください。
64 ビット プラットフォーム上の 32 ビット アプリケーションは、/LARGEADDRESSAWARE:YES リンカー フラグを使用して、最大 2 GB、または最大 4 GB に対応できます。
64 ビット アプリケーションでは、アドレス指定に 43 ビットが使用されます。これにより、アプリケーションに 8 TB の仮想アドレスが提供され、カーネル用に 8 TB が予約されます。
メモリだけでなく、メモリ マップファイル I/O を使用する 64 ビット アプリケーションは、仮想アドレス空間の増加によって大きなメリットを得られます。 64 ビット アーキテクチャでは、浮動小数点のパフォーマンスが向上し、パラメーターの受け渡しも高速化されています。 64 ビット プロセッサには、汎用型とストリーミング SIMD 拡張 (SSE) 型の両方のレジスタ数の 2 倍と、SSE および SSE2 命令セットのサポートがあります。多くの 64 ビット プロセッサでは、SSE3 命令セットもサポートされています。
ビルド時の大アドレス対応の指定
32 ビット アプリケーションをビルドするときに、リンカー フラグ /LARGEADDRESSAWARE を使用して、アプリケーションが 64 ビット プラットフォームを対象としていない場合でも、コストなしで得られる利点があるため、大きなアドレス対応を指定することをお勧めします。 前に説明したように、ビルドに対してこのフラグを有効にすると、32 ビット OS または 64 ビット OS 上の特別なブート オプションを使用して、32 ビット プログラムがより多くのメモリにアクセスできるようになります。 ただし、開発者は、上位ビットが 32 ビット ポインターで決して設定されていないと仮定するなど、ポインターの想定が行われないように注意する必要があります。 一般に、/LARGEADDRESSAWARE フラグを有効にすることをお勧めします。
大きなアドレス対応の 32 ビット アプリケーションは、 GlobalMemoryStatusEx を呼び出すことによって、現在の OS 構成で使用可能な仮想アドレス空間の合計を実行時に決定できます。 ullTotalVirtual の結果は、2147352576 バイト (2 GB) から4294836224 バイト (4 GB) までの範囲になります。 3221094400 (3 GB) を超える値は、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 ビット プラットフォームで問題なく実行されます。 いくつかのアプリケーションで次のような問題が発生する可能性があります。
- 64 ビット エディションの Windows オペレーティング システム用のすべてのドライバーは、64 ビット バージョンである必要があります。 新しい 64 ビット ドライバーを要求すると、古いドライバーに依存するコピー保護スキームに影響します。 カーネル モード ドライバーは、64 ビット エディションの Windows によって読み込まれるには Authenticode 署名されている必要があることに注意してください。
- 64 ビット プロセスでは 32 ビット DLL を読み込めません。また、32 ビット プロセスでは 64 ビット DLL を読み込めません。 開発者は、開発に進む前に、64 ビット バージョンのサード パーティ製 DLL を使用できることを確認する必要があります。 64 ビット プロセスで 32 ビット DLL を使用する必要がある場合は、Windows プロセス間通信 (IPC) を使用できます。 COM コンポーネントでは、アウトプロセス サーバーとマーシャリングを使用して境界間の通信を行うこともできますが、これを行うとパフォーマンスが低下する可能性があります。
- 多くの x64 プロセッサもマルチコア プロセッサであり、開発者はこれがレガシ アプリケーションにどのように影響するかをテストする必要があります。 マルチコア プロセッサとゲーム アプリケーションへの影響の詳細については、「 Game Timing および Multicore Processors」を参照してください。
- 一部のフォルダー名は特定のケースで変更されたので、アプリケーションは SHGetFolderPath を呼び出してファイル パスを検出する必要もあります。 たとえば、CSIDL_PROGRAM_FILESは、"C:\Program Files" ではなく、64 ビット プラットフォームで実行されている 32 ビット アプリケーションの "C:\Program Files(x86)" を返します。 開発者は、WOW64 エミュレーターのリダイレクトとリフレクション機能の動作に注意する必要があります。
さらに、開発者は、まだ使用している可能性がある 16 ビット プログラムに注意する必要があります。 WOW64 は 16 ビット アプリケーションを処理できません。これには、古いインストーラーとすべての MS-DOS プログラムが含まれます。
Note
最も一般的な互換性の問題は、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 の使用、アセンブリ コード、古いバイナリ ファイルなどがあります。
Note
Visual C++ 2010 以降には stdint.h および cstdint C99 ヘッダーが含まれており、標準の移植性の種類int32_t、uint32_t、int64_t、uint64_t、intptr_t、uintptr_tを定義します。 これらを標準の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 呼び出しに置き換えられました。
x64 プラットフォームでは、x86 プラットフォームよりも、アラインされていないアクセスに対するパフォーマンスの低下が大きくなります。 TYPE_ALIGNMENT(t) マクロと FIELD_OFFSET(t, member) マクロを使用して、コードで直接使用できるアラインメント情報を決定できます。 これらの前述のマクロを正しく使用すると、アラインされていないアクセスペナルティの可能性を排除する必要があります。
TYPE_ALIGNMENT マクロ、FIELD_OFFSET マクロ、および一般的な 64 ビット プログラミング情報の詳細については、「 64 ビット Windows プログラミング: 移行のヒント:ポインターを使用するための追加の考慮事項と規則」を参照してください。
-
アセンブリ コード
-
インライン アセンブリ コードは 64 ビット プラットフォームではサポートされていないため、置き換える必要があります。 アーキテクチャの変更によってアプリケーションのボトルネックが変更された可能性があり、C/C++ または組み込み関数では、読みやすいコードで同様の結果が得られます。 最も推奨される方法は、すべてのアセンブリ コードを C または C++ に切り替える方法です。 組み込み関数はアセンブリ コードの代わりに使用できますが、完全なプロファイリングと分析が実行された後にのみ使用する必要があります。
x87、MMX、3DNow! 命令セットは、64 ビット モードでは非推奨です。 命令セットは、32 ビット モードの下位互換性のために引き続き存在します。ただし、将来の互換性の問題を回避するために、現在および将来のプロジェクトでの使用は推奨されません。
-
非推奨の API
-
一部の古い DirectX API は、64 ビット ネイティブ アプリケーション (DirectPlay 4 以前、DirectDraw 6 以前、Direct3D 8 以前、DirectInput 7 以前) で削除されています。 また、DirectMusic のコア API はネイティブの 64 ビット アプリケーションで使用できますが、パフォーマンス レイヤーと DirectMusic プロデューサーは非推奨です。
Visual Studio は非推奨の警告を発行します。これらの変更は、最新の API を使用する開発者にとって問題ではありません。
移植されたアプリケーションのプロファイリングと最適化
すべての開発者は、新しいアーキテクチャに移植されているアプリケーションを再プロファイルする必要があります。 64 ビット プラットフォームに移植される多くのアプリケーションには、32 ビット バージョンとは異なるパフォーマンス プロファイルがあります。 開発者は、最適化する必要がある内容を評価する前に、64 ビット のパフォーマンス テストを実行する必要があります。 これに関する良いニュースは、多くの従来の最適化が64ビットプラットフォームで動作することです。 さらに、64 ビット コンパイラでは、コンパイラ フラグとコーディング ヒントを正しく使用して多くの最適化を実行することもできます。
一部の構造体では、メモリ領域を節約し、キャッシュを向上させるために、内部データ型が並べ替えられます。 場合によっては、完全な 64 ビット ポインターの代わりに配列インデックスを使用できます。 /fp:fast フラグを使用すると、浮動小数点の最適化とベクター化を改善できます。 __restrict、declspec(restrict)、declspec(noalias) を使用すると、コンパイラがエイリアシングを解決し、レジスタ ファイルの使用を向上させることができます。
/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 ビット ポインターが使用され、その命令は少し大きくなるため、メモリ要件が若干増加します。 これにより、パフォーマンスが若干低下する可能性があります。 一方、レジスタの数が 2 倍で、1 つの命令で 64 ビット整数計算を実行できる場合は、多くの場合、補正以上のものになります。 結果として、64 ビット アプリケーションの実行速度は、32 ビットと同じアプリケーションよりも若干遅くなる可能性がありますが、多くの場合、若干高速に実行されます。
まとめ
64 ビット アーキテクチャを使用すると、開発者はゲームの外観、サウンド、再生方法に関する制限をプッシュできます。 ただし、32 ビット プログラミングから 64 ビット プログラミングへの移行は簡単ではありません。 2 つの違いを理解し、最新のツールを使用することで、64 ビット プラットフォームへの移行をより簡単かつ迅速に行うことができます。
64 ビット プログラミングの詳細については、「 Visual C++ Developer Center: 64 ビット プログラミング」を参照してください。