簡體   English   中英

如何制作無網段代碼?

[英]How can I make branchless code?

與此答案相關: https//stackoverflow.com/a/11227902/4714970

在上面的答案中,提到了如何通過避免分支來避免分支預測失敗。

用戶通過替換以下內容來演示:

if (data[c] >= 128)
{
    sum += data[c];
}

附:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

這兩個是如何等效的(對於特定的數據集,不是嚴格等同的)?

在類似的情況下,我可以采取哪些一般方法來做類似的事情? 它總是通過使用>>~

int t = (data[c] - 128) >> 31;

這里的技巧是,如果data[c] >= 128 ,那么data[c] - 128是非負的,否則它是負的。 當且僅當該數字為負時, int的最高位(符號位)為1。 >>是一個擴展符號位的移位,因此右移31會使整個結果為0(如果它曾經是非負的),而所有1位(代表-1)如果它曾經是負數。 因此,如果data[c] >= 128t0 ,否則為-1 ~t切換這些可能性,因此如果data[c] >= 128~t-1 ,否則為0

x & (-1)總是等於xx & 0總是等於0 因此,如果data[c] < 128 ,則sum += ~t & data[c]sum0 ,否則加上data[c]

其中許多技巧可以應用於其他地方。 當且僅當一個值大於或等於另一個值時,這個技巧當然可以應用於數字為0 ,否則為-1 ,你可以更多地使用它來獲得<=< ,等等上。 這樣的比特是一種使數學運算無分支的常用方法,盡管它肯定不會總是用相同的操作構建; ^ (xor)和| (或)有時也會發揮作用。

雖然Louis Wasserman的回答是正確的,但我想向您展示一種更通用(更清晰)的方法來編寫無分支代碼。 你可以用? : ? :運營商:

    int t = data[c];
    sum += (t >= 128 ? t : 0);

JIT編譯器從執行配置文件中看到這里的條件預測不佳。 在這種情況下,編譯器足夠聰明,可以用條件移動指令替換條件分支:

    mov    0x10(%r14,%rbp,4),%r9d  ; load R9d from array
    cmp    $0x80,%r9d              ; compare with 128
    cmovl  %r8d,%r9d               ; if less, move R8d (which is 0) to R9d

您可以驗證此版本對已排序和未排序的數組的運行速度同樣快。

無分支代碼通常意味着使用集合[0,1]中的權重來評估條件語句的所有可能結果,以便Sum {weight_i} = 1.大多數計算基本上被丟棄。 一些優化可以由以下事實導致:當對應的權重w_i (或掩碼m_i )為零時, E_i不必是正確的。

  result = (w_0 * E_0) + (w_1 * E_1) + ... + (w_n * E_n)    ;; or
  result = (m_0 & E_0) | (m_1 & E_1) | ... | (m_n * E_n)

其中m_i代表位掩碼。

通過水平折疊並行處理E_i也可以實現速度。

這與if (a) b; else c;的語義相矛盾if (a) b; else c; if (a) b; else c; 還是它的三元速記a ? b : c a ? b : c ,其中僅評估[b,c]中的一個表達式。

因此,三元運算對於無分支代碼來說不是神奇的子彈。 一個體面的編譯器同樣產生無分支代碼

t = data[n];
if (t >= 128) sum+=t;

    movl    -4(%rdi,%rdx), %ecx
    leal    (%rax,%rcx), %esi
    addl    $-128, %ecx
    cmovge  %esi, %eax

無分支代碼的變化包括通過其他無分支非線性函數(例如ABS)呈現問題(如果存在於目標機器中)。

例如

 2 * min(a,b) = a + b - ABS(a - b),
 2 * max(a,b) = a + b + ABS(a - b)

甚至:

 ABS(x) = sqrt(x*x)      ;; caveat -- this is "probably" not efficient

除了<<~ ,使用bool!bool代替(可能是未定義的)(int >> 31)可能同樣有益。 同樣,如果條件的計算結果為[0,1],則可以生成帶有否定的工作掩碼:

-[0, 1] = [0, 0xffffffff]  in 2's complement representation

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM