ゲーム開発者のための 64 ビット プログラミング
XNA デベロッパー コネクション (XDC)
2008 年 4 月
プロセッサの製造元がデスクトップ コンピューター向けに出荷しているのは 64 ビット プロセッサのみとなり、ほとんどのラップトップ コンピューターのチップセットも x64 テクノロジをサポートしています。ゲーム開発者にとって重要なのは、64 ビット プロセッサで進歩した技術を新しいアプリケーションで利用することと、以前に開発したアプリケーションが新しいプロセッサおよび 64 ビット版 Windows Vista で正しく動作するのを確認することです。この記事では、互換性と移植の問題について説明し、64 ビット プラットフォームへのスムーズな移行を支援する情報を提供します。
Microsoft が現在提供している 64 ビット プラットフォームは、Windows Server 2003 Service Pack 1、Windows XP Professional x64 Edition (OEM に提供または MSDN を通じて開発者に提供)、および Windows Vista です。
この記事では、以下のトピックについて説明します。
- アドレス可能なメモリーの違い
- 構築時に大きいサイズのアドレスへの対応を指定する
- 64 ビット プラットフォームでの 32 ビット アプリケーションの互換性
- 64 ビット プラットフォームへのアプリケーションの移植
- 移植するアプリケーションのプロファイリングと最適化
- 64 ビット オペレーティング システムでのマネージ コード
- 64 ビット オペレーティング システムを使用することによるパフォーマンスへの影響
- まとめ
アドレス可能なメモリーの違い
ほとんどの開発者がまず気付くのは、アドレス指定可能な物理メモリーと仮想メモリーの量が 64 ビット プロセッサで大きく増えている点です。
32 ビット プラットフォームで 32 ビット アプリケーションがアドレス指定できるのは 2 GB までです。
特別な /3gb ブート オプションが指定された 32 ビット Windows XP または Windows Server 2003 で /LARGEADDRESSAWARE:YES リンカー フラグを使用して構築された 32 ビット アプリケーションは、3 GB までアドレス指定できます。この場合、カーネルが 1 GB のみに制限されるため、一部のドライバーやサービスが機能しなくなる可能性があります。
32 ビット版 Windows Vista および 32 ビット版 Windows Server (コード ネーム "Longhorn") オペレーティング システムで /LARGEADDRESSAWARE:YES リンカー フラグを使用して構築された 32 ビット アプリケーションは、ブート構成データ (BCD) 要素 IncreaseUserVa で指定された数値までメモリーをアドレス指定できます。IncreaseUserVa に指定できる値は、2048 (既定値) ~ 3072 です (3072 は Windows XP で /3gb ブート オプションによって構成されるメモリー量と一致します)。4 GB の残りがカーネルに割り当てられるので、ドライバーやサービスの構成が機能しなくなる可能性があります。
BCD の詳細については、MSDN の「Boot Configuration Data (ブート構成データ)」を参照してください。
64 ビット プラットフォーム上の 32 ビット アプリケーションは、2 GB まで、または /LARGEADDRESSAWARE:YES リンカー フラグを使用することで 4 GB までアドレス指定できます。
64 ビット アプリケーションはアドレス指定に 43 ビットを使用します。これにより、アプリケーションに 8 TB の仮想アドレスが提供され、カーネル用に 8 TB が確保されます。
メモリーマップ型ファイル I/O を使用する 64 ビット アプリケーションは、メモリーだけではなく、仮想アドレス空間が増大したことで大きなメリットを得ています。また、64 ビット アーキテクチャによって、浮動小数点のパフォーマンスが向上し、パラメーターの受け渡しが速くなりました。64 ビット プロセッサは、汎用および SSE (ストリーミング SIMD 拡張命令) タイプのレジスタの数が倍増しており、SSE と SSE2 命令セットもサポートしています。また、多くの 64 ビット プロセッサが SSE3 命令セットもサポートしています。
構築時に大きいサイズのアドレスへの対応を指定する
32 ビット アプリケーションを構築するときは、64 ビット プラットフォーム向けでない場合でも、/LARGEADDRESSAWARE リンカー フラグを使用して大きいサイズのアドレスへの対応を指定することをお勧めします。これでコストをかけずにメリットを得られます。既に説明したように、ビルドに対してこのフラグを有効にすると、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 の大部分は WOW64 (Windows 32-bit on Windows 64-bit) エミュレーターを通じて提供されます。WOW64 によって、これらの API が意図されたとおりに機能するようになります。
WOW64 には、32 ビット データのマーシャリングを処理する実行レイヤーがあります。WOW64 は DLL ファイル要求をリダイレクトし、32 ビット アプリケーションの一部のレジストリ ブランチをリダイレクトし、32 ビット アプリケーションと 64 ビット アプリケーションの一部のレジストリ ブランチを反映させます。
WOW64 の詳細については、MSDN の「WOW64 Implementation Details (WOW64 の実装に関する詳細)」を参照してください。WOW64 上で稼働するアプリケーションの構築に関するベスト プラクティスについては、Windows Hardware Developer Central の「WOW64 のベスト プラクティス」を参照してください。
互換性の潜在的な落とし穴
32 ビット プラットフォーム用に開発されたアプリケーションのほとんどは、64 ビット プラットフォームでも問題なく動作します。ごく少数のアプリケーションで、次のような問題が発生する可能性があります。
- 64 ビット OS 上のドライバーは、すべて 64 ビット版でなければなりません。新しい 64 ビット ドライバーが必要になると、古いドライバーに依存していたコピー防止スキームに影響が出ます。
- 64 ビット プロセスは 32 ビット DLL を読み込めず、32 ビット プロセスは 64 ビット DLL を読み込めません。開発を進める前に、サードパーティの DLL に 64 ビット版があるかどうかを確認する必要があります。64 ビット プロセスで 32 ビット DLL を使用する必要がある場合は、Windows のプロセス間通信 (IPC) を使用できます。COM コンポーネントでは、アウトプロセス サーバーとマーシャリングを利用して境界間で通信することもできますが、これを行うとパフォーマンスが低下するおそれがあります。
- x64 プロセッサの多くはマルチコア プロセッサでもあります。開発者は、このことがレガシ アプリケーションにどう影響するかをテストする必要があります。マルチコア プロセッサおよびゲーム アプリケーションへの影響の詳細については、「ゲームのタイミングとマルチコア プロセッサ」を参照してください。
- 場合によってはフォルダー名が変更されていることがあるので、アプリケーションで https://msdn2.microsoft.com/en-us/library/bb762181.aspx を呼び出してファイル パスを確認する必要があります。たとえば、64 ビット プラットフォームで稼働する 32 ビット アプリケーションの場合は、CSIDL_PROGRAM_FILES から "C:\Program Files" ではなく "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 は、x86 と x64 の両方のコード生成をサポートし、x64 コードを生成するために最適化されたライブラリを備えています。ただし、開発者は、アプリケーションと共に Visual C 2005 ランタイムを配布する必要もあります。Visual Studio 2005 Express Editions には x64 コンパイラが含まれていませんが、Standard、Professional、Team System の各バージョンには含まれていることに注意してください。
32 ビット プラットフォームをターゲットとしている開発者は、64 ビット開発に備えておくことで、その後の移行が楽になります。32 ビット プロジェクトをコンパイルする際には、/Wp64 フラグを使用します。これを使用すると、移植性に影響する問題に関する警告が生成されます。64 ビットのツールとライブラリに切り替えると、最初に新たなビルド エラーが多数発生する可能性があるので、ビットに依存しないツールとライブラリに切り替え、警告を修正してから 64 ビット ビルドに切り替えることをお勧めします。
ただし、ツールとライブラリを変更し、特定のコンパイラ フラグを使用するだけでは十分ではありません。コーディング標準の前提事項を再評価して、現在のコーディング標準で移植性の問題が生じないことを確認する必要があります。移植性の問題には、ポインターの切り捨て、データ型のサイズとアライメント、32 ビット DLL への依存、レガシ API の使用、アセンブリ コード、古いバイナリ ファイルなどが挙げられます。移植に関する主な問題は次のとおりです。
- ポインターの切り捨て
- データ型とバイナリ ファイル
- 古い Win32 API とデータ アライメント
- アセンブリ コード
- 非推奨となった API
ポインターの切り捨て
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 ビットの互換性を確保するための特別な処理が必要です。詳細については、「Getting Ready for 64-bit Windows: The New Data Types (64 ビット Windows への備え : 新しいデータ型)」を参照してください。
古い Win32 API とデータ アライメント
一部の Win32 API は、非推奨となり、より中立的な API 呼び出しに置き換えられました (たとえば SetWindowLongPtr に替わった SetWindowLong など)。
アクセスをアライメントしないことによるパフォーマンスの低下は、x86 プラットフォームより x64 プラットフォームの方が大きくなります。TYPE_ALIGNMENT(t) マクロと FIELD_OFFSET(t, member) マクロを使用することで、コードで直接使用できるアライメント情報を確認できます。この 2 つのマクロを正しく使用すると、アクセスがアライメントされていない場合のパフォーマンスの低下を軽減できます。
TYPE_ALIGNMENT マクロと FIELD_OFFSET マクロの詳細、および 64 ビット プログラミングに関する一般情報については、「64-bit Windows Programming:Migration Tips:Additional Considerations (64 ビット Windows プログラミング : 移行に関するヒント : その他の考慮事項)」および「Getting Ready for 64-bit Windows:Rules for Using Pointers (64 ビット Windows への備え : ポインターの使用規則)」を参照してください。
アセンブリ コード
64 ビット プラットフォームではインライン アセンブリ コードがサポートされないので、これを置き換える必要があります。アーキテクチャの変更によってアプリケーションのボトルネックが変わることがあります。また、C/C++ や組み込み関数は、同様の結果をよりわかりやすいコードで実現できます。すべてのアセンブリ コードを C または C++ に切り替えることを強くお勧めします。組み込み関数はアセンブリ コードの代わりに使用できますが、完全なプロファイリングと分析を実行してから使用する必要があります。
x87、MMX、3DNow! の各命令セットは、64 ビット モードでは非推奨となっています。これらの命令セットは 32 ビット モードとの下位互換性を維持するために残されていますが、将来の互換性の問題を回避するために、現在のプロジェクトおよび今後のプロジェクトでは使用しないことをお勧めします。
非推奨となった API
一部の古い DirectX API は、64 ビット ネイティブ アプリケーション向けに削除されました。該当するのは、DirectMusic、DirectPlay 4 以前、DirectDraw 6 以前、Direct3D 8 以前、および DirectInput 7 以前です。Visual Studio 2005 では非推奨の警告が表示されますが、最新の API を使用する開発者には関係ありません。
移植するアプリケーションのプロファイリングと最適化
新しいアーキテクチャに移植するアプリケーションは、再度プロファイリングする必要があります。64 ビット プラットフォームに移植されるアプリケーションの多くは、パフォーマンス プロファイルが 32 ビット バージョンから変わります。開発者は、最適化が必要な対象を判断する前に、64 ビットのパフォーマンス テストを実行する必要があります。従来の最適化の多くは、64 ビット プラットフォームでも有効です。さらに、コンパイラ フラグとコーディング ヒントを正しく使用すれば、64 ビット コンパイラでも多くの最適化が実行されます。
一部の構造体では、メモリー領域の節約とキャッシュの強化を目的として、内部データ型の順序を変更している場合があります。場合によっては、フル 64 ビット ポインターの代わりに配列のインデックスを使用できます。/fp:fast フラグによって、浮動小数点の最適化とベクトル化が向上する可能性があります。__restrict、declspec(restrict)、および declspec(noalias) を使用すると、コンパイラによるエイリアシングの解決とレジスタ ファイルの使用効率改善に役立ちます。
/fp:fast の詳細については、「/fp (浮動小数点の動作の指定)」を参照してください。
__restrict の詳細については、「Microsoft-Specific Modifiers (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 ビット ポインターを使用し、命令が少し大きくなるため、メモリーの必要量が若干増えます。このため、パフォーマンスが若干低下する可能性があります。一方で、レジスタの数が倍増し、1 つの命令で 64 ビット整数を計算できるようになることは、それを上回るメリットです。結果的に、64 ビット アプリケーションの実行速度は、32 ビットとしてコンパイルされた同じアプリケーションに比べてわずかに遅くなることもありますが、ほとんどの場合若干速くなります。
まとめ
64 ビット アーキテクチャでの開発では、ゲームの映像、音声、動作に課せられていた制限が解消されます。ただし、32 ビット プログラミングから 64 ビット プログラミングへの移行は簡単ではありません。両プログラミングの違いを理解し、最新のツールを使用することで、64 ビット プラットフォームへの移行を迅速かつ容易に進めることができます。
64 ビット プログラミングの詳細については、Visual C++ デベロッパー センターの「64 ビット プログラミング」を参照してください。