ホーム 道しるべ 憩いの広場 濃緑空間 濃緑研の日記

配列の扱い・最適化1
ホーム ] 上へ ] 逆数(除算) ] 逆数平方根と平方根 ] 処理時間を測ろう ] [ 配列の扱い・最適化1 ] 最適化2 ]

 

普通に時間測定
ループ展開・時間測定
ペアリング最適化I
ペアリング最適化II
x86FPUとの比較
3DNow!SISDと比較
配列演算総括

 いままでは、単一変数を扱ってきたため、Cのプログラムで宣言した変数をアセンブラの中で扱うのも比較的に楽だったのではないでしょうか。

 逆数や平方根は本来マイクロプログラムで行っている部分を、速度重視で15ビット精度での計算と分離できる代わりに24ビット精度にするための処理がプログラマの負担になっていましたが。

 さて3次元座標を扱うプログラムを書こうとすると、どうしても配列というものを扱わざるを得ません。

 まず、下のコードを見て下さい。
 最初の行ですが、今までEMMS命令が使われていないというwarningがでていたと思います。
 warningというか、VC++がMMX対応のみで、まだ3DNow!に対応していないためMMXレジスタを使っているのにemms命令が発行されていないという注意です。
 しかし、実際にはfemmsを発行していますので問題ありません。
 そこで、最初の行を追加することで、このwarningを出さないように指示しています。(余談でした)

 本題は、配列の処理の仕方です。
 今まではmainで計算処理を実行してきましたが、mainで宣言された配列をうまくmmxレジスタに渡せなかったため(コンパイルは通るのですが、実行時に例外エラーが発生してしまい、うまくいきませんでした。詳細が解り次第本ページにてお知らせします)、配列を引数にした計算用関数として別立てにしました。
 まあ、これはこれで有効な方法ですから。

#pragma warning( disable : 4799 ) // Disables EMMS warning for inline assembly

#include "amd3d.h"
#include <windows.h>
#include <math.h>
#include <stdio.h>

typedef struct vector {
    float    x,y;
} vector;

mtrx(float a[4], float b[4][4], float c[4])
{
    int        i;

    _asm{
        femms
        mov         edi,a             // edi = 配列a[0]のアドレス
        mov         ebx,b             // ebx = 配列b[0][0]のアドレス
        mov         ecx,c             // ecx = 配列c[0]のアドレス
    }
    for (i=0; i<4; i++) {
    _asm{
        movq    mm0,[edi]         // mm0:                 a[1]:             a[0]
        movq    mm1,[edi+8]         // mm1:                 a[3]:             a[2]
        movq    mm2,[ebx]         // mm2:             b[i]b[1]:         b[i]b[0]
        movq    mm3,[ebx+8]         // mm3:             b[i]b[3]:         b[i]b[2]
        pfmul    (m2,m0)             // mm2:     a[1]*b[i]b[1]: a[0]*b[i]b[0]
        pfmul    (m3,m1)             // mm3:     a[3]*b[i]b[3]: a[2]*b[i]b[2]
        pfadd    (m2,m3)             // mm2:
        pfacc    (m2,m2)             // mm2:     a[3]*b[i]b[3]+a[2]*b[i]b[2]+a[1]*b[i]b[1]+a[0]*b[i]b[0]
        movd    [ecx],mm2         // c[i] = mm2
        add         ebx,16             // ebx = 配列b[i+1][0]のアドレス
        add         ecx,4             // ecx = 配列c[i+1]のアドレス
        }
    }
    _asm{
        femms
    }
}

main()
{
    float    a[4]={0,1,1,0};
    float     b[4][4]={{1,0,0,0},{0,0,1,0},{0,-1,0,0},{0,0,0,1}};
    float    c[4];
    char    buf[80];
   
    sprintf(buf,"%f %f %f %f",a[0],a[1],a[2],a[3]);
    MessageBox(GetDesktopWindow(), buf,"Test 3DNow!",MB_OK);

    mtrx(a,b,c);

    sprintf(buf,"%f %f %f %f",c[0],c[1],c[2],c[3]);
    MessageBox(GetDesktopWindow(), buf,"Test 3DNow!",MB_OK);
}

 このロジックは4次元ベクトルa(実際には3次元座標+ダミー)に回転ベクトルを掛け座標を回転移動させるものです。
 上の例ではX軸を軸としてー90度回転させるものです。
 従って元の座標(0,1,1)が(0,1,-1)に変換されます。