分支掩码如何在CryENGINE 3中工作?

ula*_*ade 11 c++ optimization loops cryengine

这部分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__
Run Code Online (Sandbox Code Playgroud)

有人可以简单解释一下这些函数究竟用于减少分支吗?ILINE我想是预定义的内联力或类似的东西.我在Google上搜索过它,但我发现的只是在不同网站上传的CryENGINE标题的副本,但没有讨论这个特定的标题.

Jas*_*onD 11

这些函数返回可以在其他计算中得到结果的位掩码,以便在没有条件的情况下执行操作,从而不引入分支.

例如:

  • nz2mask0如果参数是0,0xffffffff则返回,否则返回.
  • msb2mask0如果参数的顶部位是0,0xffffffff则返回,如果是,则返回1.

所以,如果您有类似的代码(有x86指令供参考):

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

您可以将其替换为:

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

它产生更多指令(至少在x86上),但它避免了分支.

然后还有其他功能iselmask(),允许根据提供的掩码选择任一输入,因此您可以替换:

x = (a != 0) ? r1 : r2;
Run Code Online (Sandbox Code Playgroud)

x = iselmask(nz2mask(a), r1, r2);
Run Code Online (Sandbox Code Playgroud)

同样,这些函数应该内联并编译成相对有效的汇编程序,在没有分支的情况下交换一些额外的数学.