c/c++语言开发共享我可以在C中使用GCC的__builtin_expect()和三元运算符

GCC手册仅显示了__builtin_expect()放置在’if’语句的整个条件周围的示例。

我还注意到GCC不会抱怨如果我使用它,例如,使用三元运算符,或任何任意积分表达式,即使是未在分支上下文中使用的表达式。

所以,我想知道其实际使用的基本限制是什么。

当它在这样的三元运算中使用时是否会保持其效果:

int foo(int i) { return __builtin_expect(i == 7, 1) ? 100 : 200; } 

那么这个案子呢:

 int foo(int i) { return __builtin_expect(i, 7) == 7 ? 100 : 200; } 

还有这个:

 int foo(int i) { int j = __builtin_expect(i, 7); return j == 7 ? 100 : 200; } 

    它显然适用于三元和常规if语句。

    首先,我们来看看以下三个代码示例,其中两个代码在regular-if和ternary-if样式中使用__builtin_expect ,第三个代码样本根本不使用它。

    builtin.c:

     int main() { char c = getchar(); const char *printVal; if (__builtin_expect(c == 'c', 1)) { printVal = "Took expected branch!n"; } else { printVal = "Boo!n"; } printf(printVal); } 

    ternary.c:

     int main() { char c = getchar(); const char *printVal = __builtin_expect(c == 'c', 1) ? "Took expected branch!n" : "Boo!n"; printf(printVal); } 

    nobuiltin.c:

     int main() { char c = getchar(); const char *printVal; if (c == 'c') { printVal = "Took expected branch!n"; } else { printVal = "Boo!n"; } printf(printVal); } 

    使用-O3编译时,所有三个都导致相同的程序集。 但是,当-O被省略时(在GCC 4.7.2上),ternary.c和builtin.c都有相同的汇编列表(重要的地方):

    builtin.s:

      .file "builtin.c" .section .rodata .LC0: .string "Took expected branch!n" .LC1: .string "Boo!n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp call getchar movb %al, 27(%esp) cmpb $99, 27(%esp) sete %al movzbl %al, %eax testl %eax, %eax je .L2 movl $.LC0, 28(%esp) jmp .L3 .L2: movl $.LC1, 28(%esp) .L3: movl 28(%esp), %eax movl %eax, (%esp) call printf leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Debian 4.7.2-4) 4.7.2" .section .note.GNU-stack,"",@progbits 

    ternary.s:

      .file "ternary.c" .section .rodata .LC0: .string "Took expected branch!n" .LC1: .string "Boo!n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp call getchar movb %al, 31(%esp) cmpb $99, 31(%esp) sete %al movzbl %al, %eax testl %eax, %eax je .L2 movl $.LC0, %eax jmp .L3 .L2: movl $.LC1, %eax .L3: movl %eax, 24(%esp) movl 24(%esp), %eax movl %eax, (%esp) call printf leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Debian 4.7.2-4) 4.7.2" .section .note.GNU-stack,"",@progbits 

    而nobuiltin.c则不:

      .file "nobuiltin.c" .section .rodata .LC0: .string "Took expected branch!n" .LC1: .string "Boo!n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp call getchar movb %al, 27(%esp) cmpb $99, 27(%esp) jne .L2 movl $.LC0, 28(%esp) jmp .L3 .L2: movl $.LC1, 28(%esp) .L3: movl 28(%esp), %eax movl %eax, (%esp) call printf leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Debian 4.7.2-4) 4.7.2" .section .note.GNU-stack,"",@progbits 

    相关部分:

    我可以在C中使用GCC的__builtin_expect()和三元运算符

    基本上, sete %al导致额外的代码( sete %al …)在je .L2之前执行,基于testl %eax, %eax的结果testl %eax, %eax ,CPU更有可能预测为1(这里是天真的假设)而不是基于输入字符与'c'的直接比较。 在nobuiltin.c的情况下,没有这样的代码,并且je / jne直接跟随’c’( cmp $99 )的比较。 请记住,分支预测主要在CPU中完成,这里GCC只是为CPU分支预测器“设置一个陷阱”来假设将采用哪条路径(通过额外的代码和jejne的切换,尽管我没有有一个这方面的来源,因为英特尔的官方优化手册没有提到用分支预测来区分jejne第一次遭遇!我只能假设GCC团队通过反复试验来达到这个目的。

    我确信有更好的测试用例可以更直接地看到GCC的分支预测(而不是观察CPU的提示),尽管我不知道如何简洁/简洁地模拟这样的情况。 (猜猜:在编译过程中可能会涉及循环展开。)

      以上就是c/c++开发分享我可以在C中使用GCC的__builtin_expect()和三元运算符相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/562753.html

      (0)
      上一篇 2021年2月5日
      下一篇 2021年2月5日

      精彩推荐