2012-12-15 27 views
12

的的CryENGINE SDK頭的這部分引起了我的注意:在CryENGINE 3中,分支掩膜如何工作?

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頭文件的副本,但沒有討論這個具體問題。

+0

與更好的分支預測我猜 – user1849534

+0

@didierc或者更確切地說,這些例子完全刪除分支。 – Mysticial

回答

12

這些函數返回可在其他計算中使用結果的位掩碼,以便無條件地執行操作,從而不引入分支。

例如:

  • nz2mask返回0如果參數0,和0xffffffff否則。
  • msb2mask返回0如果參數的最高位是00xffffffff如果它是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); 

而且,這些功能應內嵌並編譯成相對高效的彙編程序,交換一些額外的數學,不分支。

+1

upvoted。在第一個例子中,我們可以看到有一個測試('if'指令),它在機器代碼級被轉換爲條件分支。在替換指令中,沒有找到更多的測試,並且機器代碼將不包含分支。 – didierc

+0

感謝您的答案,我想我會替換我的,如果現在這個elses:D –

+0

在盲目做這種事情之前肯定地分析結果 - 它並不總是一個勝利。在試圖消除分支之前,你必須知道分支會使你的性能付出代價。 – JasonD