簡體   English   中英

在if-else if鏈中使用Likely()/不可思議()預處理器宏

[英]Using Likely() / Unlikely() Preprocessor Macros in if-else if chain

如果我有:

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

if (A)
    return true;
else if (B)
    return false;
...
else if (Z)
    return true;
else
    //this will never really happen!!!!
    raiseError();
    return false;

else if (likely(Z))表示最終語句(else)非常不可能沒有編譯器影響先前檢查的分支預測,我可以將最近的條件檢查else if (likely(Z))放在最后一個條件周圍嗎?

基本上,如果存在帶分支預測器提示的單個條件語句,GCC是否會嘗試優化整個if-else if塊?

你應該明確這樣做:

if (A)
  return true;
else if (B)
  return true;
...  
else if (Y)
  return true;
else {
  if (likely(Z))
    return true;

  raiseError();
  return false;
}

現在編譯器清楚地了解您的意圖,並且不會重新分配其他分支概率。 代碼的可讀性也增加了。

PS我建議你重寫Linux內核以防止靜默整體轉換的方式也可能和不太可能:

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)

一般來說,GCC假設if語句中的條件是真的 - 有例外,但它們是上下文的。

extern int s(int);

int f(int i) {
  if (i == 0)
    return 1;
  return s(i);
}

產生

f(int):
        testl   %edi, %edi
        jne     .L4
        movl    $1, %eax
        ret
.L4:
        jmp     s(int)

extern int t(int*);
int g(int* ip) {
  if (!ip)
    return 0;
  return t(ip);
}

生產:

g(int*):
        testq   %rdi, %rdi
        je      .L6
        jmp     t(int*)
.L6:
        xorl    %eax, %eax
        ret

(見godbolt

注意f的分支是jne (假設條件為真),而在g中假定條件為假。

現在與以下內容進行比較:

extern int s(int);
extern int t(int*);

int x(int i, int* ip) {
  if (!ip)
    return 1;
  if (!i)
    return 2;
  if (s(i))
    return 3;
  if (t(ip))
    return 4;
  return s(t(ip));
}

哪個產生

x(int, int*):
        testq   %rsi, %rsi
        je      .L3         # first branch: assumed unlikely
        movl    $2, %eax
        testl   %edi, %edi
        jne     .L12        # second branch: assumed likely
        ret
.L12:
        pushq   %rbx
        movq    %rsi, %rbx
        call    s(int)
        movl    %eax, %edx
        movl    $3, %eax
        testl   %edx, %edx
        je      .L13       # third branch: assumed likely
.L2:
        popq    %rbx
        ret
.L3:
        movl    $1, %eax
        ret
.L13:
        movq    %rbx, %rdi
        call    t(int*)
        movl    %eax, %edx
        movl    $4, %eax
        testl   %edx, %edx
        jne     .L2       # fourth branch: assumed unlikely!
        movq    %rbx, %rdi
        call    t(int*)
        popq    %rbx
        movl    %eax, %edi
        jmp     s(int)

在這里我們看到一個上下文因素:GCC發現它可以在這里重用L2 ,所以它決定不太可能認為最終的條件,以便它可以發出更少的代碼。

讓我們看一下你給出的例子的匯編:

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

extern void raiseError();

int f(int A, int B, int Z)
{
  if (A)
    return 1;
  else if (B)
    return 2;
  else if (Z)
    return 3;

  raiseError();
  return -1;
}

程序集看起來像這樣

f(int, int, int):
        movl    $1, %eax
        testl   %edi, %edi
        jne     .L9
        movl    $2, %eax
        testl   %esi, %esi
        je      .L11
.L9:
        ret
.L11:
        testl   %edx, %edx
        je      .L12       # branch if !Z
        movl    $3, %eax
        ret
.L12:
        subq    $8, %rsp
        call    raiseError()
        movl    $-1, %eax
        addq    $8, %rsp
        ret

請注意,生成的代碼在!Z為真時分支,它的行為就像Z可能一樣。 如果我們告訴Z可能會發生什么?

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

extern void raiseError();

int f(int A, int B, int Z)
{
  if (A)
    return 1;
  else if (B)
    return 2;
  else if (likely(Z))
    return 3;

  raiseError();
  return -1;
}

現在我們得到了

f(int, int, int):
        movl    $1, %eax
        testl   %edi, %edi
        jne     .L9
        movl    $2, %eax
        testl   %esi, %esi
        je      .L11
.L9:
        ret
.L11:
        movl    $3, %eax    # assume Z
        testl   %edx, %edx
        jne     .L9         # but branch if Z
        subq    $8, %rsp
        call    raiseError()
        movl    $-1, %eax
        addq    $8, %rsp
        ret

這里的要點是,在使用這些宏時要謹慎,並仔細檢查前后的代碼,以確保獲得預期的結果,並進行基准測試(例如使用perf)以確保處理器正在進行對齊的預測使用您生成的代碼。

暫無
暫無

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

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