-- MMX Technology Reference --


1章.はじめに

2章.MMXテクノロジの検出

3章.MMXレジスタ・セット

4章.データタイプ

5章.パック/アンパック

6章.Wraparound Satulation

7章.SIMD

8章.MMX Instructions

9章.MMXテクノロジの使用/実験

10章.参考文献/資料

 


1章.はじめに

MMXテクノロジは、Intelが開発したx86 CPUの拡張機能の1つで、単一命令・複数データ(SIMD)技法を使用して、複数のデータ要素を並行して処理することにより、マルチメディアおよびコミュニケーション・ソフトウェアを高速化することが可能になります。

MMXテクノロジは、現在最新技術であるIntel-SSEKNI)やAMD-3DNow!テクノロジの基礎になっています。従って、MMXテクノロジを理解することはSSE3DNow!テクノロジを理解することにもつながり、今後登場するSIMDテクノロジに柔軟に対応することができるようになると思われます。

本書はそのMMXテクノロジを理解することを目的とした解説書です。


2章.MMXテクノロジの検出

MMXテクノロジは現在流通しているプロセッサにはほぼ全てに搭載されていますが、Intel-Pentium以前のプロセッサには搭載されていないのでMMX命令が使用可能か調べる必要があります。

MMXテクノロジを検出するにはCPUID命令を使用します。CPUID命令はCPUから、そのCPU固有の情報を得る命令です。しかし、そのためにはアセンブラコードを必要とします。では、具体的な方法をC言語(インラインアセンブラ)を用いて解説します。

C言語(インラインアセンブラ)例:

//**********************************************************************
// MMXテクノロジ検出関数
// BOOL IsMMXEnabled(void)
// 戻り値:
// TRUE (0以外)
MMXテクノロジが検出された
// FALSE 0)
  …
MMXテクノロジが検出されなかった//**********************************************************************

#define MMX_FLAG 0x00800000 //MMX検出ビットの定義
#define CPUID __asm _emit 0x0F __asm _emit 0xA2 //CPUID命令を定義

BOOL IsMMXEnabled(void)
{
    int flag=0;

_asm
{//=== インラインアセンブラ開始 ===========================

push eax //レジスタ退避
push ebx //レジスタ退避
push ecx //レジスタ退避
push edx //レジスタ退避

mov eax, 1 //レジスタeax 1を代入

CPUID //CPUID命令

mov flag, edx //検出用フラグ取得

pop edx //レジスタ復元
pop ecx //レジスタ復元
pop ebx //レジスタ復元
pop eax //レジスタ復元

}//=== インラインアセンブラ終了 ===========================

if( (flag & MMX_FLAG) == 0 )
{
    return FALSE;
}

    return TRUE;
}
//**********************************************************************

 

解説:

まず、レジスタeax 1を代入し、CPUID命令( 0FA2 )を実行します。すると自動的にedxにそのプロセッサに搭載されているテクノロジのフラグがセットされます。それを取得し、判定することでMMXテクノロジの検出が行えます。

これを少々改良すれば、現在の最新のテクノロジ、及び今後登場するテクノロジをも検出することが可能です。具体的には if((flag & XXX)==0) XXXを変えればよいだけです。Intel SSEKNI)ならば 0x02000000 AMD3DNow!ならば0x80000000です。これらの値は各メーカーの、それぞれのプロセッサのデータシートを調べれば見つかるはずです。

この関数でFALSEが帰ってきた場合はそのテクノロジが使用不可ということになります。


3章.MMXレジスタ・セット

 

MMXレジスタ FPUレジスタ:

MMX命令にはMMXレジスタが使用され、それはMM0 MM7 という 8つの新しい64 ビットレジスタです。しかし、実はFPUレジスタと共有されており、MMXモード/FPUモードを切り替えて使用しています。

MMXモード/FPUモードの切り替えは、FPU MMX への切り替えは自動ですが、 MMX FPUへの切り替えは命令を要します。

実際のプログラムでは、なるべくMMX命令とFPU命令を混在して使用しないようにし、MMXコードの終わりには「EMMS」という命令を実行してMMXレジスタをクリアするようにします。

 

モード切り替えペナルティ:

EMMS命令を使用してMMX命令とFPU命令を混在して使用することも可能ですが、モード切り替えのペナルティ(最大50クロック)が大きいため、できるだけこの切り替えを少なくすることが重要です。


4章.データタイプ

MMX命令は専用の新しいデータ型を使用します。それが「パックド整数値」とよび、下の図のように64ビットサイズで、1バイト整数×8個、2バイト整数×4個、4バイト整数×2個、8バイト整数×1個の4種類あります。また、この半分の32ビットサイズで、1バイト整数×4個、2バイト整数×2個、4バイト整数×1個を扱うこともできます。

パックド・バイト: 64 ビットにパックされた8 バイト

1バイト

1バイト

1バイト

1バイト

1バイト

1バイト

1バイト

1バイト


パックド・ワード:
64 ビットにパックされた4 ワード

2バイト

2バイト

2バイト

2バイト


パックド・ダブルワード:
64 ビットにパックされた2 ダブルワード

4バイト

4バイト


クワッドワード:
64 ビットクワッドワード

8バイト

 

実際にはこれらは変数として宣言したりすることはできません。char mem[8]; などとして普通にメモリに割り当てます。


5章.パック/アンパック

パックとはパックド・ワード パックド・バイト、パックド・ダブルワード パックド・ワードのようにパックされたそれぞれのデータをさらに上位ビットをカットして1つのデータサイズを小さくすることです。

アンパックとはパックの逆で、パックド・バイト パックド・ワード、パックド・ワード パックド・ダブルワードのようにパックされたそれぞれのデータを大きなサイズに変換することです。

簡単に言うとデータのキャストを複数同時に処理すると考えられます。

パックの例:

A

B

C

D

       

A

B

C

D

 

アンパックの例:

       

A

B

C

D

A

B

C

D

 


6章.Wraparound Satulation

Wraparound

通常、char などの変数には最大値があり、最大値を超えると最小値に戻ってしまいます。例えばchar型の変数に127(最大値)を代入し、さらに1を足すと ?128 になってしまいます。これをWraparoundといいます。これを防ぐ為には計算をうまくやるか、予想される最大値に収まる型を使用しなければなりません。また、最小値に対しても同様のことが起こります。

127 (0x7F)

+1

=

-128 (0x80)

 

Satulation

これに対し、Satulationは例えばchar型の変数に127(最大値)を代入し、さらに1を足してもその最大値を超えたり、Wraparoundせずに最大値を保持します。これは最小値に対しても同様の機能を果たします。SatulationMMX演算時の特殊な機能でこれを知っておくと便利なことがあります。

127 (0x7F)

+1

=

127 (0x7F)

 

実例:

実際には、例えばWindowsにおいて画像の1pixel1成分(RGBのうちの1つ)は0~255なのでこの範囲内の値を保持しておけばよいのですが、計算時にこれを超えることがあります。そのときに超えているかどうかの判定をしなければならないところがしなくてもよくなります。具体的にプログラムでは、以下のようなことが起こります。

//***************************************************

int r,g,b; // int unsigned char とすることができる

r = r + blightness;

if( r > 255 ){ r = 255; } // ここが不要になる

//***************************************************

MMXテクノロジではこのSatulationの機能を使用することができ、コードの簡略化および実行速度の高速化をすることができます。


7章.SIMD

SIMDとは:

SIMDSingle Instruction Multiple Data の略で、1つの命令で複数のデータを扱い、並列に処理することで高速化を図る技術です。例えば、パックド・バイトならば1度に8つのデータを加算・減算などして処理できることになります。

SIMDの例:

下のような8個の要素をもつ配列 A、B、C があったとします。

A1

A2

A3

A4

A5

A6

A7

A8

B1

B2

B3

B4

B5

B6

B7

B8

C1

C2

C3

C4

C5

C6

C7

C8

A、Bのそれぞれの要素を足して結果をCに代入したいとすると、

C1 A1 B1     C2 A2 B2

C3 A3 B3     C4 A4 B4

C5 A5 B5     C6 A6 B6

C7 A7 B7     C8 A8 B8

のように8回演算処理しなければなりません。

ところが、SIMDでは、

とするだけで同様の結果を得ることができます。

 

SIMDの長所:

・プロセッサでは 「メモリからレジスタへ移動」 「演算」 「メモリへ戻す」の作業の回数が少なくなるため、メモリアクセスタイミング等も考慮して非常に効率よくメモリアクセスができます。

・1度に複数処理をするのでループの回数を減らすことができます。ループの回数が減れば、ループ終了判定回数が減ることになり、そのぶん高速化します。

SIMDの短所:

・データ数に制限が起こります。N個同時に演算できるSIMDならば確保するデータのサイズはNの倍数である必要があり、処理できる個数もNの倍数となります。従って、無駄に多く処理してしまう場合や、少ない場合は残りのデータを処理するためにSIMDでない処理をすることになります。


8章.MMX Instructions

命令表記フォーマット・法則:

MMX命令表記はプリフィックス、命令、サフィックスからなっています。

・プリフィックス P (パックドの略)

・命令 … ADD MOVなど

・サフィックス Wraparound 符号あり・なしのSatulation データ型(
                    パックド・バイト /パックド・ワード パックド・ダブルワード/ クワッドワード
を指定する。

Wraparound 特に表記しない

符号ありSatulation S

符号なしSatulation US

パックド・バイト : B

パックド・ワード : W

パックド・ダブルワード : D

クワッドワード : Q

まとめると次のようになります。

[P][Instruction][S/US][B/W/D/Q]

 

表記例:

PADDSB

説明

P パックド
ADD
加算
S
符号ありSatulation
B
バイト型

▽この命令は「バイト型の符号ありSatulation パックド加算」であることが分かります。

 

この章での表の見方:

命令

B

W

D

Q

説明

MOVD

×

×

×

32ビットのデータをMMXレジスタに転送

 

命令

命令名

B/W/D/Q

B

… パックド・バイト

使用可

W

… パックド・ワード

×

使用不可

D

… パックド・ダブルワード

上位データ型へ変化

Q

… クワッドワード

下位データ型へ変化

 


Data-Transfar Instructins (データ転送命令)

データをMMXレジスタに転送する命令。

命令

B

W

D

Q

説明

MOVD

×

×

×

32ビットのデータをMMXレジスタに転送

MOVQ

×

×

×

64ビットのデータをMMXレジスタに転送

例:

movd mm0 mem132ビットデータをメモリからMMXレジスタへ)
movq mem2 mm1   (64ビットデータをMMXレジスタからメモリへ)
movq mm1 mm2  
  64ビットデータをMMXレジスタからMMXレジスタへ)


Add Sub Instructions (加算/減算命令)

命令

B

W

D

Q

説明

PADD

×

加算(Wraparound

PADDS

×

×

符号あり加算(Satulation

PADDUS

×

×

符号なし加算(Satulation

PSUB

×

減算(Wraparound

PSUBS

×

×

符号あり減算(Satulation

PSUBUS

×

×

符号なし減算(Satulation

例:

paddb mm1 mm2BYTE型加算(Wraparound)
paddsw mm2 mm3WORD型符号あり加算(Satulation))
psubusb mm4 mm5 BYTE型符号なし減算(Satulation)


Shift Instructions (シフト命令)

命令

B

W

D

Q

説明

PSLL

×

左シフト演算(つねに0で埋める)

PSRA

×

×

右シフト演算(最上位ビットで埋める)

PSRL

×

右シフト演算(つねに0で埋める)

例/詳細:

psllw mm1 3 (WORD型左シフト演算)

mm1

1100011000001010

1110100001011000

0101110010110000

0001100001011011

それぞれのWORDを左へ3つシフトする

mm1

0011000001010000

0100001011000000

1110010110000000

1100001011011000

 

psrad mm6 5 (WORD型右シフト演算)

mm6

11000110000010101110100001011000

01011100101100000001100001011011

それぞれのDWORDを右へ5つシフトし最上位ビットは元の最上位ビットで埋める

mm6

11111110001100000101011101000010

00000010111001011000000011000010


Logical Instructions (論理演算命令)

命令

B

W

D

Q

説明

PAND

ビットごとのAND演算

PANDN

ビットごとのNOTAND演算

POR

ビットごとのOR演算

PXOR

ビットごとの XOR演算

例:

pandn mm1 mm2
(mm1 のビットごとのNOT と mm2 のANDをとる mm1 = (~mm1)&mm2

pandqのようにqはつけない。(B/W/D/Qの区別が無い)


Multiply Instructions (掛算命令)

命令

B

W

D

Q

説明

PMADD

×

×

×

WORDの掛け算をDWORDに出力

PMULH

×

×

×

掛け算の結果の上位WORDを出力

PMULL

×

×

×

掛け算の結果の下位WORDを出力

例/詳細:

pmaddwd mm1 mm2

mm1

A1

A2

A3

A4

 

mm2

B1

B2

B3

B4

↓演算後

mm1

C1

C2

pmaddwdは次のような演算をする。

C1 A1 × B1 A2 × B2
C2
A3 × B3 A4 × B4


Compare Instructions (比較命令)

命令

B

W

D

Q

説明

PCMPEQ

×

比較命令(=かどうか)

PCMPGT

×

比較命令(>かどうか)

例/詳細:

PCMPEQW mm1 mm3

mm1

0070

1000

0400

0008

 

mm3

0070

2000

5400

0008

↓演算後

mm1

FFFF

0000

0000

FFFF

WORDのそれぞれを比較して等しいならばFFFFをセットし、それ以外は0にセットする。


Pack Unpack Instructions (パック/アンパック命令)

命令

B

W

D

Q

説明

PACKSS

×

×

符号ありSatulationパック

PACKUS

×

×

×

符号なしSatulationパック

PUNPCKH

×

上位32ビットアンパック

PUNPCKL

×

下位32ビットアンパック

例/詳細:

mm2 及びmm3を以下の様に代入したとする。

mm2

1111

2222

3333

4444

mm3

aaaa

bbbb

cccc

dddd

 

PACKSSWB mm2 mm3

演算後:

mm2

80

80

80

80

7F

7F

7F

7F

それぞれの値が127 (0x7f)よりも大きい場合は127に、-128(0x80)よりも小さい場合は-128Satulationされる。(下位4バイトにmm2の結果、上位4バイトにmm3の結果が合成されて出力される)

PACKUSWB mm2 mm3

演算後:

mm2

00

00

00

00

FF

FF

FF

FF

それぞれの値が255 (0xFF)よりも大きい場合は255に、0 (ここでWORDは符号ありで見られるため0x8000、つまり ?1 )よりも小さい場合は0Satulationされる。

PUNPCKHWD mm2 mm3

演算後:

mm2

aaaa1111

bbbb2222

mm2mm3のそれぞれの上位32ビットのうちのさらに上位16ビット/下位16ビットが結合されて出力される。

PUNPCKLWD mm2 mm3

演算後:

mm2

cccc3333

dddd4444

mm2mm3のそれぞれの下位32ビットのうちのさらに上位16ビット/下位16ビットが結合されて出力される。


EMMS Instruction EMMS命令)

命令

B

W

D

Q

説明

EMMS

MMXモード終了

MMXレジスタのMMX状態をクリアする。


9章.MMXテクノロジの使用/実験

MMXテクノロジの使用:

MMXテクノロジを使用するにはアセンブラ言語で記述する必要があります。VisualC++ならばインラインアセンブラで実現できます。ただし、VisualC++ Ver4.2以降でないと第8章で解説したようなMMX命令に対応していないので、_emitなどで直接機械語を入力することになります。

以下にインラインアセンブラでの記述例を示します。

void MMXalpha( void* pSrc1, void* pSrc2, void* pDst )
{
    _asm
     {
  
    mov eax, 7f7f7f7fh
      movd mm4, eax

punpckldq mm4, mm4

mov eax, pDst
mov esi, pSrc1
mov edi, pSrc2

movq mm0, [esi] //64ビット転送
movq mm1, [edi]

psrlq mm0, 1 //右に1シフト( 2で割る )
psrlq mm1, 1

pand mm0, mm4 //mm0 と mm4をAND演算
pand mm1, mm4

paddusb mm0, mm1 //mm0 mm1を加算

movq [eax], mm0 //演算結果を出力先へ転送

emms

    }

}

・このサンプルはpSrc1pSrc2のポインタで示すデータを64ビット(1バイト×8個のデータ)読み出し、それぞれ半分に割り、足した結果をpDstで示す先へ出力するものです。

これをC言語で書くと次のようになります。

unsigned int dst;

for( int n=0; n < 8; n++ )
{

dst = (*(pSrc1 + n) + *(pSrc2 + n)) /2;

if( dst < 255 ){ *(pDst + n) = (unsigned char)dst; }

else{ *(pDst + n) = 255; }

}

(注:引き数 pSrc1, pSrc2, pDstvoid* ではなく、unsigned char* とする必要がある)

 

MMX命令の使用の流れ:

@ movd または movq でmm0 からmm7 までのどれかのMMXレジスタにデータを転送する。

A @を繰り返し、必要なデータをそろえる。

B パック/アンパック命令で、データ型をそろえる。

B MMXレジスタに対し、MMX命令(paddusbpsrlwなど)を実行する。

C Bを繰り返し、計算したい演算を実行する。

D パック/アンパック命令で、データ型をそろえる。

E movd または movq で演算結果出力先に転送する。

F EMMS 命令でMMX状態をクリアする。

 

MMX命令の使用に関して注意すべきこと:

(1)EMMS命令の使用

MMX命令の使用の流れにおいて、Fは必ず必要です。Intelの“インテル・アーキテクチャ・ソフトウェア・ディベロッパーズ・マニュアル”によると、「FPU タグ・ワードがEMMS 命令によってリセットされる前に浮動小数点命令がFPU レジスタ・スタックのレジスタの1 つに値をロードした場合は、浮動小数点スタック・オーバフローが発生する可能性があり、その結果、浮動小数点例外が発生したり、誤った結果が生じることになる。」 とありますので注意する必要があります。もし、アプリケーションレベルで浮動小数点を使用しない場合においてもEMMS命令は使用しなくてはなりません。

(2)アセンブラ言語の理解

MMX命令はアセンブラ言語で記述するため、アセンブラ言語をある程度理解しておくことが必要です。

(3)その他

MMX命令はSIMDであることに特に注意し、MMX命令を使用するべき部分、そうでない部分を見極めることが大切です。また、データ型も特殊であるため、この点も特に注意しなければなりません。

 


MMXテクノロジの実験 − 画像処理:

MMX演算または通常演算で2つの画像をアルファブレンドするプログラムを作成し、実験をしてみた。


アルファブレンドとは?

ABの画像をある一定の割合(alpha)で足す合成処理を考えます。 alpha0.01.0 とし、このときの、Aの(XY)座標とBの(XY)座標の画素をA(X,Y)、B(X,Y)とすると合成後の画素 P(X,Y)は、

P(X,Y) = A(X,Y) × alpha B(X,Y)×(1.0alpha となります。

しかし、この計算は Red、Green、Blue、それぞれで行う必要があります。

P.R(X,Y) = A.R(X,Y) × alpha B.R(X,Y)×(1.0alpha
P.G(X,Y)
= A.G(X,Y) × alpha B.G(X,Y)×(1.0alpha
P.B(X,Y)
= A.B(X,Y) × alpha
B.B(X,Y)×(1.0−alpha)

MMX演算ではこのRGB3つ別々の処理を一つにまとめることができます。

 

<実験結果>

MMX演算(1)は4個同時、MMX演算(2)は8個同時のMMX演算を使用した。

画像サイズ
Pixel

通常演算(ms

MMX演算(1)(ms

MMX演算(2)(ms

高速化(1)

高速化(2)

2048×1536

612

212

200

287%

306%

1024×768

154

53

50.5

290%

305%

512×384

39

14.5

13

269%

300%

256×192

10

3

3

333%

333%

テスト環境: Intel  Pentium2 300MHz192MB MemMatrox - Millenium2

 

<結果考察>

・実験結果は表のとおり、通常演算よりもMMX演算の方が約3倍高速化した。(1)も(2)も、理論的には計算部分を4個(8個)同時に演算しているので4倍(8倍)高速になるはずですが、1つの演算にかかる実行時間の違いにより、理論どおりにならなかったと考えられます。通常演算を1とするとMMX演算(1)では約1.3MMX演算(2)では約2.7倍と考えることができます。

 


その他のSIMDテクノロジの使用(VisualC++):

VisualC++ MMXテクノロジ以外のSIMDテクノロジ(Intel - Internet Streaming SIMD Extensionや、AMD3DNow!テクノロジ/エンハンスド3DNow!テクノロジ等)には対応していません。しかし、AMDの開発サイトから開発キットをダウンロードし、この開発キットを組み込めば3DNow!テクノロジ及びエンハンスド3DNow!テクノロジを使用することができます(もちろんインラインアセンブラレベルですが)。 Intel SSEは現時点では対応の開発キットを見つけることができませんでした(MASM用のものしかありませんでした。)。

 


10章.参考文献/資料

 

・インテル・アーキテクチャ・ソフトウェア・ディベロッパーズ・マニュアル(上巻: 基本アーキテクチャ)

→インテルのサイトからダウンロードすることができます。

(アドレス: http://www.intel.co.jp/jp/developer/design/pentiumiii/manuals/index.htm

 

・インテル・アーキテクチャ MMXR テクノロジプログラマーズ・リファレンス・マニュアル

(→以前にインテルのサイトからダウンロードした。)

 


-- MMX Technology Reference Ver 1.01 --   Copyright (C) 1999-2002 by Syn-K.