在if中使用可能()/不可能()预处理器宏

如果我有:

#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)以确保处理器正在预测对齐与您正在生成的代码。

链接地址: http://www.djcxy.com/p/54875.html

上一篇: Using Likely() / Unlikely() Preprocessor Macros in if

下一篇: Is it possible to tell the branch predictor how likely it is to follow the branch?