予測と分岐・設定・8ビット整数型

プレディケーション(分岐の排除)によって、条件判断が必要な処理を効率良く実行できます。
AVX-512 命令のマスクレジスターを使用すると、プレディケーション(分岐の排除)によって、条件判断が必要な処理を効率良く実行できます。ここで紹介するものは、条件に合致するときのみ値の設定を行い、条件を満足しない場合は何も行いません。以降に、逐次処理で条件判断が必要な処理を実行する場合と、ベクトル命令でプレディケーションした例を図とコードで示します。

呼び出し側

8ビット整数型(signed)

符号付8ビット整数型配列の要素を、渡された値と比較します。条件を満足したら渡された値を設定し、そうでなければ何も行いません。比較対象の値と、設定する値は別々に渡されます。
以降に、呼び出し側の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 char
extern "C"
{
    void cmpltb(T*, const T, const size_t, const T);
    void cmpeqb(T*, const T, const size_t, const T);
    void cmpleb(T*, const T, const size_t, const T);
    void cmpneb(T*, const T, const size_t, const T);
    void cmpgeb(T*, const T, const size_t, const T);
    void cmpgtb(T*, const T, const size_t, const T);
}
typedef void (*Dfunc)(T*, const T, const size_t, const T);

extern "C"
Dfunc afunc[] = { cmpeqb, cmpltb, cmpleb, cmpneb, cmpgeb, cmpgtb };
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;
}

8ビット整数型(unsigned)

符号なし8ビット整数型配列呼び出し側を示します。型以外は符号付と同じです。

#include "..\common.h"  // TEMPLATES

// asmbler関数名はデータ型を持つ
#define T unsigned char
extern "C"
{
    void cmpltub(T*, const T, const size_t, const T);
    void cmpequb(T*, const T, const size_t, const T);
    void cmpleub(T*, const T, const size_t, const T);
    void cmpneub(T*, const T, const size_t, const T);
    void cmpgeub(T*, const T, const size_t, const T);
    void cmpgtub(T*, const T, const size_t, const T);
}
typedef void (*Dfunc)(T*, const T, const size_t, const T);

extern "C"
Dfunc afunc[] = { cmpequb, cmpltub, cmpleub, cmpneub, cmpgeub, cmpgtub };
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
        vpbroadcastb zmm1, rdx                  ; zmm1 = cmpValue
        vpbroadcastb zmm2, r9                   ; zmm2 = value
        xor         rax, rax                    ; clear index
loop_f:
        vmovdqu8    zmm0, zmmword ptr [rcx+rax] ; load r[]
        MINST       k1, zmm0, zmm1, CONDITION
        vmovdqu8    zmmword ptr [rcx+rax]{k1}, zmm2; stote r[]

        add         rax, 64
        cmp         rax, r8
        jb          short loop_f

        ret
MNAME   endp

        endm


;-------------------------------------------------------------------
; code
_TEXT   segment

;BYTE
mymacro cmpeqb,  vpcmpb,  _MM_CMPINT_EQ  ; ==
mymacro cmpleb,  vpcmpb,  _MM_CMPINT_LE  ; <=
mymacro cmpltb,  vpcmpb,  _MM_CMPINT_LT  ; <
mymacro cmpneb,  vpcmpb,  _MM_CMPINT_NE  ; !=
mymacro cmpgeb,  vpcmpb,  _MM_CMPINT_GE  ; >=
mymacro cmpgtb,  vpcmpb,  _MM_CMPINT_GT  ; >

;BYTE Unsigned
mymacro cmpequb, vpcmpub, _MM_CMPINT_EQ  ; ==
mymacro cmpleub, vpcmpub, _MM_CMPINT_LE  ; <=
mymacro cmpltub, vpcmpub, _MM_CMPINT_LT  ; <
mymacro cmpneub, vpcmpub, _MM_CMPINT_NE  ; !=
mymacro cmpgeub, vpcmpub, _MM_CMPINT_GE  ; >=
mymacro cmpgtub, vpcmpub, _MM_CMPINT_GT  ; >

_TEXT   ends
        end


 実行結果(signed)

C:\>ml64 /c chgByCondBAsm.asm
C:\>cl /O2 /EHsc chgByCondB.cpp chgByCondBAsm.obj
C:\>chgByCondB
----[0]---- Ok!
----[1]---- Ok!
----[2]---- Ok!
----[3]---- Ok!
----[4]---- Ok!
----[5]---- Ok!


 実行結果(unsigned)

C:\>ml64 /c chgByCondBAsm.asm
C:\>cl /O2 /EHsc chgByCondUB.cpp chgByCondBAsm.obj
C:\>chgByCondUB
----[0]---- Ok!
----[1]---- Ok!
----[2]---- Ok!
----[3]---- Ok!
----[4]---- Ok!
----[5]---- Ok!

common.h、共通関数

arques.hatenablog.com
と同じです。