プレディケーション(分岐の排除)によって、条件判断が必要な処理を効率良く実行できます。
AVX-512 命令のマスクレジスターを使用すると、プレディケーション(分岐の排除)によって、条件判断が必要な処理を効率良く実行できます。ここで紹介するものは、条件に合致するときのみ値の設定を行い、条件を満足しない場合は何も行いません。以降に、逐次処理で条件判断が必要な処理を実行する場合と、ベクトル命令でプレディケーションした例を図とコードで示します。
呼び出し側
16ビット整数型(signed)
符号付16ビット整数型配列の要素を、渡された値と比較します。条件を満足したら渡された値を設定し、そうでなければ何も行いません。比較対象の値と、設定する値は別々に渡されます。
以降に、呼び出し側のC++コードを示します。common.hをincludeしていますがSIMDで記述したアセンブリ言語の関数と等価な機能を持つC++言語で記述した共通関数のヘッダです。
それぞれ、lt=less than、le= less equal、eq=equal、ne=not equal、ge=grater qual、gt=grater thanです。
#include "..\common.h" // TEMPLATES // asmbler関数名はデータ型を持つ #define T short extern "C" { void cmpltw(T*, const T, const size_t, const T); void cmpeqw(T*, const T, const size_t, const T); void cmplew(T*, const T, const size_t, const T); void cmpnew(T*, const T, const size_t, const T); void cmpgew(T*, const T, const size_t, const T); void cmpgtw(T*, const T, const size_t, const T); } typedef void (*Dfunc)(T*, const T, const size_t, const T); extern "C" Dfunc afunc[] = { cmpeqw, cmpltw, cmplew, cmpnew, cmpgew, cmpgtw }; Dfunc cfunc[] = { ccmpeq, ccmplt, ccmple, ccmpne, ccmpge, ccmpgt }; // main, cmpValueと条件に従って比較し、 trueならvalueへ int main(void) { const size_t ArrLen = 4096; static_assert(ArrLen % 16 == 0, "number of elements must be an integral multiple of 16."); T a[ArrLen], c[ArrLen]; const T cmpValue = 8, value = 12; for (int i = 0; i < sizeof(cfunc) / sizeof(*cfunc);i++) { cout << "---[" << i << "]--- "; init(a, c, ArrLen); cfunc[i](c, cmpValue, ArrLen, value); afunc[i](a, cmpValue, ArrLen, value); verify(a, c, ArrLen); } return 0; }
16ビット整数型(unsigned)
符号なし16ビット整数型配列呼び出し側を示します。型以外は符号付と同じです。
#include "..\common.h" // TEMPLATES // asmbler関数名はデータ型を持つ #define T unsigned short extern "C" { void cmpltuw(T*, const T, const size_t, const T); void cmpequw(T*, const T, const size_t, const T); void cmpleuw(T*, const T, const size_t, const T); void cmpneuw(T*, const T, const size_t, const T); void cmpgeuw(T*, const T, const size_t, const T); void cmpgtuw(T*, const T, const size_t, const T); } typedef void (*Dfunc)(T*, const T, const size_t, const T); extern "C" Dfunc afunc[] = { cmpequw, cmpltuw, cmpleuw, cmpneuw, cmpgeuw, cmpgtuw }; Dfunc cfunc[] = { ccmpeq, ccmplt, ccmple, ccmpne, ccmpge, ccmpgt }; // main, cmpValueと条件に従って比較し、 trueならvalueへ int main(void) { const size_t ArrLen = 4096; static_assert(ArrLen % 16 == 0, "number of elements must be an integral multiple of 16."); T a[ArrLen], c[ArrLen]; const T cmpValue = 8, value = 12; for (int i = 0; i < sizeof(cfunc) / sizeof(*cfunc);i++) { cout << "---[" << i << "]--- "; init(a, c, ArrLen); cfunc[i](c, cmpValue, ArrLen, value); afunc[i](a, cmpValue, ArrLen, value); verify(a, c, ArrLen); } return 0; }
呼び出され側
アセンブラーのコードを示します。条件ごとに関数を記述するのは面倒なのでマクロを使って記述します。
;------------------------------------------------------------------- _MM_CMPINT_EQ EQU 0 ; - 等しい == _MM_CMPINT_LT EQU 1 ; - より小さい < _MM_CMPINT_LE EQU 2 ; - 以下 <= _MM_CMPINT_NE EQU 4 ; - 等しくない != _MM_CMPINT_GE EQU 5 ; - 以上 >= _MM_CMPINT_GT EQU 6 ; - より大きい > ;------------------------------------------------------------------- ; macro mymacro macro MNAME, MINST, CONDITION public MNAME align 16 MNAME proc vpbroadcastw zmm1, rdx ; zmm1 = cmpValue vpbroadcastw zmm2, r9 ; zmm2 = value xor rax, rax ; clear index loop_f: vmovdqu16 zmm0, zmmword ptr [rcx+rax*2] ; load r[] MINST k1, zmm0, zmm1, CONDITION vmovdqu16 zmmword ptr [rcx+rax*2]{k1}, zmm2; stote r[] add rax, 32 cmp rax, r8 jb short loop_f ;loop loop_f ret MNAME endp endm ;------------------------------------------------------------------- ; code _TEXT segment ;WORD mymacro cmpeqw, vpcmpw, _MM_CMPINT_EQ ; == mymacro cmplew, vpcmpw, _MM_CMPINT_LE ; <= mymacro cmpltw, vpcmpw, _MM_CMPINT_LT ; < mymacro cmpnew, vpcmpw, _MM_CMPINT_NE ; != mymacro cmpgew, vpcmpw, _MM_CMPINT_GE ; >= mymacro cmpgtw, vpcmpw, _MM_CMPINT_GT ; > ;WORD Unsigned mymacro cmpequw, vpcmpuw, _MM_CMPINT_EQ ; == mymacro cmpleuw, vpcmpuw, _MM_CMPINT_LE ; <= mymacro cmpltuw, vpcmpuw, _MM_CMPINT_LT ; < mymacro cmpneuw, vpcmpuw, _MM_CMPINT_NE ; != mymacro cmpgeuw, vpcmpuw, _MM_CMPINT_GE ; >= mymacro cmpgtuw, vpcmpuw, _MM_CMPINT_GT ; > _TEXT ends end
実行結果(signed)
C:\>ml64 /c chgByCondWAsm.asm
C:\>cl /O2 /EHsc chgByCondW.cpp chgByCondWAsm.obj
C:\>chgByCondW
----[0]---- Ok!
----[1]---- Ok!
----[2]---- Ok!
----[3]---- Ok!
----[4]---- Ok!
----[5]---- Ok!
実行結果(unsigned)
C:\>ml64 /c chgByCondWAsm.asm
C:\>cl /O2 /EHsc chgByCondUW.cpp chgByCondWAsm.obj
C:\>chgByCondUW
----[0]---- Ok!
----[1]---- Ok!
----[2]---- Ok!
----[3]---- Ok!
----[4]---- Ok!
----[5]---- Ok!
common.h、共通関数
arques.hatenablog.com
と同じです。