11

CryENGINE SDK ヘッダーのこの部分が私の注意を引きました。

branchmask.h

#ifndef __BRANCHLESS_MASK__
#define __BRANCHLESS_MASK__

///////////////////////////////////////////
// helper functions for branch elimination
//
// msb/lsb - most/less significant byte
//
// mask - 0xFFFFFFFF
// nz   - not zero
// zr   - is zero

ILINE const uint32 nz2msb(const uint32 x)
{
    return -(int32)x | x;
}

ILINE const uint32 msb2mask(const uint32 x)
{
    return (int32)(x) >> 31;
}

ILINE const uint32 nz2one(const uint32 x)
{
    return nz2msb(x) >> 31; // int((bool)x);
}

ILINE const uint32 nz2mask(const uint32 x)
{
    return (int32)msb2mask(nz2msb(x)); // -(int32)(bool)x;
}


ILINE const uint32 iselmask(const uint32 mask, uint32 x, const uint32 y)// select integer with mask (0xFFFFFFFF or 0x0 only!!!)
{
    return (x & mask) | (y & ~mask);
}


ILINE const uint32 mask_nz_nz(const uint32 x, const uint32 y)// mask if( x != 0 && y != 0)
{
    return msb2mask(nz2msb(x) & nz2msb(y));
}

ILINE const uint32 mask_nz_zr(const uint32 x, const uint32 y)// mask if( x != 0 && y == 0)
{
    return msb2mask(nz2msb(x) & ~nz2msb(y));
}


ILINE const uint32 mask_zr_zr(const uint32 x, const uint32 y)// mask if( x == 0 && y == 0)
{
    return ~nz2mask(x | y);
}

#endif//__BRANCHLESS_MASK__

誰かがこれらの関数がブランチを削減するためにどのように使用されることを意図しているのかを簡単に説明できますか? ILINE 事前定義された強制インラインまたはそのようなものだと思います。Google で検索しましたが、別のサイトにアップロードされた CryENGINE ヘッダーのコピーしか見つかりませんでしたが、この特定のサイトについての議論はありませんでした。

4

1 に答える 1

11

これらの関数は、条件なしで演算を実行するために、分岐を導入せずに演算を実行するために、他の計算の結果と論理積できるビットマスクを返します。

例えば:

  • nz2mask0引数が の場合は を返し00xffffffffそうでない場合は を返します。
  • msb2mask0引数の最上位ビットが である場合0、および で0xffffffffある場合は を返します1

したがって、次のようなコードがある場合 (参照用に x86 の手順を使用):

if(a != 0) x += y;
    //  test        ebx,ebx  
    //  je          skip  
    //  add         dword ptr [x],eax  
    // skip:

次のように置き換えることができます。

x += y & (nz2mask(a));
    //  mov     ecx,ebx  
    //  neg     ecx  
    //  or      ecx,ebx  
    //  sar     ecx,1Fh  
    //  and     ecx,eax  
    //  add     ecx,dword ptr [x]  

(少なくとも x86 では) より多くの命令が生成されますが、分岐は回避されます。

iselmask()次に、提供されたマスクに基づいていずれかの入力を選択できるような追加の関数があるため、次のように置き換えることができます。

x = (a != 0) ? r1 : r2;

x = iselmask(nz2mask(a), r1, r2);

繰り返しになりますが、これらの関数はインライン化して、比較的効率的なアセンブラーにコンパイルする必要があります。

于 2012-12-15T17:37:34.347 に答える