HEXA BLOG

プログラム

HEXA BLOGプログラム2011.9.28

AVXを使ってみました!

こんにちは!
そろそろ職場の空気を掴みはじめてきた、新人のイワモーです晴れ
さて、前回のブログでは新しいPCを買った事をお話ししたと思いますが、
その中でAVXを勉強中という事を書きました。
今回は早速、そのAVXについて少しお話してみようかと思いますぴかぴか(新しい)
まず簡単に説明すると・・・
AVXとは、拡張SIMD命令セットの事で、従来のSSE命令では最大128bitまでの演算幅だったのが、
AVXでは256bitまで拡張されており、一回の命令で最大8つの数値を演算させる事が可能になったSIMD命令。
そして、SSEの拡張を重ねた結果、非常に煩雑になってしまったSIMD命令への解決へのアプローチ
という感じでしょうか。
厳密には更に説明が必要だったりしますが、詳細についてはAVXのリファレンス等を読んで頂ければと思います目
インテルAVXリファレンス(大き目のPDFデータなので、注意してください)
http://download.intel.com/jp/software/AVE/319433-006JA.pdf
では、実際にはどんな感じなのかexclamation&question
AVXを用いた簡単なコードの流れを少し書いてみましたぴかぴか(新しい)
今回扱うデータはこの様な形にしました。
ゲーム等ではよく使いそうな、よくあるVector4データです。

struct VERTEX_DATA
{
float x;
float y;
float z;
float w;
};
VERTEX_DATA *pDataBuffer0;	//データバッファ
VERTEX_DATA *pDataBuffer1;	//計算値のバッファ
static const int MAX_VERTEX = 10000000; //データの大きさ

まずAVXを使うにあたり注意する所は、
アラインメントを32byteで定義してメモリを確保しなければならない所です目
AVXでは、基本的に一つのデータにつき、32byteのアラインメントを必要としています。

pDataBuffer0 = (VERTEX_DATA*)_aligned_malloc( sizeof(VERTEX_DATA) * MAX_VERTEX, 32);
pDataBuffer1 = (VERTEX_DATA*)_aligned_malloc( sizeof(VERTEX_DATA) * MAX_VERTEX, 32);

普段_aligned_mallocはあまり使わないアロケート方法ですが、
第2引き数でアラインメント幅を入力する事が出来ます。
これでメモリを32byteアラインメントで確保しました手(チョキ)
もちろん、構造体の定義の方で__declspec(align(32))の様にアラインメントを定義してしまっても問題は無いと思います。
その場合は、普通のnew演算子でメモリをアロケートしても大丈夫でするんるん
次にデータの入力ですが、これは普通のプログラムと同じ様に代入できます。

pDataBuffer0[i].x = 100;
pDataBuffer0[i].y = 100;
pDataBuffer0[i].z = 100;
pDataBuffer0[i].w = 100;
pDataBuffer1[i].x = 100;
pDataBuffer1[i].y = 100;
pDataBuffer1[i].z = 100;
pDataBuffer1[i].w = 100;

データ入力が終わったら後はAVXのイントリンシック命令を使って計算させるだけですexclamation
__m256というAVX用のデータ型に値をロードし、計算させ、結果を元に戻す、という手順ですぴかぴか(新しい)

//MAX_VERTEXは、2の倍数と今回は仮定
//また_mm256_load_psはfloat8個分のデータ転送を行えるので、VERTEX_DATA2個分に相当する。
//よって、ループカウンタを2でインクリメント
__m256 srcIntrin;
__m256 destIntrin;
for(int i=0; i<MAX_VERTEX; i+=2)
{
    //値を__m256の変数にロードする
    srcIntrin  = _mm256_load_ps((float*)&pDataBuffer0[i]);
    destIntrin = _mm256_load_ps((float*)&pDataBuffer1[i]);
    //ロードが終わったら、計算命令にデータを入れ(この命令は掛け算)結果を受け取り
    srcIntrin  = _mm256_mul_ps(srcIntrin,destIntrin);
    //計算結果を元の場所に戻す
    _mm256_store_ps((float*)&pDataBuffer0[i],srcIntrin);
}

これで、無事にAVXを使って計算させる事に成功しましたexclamation
上の例では、_mm256_mul_psを利用し、掛け算を行っています。
最後に忘れてはならないのが、確保したメモリの解放ですふらふら
_aligned_mallocを使っている場合は_aligned_freeでメモリを解放してあげましょう。

_aligned_free(pDataBuffer0);
_aligned_free(pDataBuffer1);

もしも構造体を__declspec(align(32))で定義し、newで確保している場合は、
そのままdelete命令で解放してしまいましょうるんるん
以上が基本的な流れですパンチ
また、今回はコンパイルすれば動作するコードを .CPPデータとして添付ました。
main.cpp
※ ・CPPデータのコンパイルは、VisualStudio2010が必要となります
  ・データの実行については、OSのバージョンがWindows7SP1である必要があります。
  ・AVXに対応しているCPUが必要となります。
自分の環境では実際にAVXを用いたパターンと、通常の計算方法をリリースビルドで比べてみると、
割り算等のコストが高めの演算では6倍近い速度で計算を行わせる事ができました。
しかし、足し算や引き算等のコストが低めの計算では3割程早くなる程度でしたもうやだ〜(悲しい顔)
使いこなせれば動作速度を向上させるツールに十分なると思いますが、
アラインメントに気を遣うのでメンテナンスしずらい点、新しい命令が故のバグ、
対応環境の少なさ、効果を発揮できる場面の見極め等々、壁はとても大きいです。
それに加えて、AVXは従来のSSE命令が混在しているプログラムだと最大限のパフォーマンスが得られないという欠点もあります。バッド(下向き矢印)
ですが、実際に動作速度を向上出来た時の感動も大きいですexclamation×2
まだまだSIMD最適化に片足を突っ込んだばかりですが、有志で開いている勉強会等に積極的に参加していき、少しずつ理解を深めていきたいと思いますexclamation
それではまた手(パー)

RECRUIT

大阪・東京共にスタッフを募集しています。
特にキャリア採用のプログラマー・アーティストに興味がある方は下のボタンをクリックしてください

RECRUIT SITE