プロセッサが64ビット対応になって何がうれしいかというと、まず広大なアドレス空間を利用できるようになったことでしょう。試しに32ビットシステムと64ビットシステム用の同じプログラムで、どれだけのメモリを利用できるかという点をList1のプログラムで確認してみることにします。
List.1 仮想メモリのサイズを取得するプログラム |
void main() { MEMORYSTATUS status; status.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus( &status ); _tprintf( TEXT("Total Virtual Space: %u MB\n"), status.dwTotalVirtual / (1024 * 1024) ); } |
実行結果はTable4のとおりで(付録CD-ROMに実行画面のスクリーンショットを収録)、32ビットシステムでは2Gバイトまでしか仮想アドレスがサポートされていなかったのに対し、64ビットシステムでは8Tバイトもの仮想アドレスを利用できることが分かります。しかし、ちょっと待ってください。8Tバイトというと、プロセッサが使用できる最大仮想アドレスサイズには達していません。これはx64版のWindowsの仕様によるものです。しかし、32ビットシステムで利用可能だった2Gバイトのアドレス空間に比べるとはるかに大きな値となっていることがわかります。Table5にメモリの仕様を示します。
|
|
汎用レジスタが64ビットに拡張され、数も増えたということは前述したとおりですが、ロングモード(IA-32eモード)では、このレジスタをネイティブな64ビットデータ型として利用できます(ユーザーアプリケーションは64ビットモードのみ)。
64ビットのデータ型をネイティブで利用しているかどうかを、List2に示すような単純な64ビット値の乗算プログラムで確認してみましょう。
List2 64ビット型がネイティブで使われているかどうかを確認する |
__int64 A = 0x7fffffff; void main() { _tprintf( TEXT("Answer: %016I64lx\n"), A * A ); } |
List2を32ビットアプリケーションとしてビルドしたあと、その逆アセンブルしたものがList3です[注2]。それぞれの命令が何をしているか、分かりやすいように各行にコメントをつけておきました。32ビットシステムでは64ビットの値を計算するのに桁上がりや符号拡張が必要になるために、かなり長い命令が生成され、さらに関数コールまで行われていることが分かります。
List.3 32ビットバージョンで生成(64bit.asm) |
mov eax, DWORD PTR ?a@@3_JA+4 ;eaxレジスタにグローバル変数Aの下位32ビットをロード mov ecx, DWORD PTR ?a@@3_JA ;ecxレジスタにグローバル変数Aの上位32ビットをロード push eax ; push ecx ; push eax ;次に呼ぶ関数のためにデータをスタックに格納する push ecx ; call __allmul ;64ビットの乗算を行う関数をコール push edx ;結果の上位をスタックに格納 push eax ;結果の下位をスタックに格納 push OFFSET FLAT:... ;出力フォーマットのオフセットをスタックに格納 call _printf ;表示 add esp, 12 |
一方、64ビットアプリケーションとしてビルドしたあと、逆アセンブルしたものがList4です。List3と比較して、とてもシンプルな形になっています。64ビットシステムでは64ビットの値をネイティブでサポートしているため、たった1つの命令で計算が完了するのです。
List.4 64ビットバージョンで生成(64bit.asm) |
mov rdx, QWORD PTR ?a@@3_JA ;rdxレジスタに64ビットのグローバル変数Aをロード lea rcx, OFFSET FLAT:... ;出力フォーマットのオフセットを計算 imul rdx, rdx ;乗算 call printf ;表示 |
また、関数コールやスタックの利用もなく、よりシンプルな形になったということは、当然処理速度の向上も期待できることになります。64ビットのネイティブ化によって、より大きな値を扱う計算がいかに改善されるかは、この例で見てとることができるかと思います。
|
Copyright(C) 2010 SOFTBANK Creative Inc. All Right Reserved.