■ MMX・SIMD・SSE ■
前回はMMX(Multi Media eXtension)技術が実際には浮動小数点
レジスタを使用する物である事を説明し、その動作概念を見て
みました。
MMXレジスタとしては64ビットの大きさを使用でき、いっき
に8バイトのデータを処理する事ができるのでした。
今回はMMX技術を使って実際にプログラムを作ってみることに
します。
以前に「inc.c」という、データbをデータaに加算するプログ
ラムを作成しました。
これにMMX技術を導入します。
まずは「inc.c」をもう1度見てみましょう。
─↓inc.c───────────────────────
#include <stdio.h>
int main(void);
int sub(char*, char*);
int main()
{
int i;
char a[8]={ 0, 1, 2, 3, 4, 5, 6, 7};
char b[8]={10,11,12,13,14,15,16,17};
for(i=0;i<8;i++)
{
printf("a[%d]:%d b[%d]:%d\n", i, a[i], i, b[i]);
}
sub(&a[0], &b[0]);
for(i=0;i<8;i++)
{
printf("a[%d]:%d b[%d]:%d\n", i, a[i], i, b[i]);
}
return 0;
}
int sub(char* a, char* b)
{
printf("sub routine called\n");
_asm{
mov edi, a
mov esi, b
mov ecx, 8
LOOP1:
mov al, [edi] ;←データaをレジスタalに移動
mov bl, [esi] ;←データbをレジスタblに移動
add al, bl ;←データa+データbをalレジスタに格納
mov [edi], al ;←alレジスタをデータaに移動
inc edi
inc esi
loop LOOP1
}
}
─↑inc.c───────────────────────
サブ関数はデータaにデータbを加算する処理を行なうのでした。
この「inc.c」にMMX技術を適用して、処理がもっと速く行なわ
れる様に変更してみます。
「inc.c」をコピー&貼り付けして、新しく出来たファイルを
「mmx.c」というファイル名に変更しておきましょう。
今回は「mmx.c」に変更を加えていきます。
まずメイン関数ですが、これは全く変更の必要はありません。
次にサブ関数ですが、以下の様に変更してください。
変更部分にはコメントを入れておきます。
─↓ここから──────────────────────
int sub(char* a, char* b)
{
printf("Sub Routine Called\n");
_asm{
mov edi, a
mov esi, b
movq mm0, [edi] ;データaをmm0レジスタに移動
movq mm1, [esi] ;データbをmm1レジスタに移動
paddd mm0, mm1 ;データa+データbをmm0レジスタに格納
movq [edi], mm0 ;mm0レジスタをデータaに移動
emms ;MMX後処理
}
}
─↑ここまで──────────────────────
まず、変更部分の最初の命令を見てみましょう。
movq mm0, [edi] ;データaをmm0レジスタに移動
新しいレジスタが出てきました。
「mm0」レジスタはMMX技術を使用する為のレジスタで64ビッ
トの大きさがあります。(実際には浮動小数点レジスタです。)
また、新しい命令として「movq」という命令が出てきました。
これは64ビットの大きさのデータを移動する為の命令です。
32ビット以下のデータを移動する為の命令は全て「mov」で
良かったのですが、64ビットのデータを移動する時は「movq」
を使用しなければなりません。
つまり、この「movq mm0, [esi]」という命令は、「メモリか
らレジスタへ64ビット分のデータを移動しなさい」という意
味になります。
以下にこの命令の処理イメージを示します。
1.データaからレジスタへデータを移動する。
movq mm0, [edi] ;データaをmm0レジスタに移動
mm0レジスタ メモリ(データa)
┌────────────────────────┐ ┌───┐
│ 7 6 5 4 3 2 1 0│←───────┤ 0│
└────────────────────────┘ ┌──────┤ 1│
↑ ↑ ↑ ↑ ↑ ↑ ↑ │┌─────┤ 2│
│ │ │ │ │ │ └─────┘│┌────┤ 3│
│ │ │ │ │ └─────────┘│┌───┤ 4│
│ │ │ │ └─────────────┘│┌──┤ 5│
│ │ │ └─────────────────┘│┌─┤ 6│
│ │ └─────────────────────┘│┌┤ 7│
│ └─────────────────────────┘│
└─────────────────────────────┘
レジスタの大きさが64ビットなので一気に8バイトもデータ
を移動しています。
次に同じ様にして今度はデータbをレジスタに移動します。
2.データbからレジスタへデータを移動する。
movq mm1, [esi] ;データbをmm1レジスタに移動
mm1レジスタ メモリ(データb)
┌────────────────────────┐ ┌───┐
│ 17 16 15 14 13 12 11 10│←───────┤ 10│
└────────────────────────┘ ┌──────┤ 11│
↑ ↑ ↑ ↑ ↑ ↑ ↑ │┌─────┤ 12│
│ │ │ │ │ │ └─────┘│┌────┤ 13│
│ │ │ │ │ └─────────┘│┌───┤ 14│
│ │ │ │ └─────────────┘│┌──┤ 15│
│ │ │ └─────────────────┘│┌─┤ 16│
│ │ └─────────────────────┘│┌┤ 17│
│ └─────────────────────────┘│
└─────────────────────────────┘
「mm1」レジスタも「mm0」レジスタと同じ64ビットの大きさ
です。
レジスタの大きさが64ビットなので一気に8バイトのデータ
を移動しています。
メモリからレジスタへデータを移動出来たので、今度は加算す
る処理を行ないます。
paddd mm0, mm1 ;データa+データbをmm0レジスタに格納
新たに「paddd」という命令が出てきました。
これは64ビットの大きさのデータをMMX技術を使用して足し
算しなさい、という命令です。
足し算した結果は「mm0」レジスタに格納されます。
以下にMMX命令での足し算のイメージを示します。
3.データaにデータbの値を加算する。
paddd mm0, mm1 ;データa+データbをmm0レジスタに格納
┌────────────────────────┐
mm0│ 24 22 20 18 16 14 12 10│
└────────────────────────┘
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
+ + + + + + + +
│ │ │ │ │ │ │ │
┌──┴──┴──┴──┴──┴──┴──┴──┴┐
mm1│ 17 16 15 14 13 12 11 10│
└────────────────────────┘
MMX技術により一気に8個ものデータが足し算されています。
たった1つの命令でこれだけの事を行ってくれるのです。
足し算が行なわれたので答えをメモリに格納してあげます。
これも先ほどと同じ「movq」命令を使用します。
4.レジスタからデータaへデータを移動する。
movq [edi], mm0 ;mm0レジスタをデータaに移動
mm0レジスタ メモリ(データa)
┌────────────────────────┐ ┌───┐
│ 24 22 20 18 16 14 12 10├────────→│ 10│
└──┬──┬──┬──┬──┬──┬──┬───┘ ┌──────→│ 12│
│ │ │ │ │ │ │ │┌─────→│ 14│
│ │ │ │ │ │ └─────┘│┌────→│ 16│
│ │ │ │ │ └─────────┘│┌───→│ 18│
│ │ │ │ └─────────────┘│┌──→│ 20│
│ │ │ └─────────────────┘│┌─→│ 22│
│ │ └─────────────────────┘│┌→│ 24│
│ └─────────────────────────┘│
└─────────────────────────────┘
これでデータbをデータaに加算した結果がメモリに格納されま
した。
これでこのプログラムの処理終了のように思うかもしれません
が、最後にちょっと特別な事を行う必要があります。
emms ;MMX後処理
「emms」という命令があります。
コメントには「MMX後処理」とあります。
これは何をしているのかと言うと、先ほど説明したようにMMX
で使用しているレジスタは実際には浮動小数点レジスタです。
通常、プログラムが動作している時はこのレジスタは「浮動小
数点レジスタ」として動作していますが、プロセッサはMMX命
令を見つけると、このレジスタを「MMXレジスタ」として解釈
するようになります。
つまり80ビットの大きさがある「浮動小数点」レジスタとし
てではなく、MMX命令用に動作する為に64ビットの大きさの
レジスタとして動作するようになるのです。
これをそのままにして放っておくと、MMX命令の動作が完了し
た後もずっとMMXレジスタとして動作してしまう事になり、本
当に「浮動小数点レジスタ」として動作させたい時に不具合が
起こる事になります。
その為、「emms」という命令を使用して「MMXの処理は終わり
ましたよ。」とプロセッサに教えてあげる必要があるのです。
「emms」という命令を見つけるとプロセッサは今まで「MMXレ
ジスタ」として動作していたのを、本来の「浮動小数点レジス
タ」として動作するようになります。
さて、プログラムを作り終わったらビルド&実行させてみまし
ょう。
● ●
● ビルドする ●
● ●
今まで書いてませんでしたが、コマンドプロンプトを起動した
後は必ず環境設定のバッチファイルを実行する必要があるので
忘れないようにしましょう。
「環境設定のバッチファイル」って何?という方はこちらをご
覧ください。
>>>
academy002-039.htm
env.bat
上記バッチファイル実行後、コマンドプロンプト上で以下の様
に入力してください。
sc mmx.c -j
これでビルドされ、"mmx.exe"という実行ファイルが作成され
るはずです。
● ●
● 実行する ●
● ●
ここで気を付けて欲しいのですが、このプログラムはMMXに対
応したプロセッサでないと動作しません。
(MMX-Pentium以降であればOKです。)
対応していないプロセッサで無理やり実行させてもプロセッサ
の保護機能が動作し、強制終了されますのでご注意ください。
それでは実行してみましょう。
コマンドプロンプト上で以下の様に入力してください。
mmx.exe
これでデータbをデータaに加算するプログラムが動作し、以下
の様に表示されたはずです。
実行結果:
a[0]:0 b[0]:10
a[1]:1 b[1]:11
a[2]:2 b[2]:12
a[3]:3 b[3]:13
a[4]:4 b[4]:14
a[5]:5 b[5]:15
a[6]:6 b[6]:16
a[7]:7 b[7]:17
sub routine called
a[0]:10 b[0]:10
a[1]:12 b[1]:11
a[2]:14 b[2]:12
a[3]:16 b[3]:13
a[4]:18 b[4]:14
a[5]:20 b[5]:15
a[6]:22 b[6]:16
a[7]:24 b[7]:17
以前作成したプログラムと実行結果が同じなのでつまらないか
もしれませんが、MMX命令を使用している事を考えると少しは
へー、と思いますよね?!
しかも実行速度は単純計算で8倍になってます。
(体感は出来ませんが・・・。)
さて、MMX技術について説明してきました。
インテルが開発したMMX(Multi Media eXtension)技術の様に、
1つの命令を使用する事により、結果として複数のデータが出
来上がる技術の事を、「SIMD(Single Instruction Multiple
Data)」といいます。
SIMDというのはインテルプロセッサの用語ではなく、プロセッ
サ全般に関する用語です。
このSIMD技術(=MMX技術)ですが、ペンティアム3、ペンティア
ム4と新しいプロセッサが出る度に発展していきました。
まずペンティアム3により新しくレジスタが追加になりました。
これは浮動小数点レジスタと共用しない、全く新しいレジスタ
で「XMM0〜XMM7」というレジスタです。
このレジスタは128ビットの大きさがあります。
非常に大きなレジスタなので32ビットのデータも複数同時に
計算したりする事が可能になっています。
インテルではペンティアム3でのこの仕様変更の事を、SIMD技
術(=MMX技術)を「拡張した物」、という意味のStreaming SIMD
Expansion(SSE)と名付けました。
ペンティアム4ではMMX用のレジスタが増えたりする事はあり
ませんが、64ビットの浮動小数点演算を同時に2つ行なう事
が出来るようになっており、更に複数のSIMD用命令が追加され
ています。
こちらの技術にはSSE2という名前が付けられました。
さて、MMX技術について複数回に渡り説明してきました。
MMXの説明が分量としては多くなってしまいましたが、「簡単
なアセンブラ言語講座」として説明しなければならない事は、
あとほんのちょっとだけになりました。
次回はプロセッサに「比較」を行なわせ、その後は少し「関数」
について見てみようと思っています。