例题描述

例题描述:通过C语言识别一个int型数据在十进制下是否为回文数字。不能有额外的字符串空间开销。
如:2156512是回文数,而21565不是回文数。

问题分析:
1. 当这个数字是负数的时候,肯定不是回文数
2. 可以将这个数翻转,判断翻转后是否相同

C语言代码演示:

/*************************************************************************> File Name: isPalindrome.cpp> Author: ChenXiansen > Mail: 1494089474@qq.com > Created Time: Wed 11 Nov 2020 09:49:43 AM CST************************************************************************/#include <stdio.h>bool isPalindrome(int x, int n) {if (__builtin_expect(!!(x < 0), 0)) return false;int y = 0, z = x;while (x) {y = y * n + x % n;x /= n;}return z == y;
}int main() {int n, Cov = 10;scanf("%d", &n);if (isPalindrome(n, Cov)) {printf("Num: %d in Cov: %d is a reverse num!\n", n, Cov);} else {printf("NO! It's not a reverse num!\n");}return 0;
}

代码分析

isPalindrome函数中,使用了if (__builtin_expect(!!(x < 0), 0)) return false; 这条语句。
下面是对__builtin_expect宏的表述:

GCC提供了__builtin_expect宏,作为编译分支时候的暗示。用法是__builtin_expect(var, expected_value),也就是说,告诉编译器var这个变量的值比较可能是什么。在kernel中这个宏被用在likely和unlikely这两个宏定义中:

#define likely(x) __builtin_expect(!!(x), 1)       //x很可能成立
#define unlikely(x) __builtin_expect(!!(x), 0)      //x很可能不成立

从现代的处理器架构说起。相信大家都知道流水线技术,就是CPU可以在统一个时钟周期内同时执行多条指令,当前指令尚未执行完毕,实际上就已经开始处理后面的指令了。然而当处理器遇到分支的时候,就无法判断即将执行的是哪个分支,流水线优化就受到了限制。

后来,随着处理器技术的发展,处理器开始直接预取分支后面的指令,如果发现分支预判错误,则抛弃之前的执行结果,重新转入正确的分支继续执行。 更加现代的处理器甚至能够预取更多后面的指令,对于不依赖之前执行结果的指令都可以按照一定的规则预先执行得到结果。


汇编代码

1. 初始代码汇编

接下来在终端运行gcc -S isPalindrome.cpp,查看汇编代码中的isPalindrome()函数:

.LFB0:.cfi_startprocendbr64pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6movl   %edi, -20(%rbp)movl %esi, -24(%rbp)movl -20(%rbp), %eaxshrl $31, %eaxmovzbl %al, %eaxtestq  %rax, %raxje    .L2movl $0, %eaxjmp .L3
.L2:movl    $0, -8(%rbp)movl    -20(%rbp), %eaxmovl %eax, -4(%rbp)
.L5:cmpl    $0, -20(%rbp)je .L4movl -8(%rbp), %eaximull -24(%rbp), %eaxmovl %eax, %ecxmovl  -20(%rbp), %eaxcltdidivl    -24(%rbp)movl   %edx, %eaxaddl  %ecx, %eaxmovl  %eax, -8(%rbp)movl  -20(%rbp), %eaxcltdidivl    -24(%rbp)movl   %eax, -20(%rbp)jmp  .L5
.L4:movl    -4(%rbp), %eaxcmpl  -8(%rbp), %eaxsete  %al
.L3:popq    %rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE0:.size _Z12isPalindromeii, .-_Z12isPalindromeii.section    .rodata

对.LFB0段的释义:

  1. main()函数中的变量n的值存入了edi寄存器,作为bool isPalindrome(int x, int n)的形参x。
  2. main()函数中的变量Cov的值存入esi寄存器,作为bool isPalindrome(int x, int n)的形参n。
  3. movl %edi, -20(%rbp)
    movl %esi, -24(%rbp)
    将这两个参数压入函数栈。
  4. movl -20(%rbp), %eax :将存在于-20(%rbp)中x的值,存入eax寄存器。
  5. 执行以下汇编指令,这段汇编指令由if (__builtin_expect(!!(x < 0), 0)) return false;转换得到:
       shrl    $31, %eaxmovzbl %al, %eaxtestq  %rax, %raxje    .L2movl $0, %eaxjmp .L3
    

    可以看到:在执行前三行语句后(这三行代码现在没有弄清楚具体含义,待后续查阅相关资料),下一步是判断分支:汇编代码是je .L2

    1. 如果满足testq %rax, %rax条件,下一步是执行C程序中的:

      int y = 0, z = x;
      

      对应在汇编.L2函数中的:

         .L2:movl    $0, -8(%rbp)movl    -20(%rbp), %eaxmovl %eax, -4(%rbp)
      
    2. 如果不满足条件,将返回值0放入寄存器eax中,执行.L3 汇编函数部分的return操作。
修改程序后汇编:

现在将

if (__builtin_expect(!!(x < 0), 0)) return false;

这部分C代码转换成

if (x < 0) return false;

观察执行预处理-编译-汇编过程后,查看汇编代码中的isPalindrome()函数。(由于.L2之后的部分完全相同,因此没有在博客中显示)

.LFB0:.cfi_startprocendbr64pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6movl   %edi, -20(%rbp)movl %esi, -24(%rbp)cmpl $0, -20(%rbp)jns    .L2movl $0, %eaxjmp .L3

可以看到:将函数参数全部压栈后,进行的分支判断:

 movl    %edi, -20(%rbp)movl %esi, -24(%rbp)cmpl $0, -20(%rbp)jns    .L2movl $0, %eaxjmp .L3

cmpl $0, -20(%rbp):如果 bool isPalindrome(int x, int n)函数中 参数x的值不满足 x < 0,则执行.L2之后的汇编函数,反之直接return 0.


参考资料:
https://zhuanlan.zhihu.com/p/27339191
http://deltamaster.is-programmer.com/posts/37285.html

附录:完整汇编程序代码:

 .file   "isPalindrome.cpp".text.globl _Z12isPalindromeii.type _Z12isPalindromeii, @function
_Z12isPalindromeii:
.LFB0:.cfi_startprocendbr64pushq    %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6movl   %edi, -20(%rbp)movl %esi, -24(%rbp)movl -20(%rbp), %eaxshrl $31, %eaxmovzbl %al, %eaxtestq  %rax, %raxje    .L2movl $0, %eaxjmp .L3
.L2:movl    $0, -8(%rbp)movl    -20(%rbp), %eaxmovl %eax, -4(%rbp)
.L5:cmpl    $0, -20(%rbp)je .L4movl -8(%rbp), %eaximull -24(%rbp), %eaxmovl %eax, %ecxmovl  -20(%rbp), %eaxcltdidivl    -24(%rbp)movl   %edx, %eaxaddl  %ecx, %eaxmovl  %eax, -8(%rbp)movl  -20(%rbp), %eaxcltdidivl    -24(%rbp)movl   %eax, -20(%rbp)jmp  .L5
.L4:movl    -4(%rbp), %eaxcmpl  -8(%rbp), %eaxsete  %al
.L3:popq    %rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE0:.size _Z12isPalindromeii, .-_Z12isPalindromeii.section    .rodata
.LC0:.string    "%d".align 8
.LC1:.string    "Num: %d in Cov: %d is a reverse num!\n"
.LC2:.string    "NO! It's not a reverse num!".text.globl main.type   main, @function
main:
.LFB1:.cfi_startprocendbr64pushq    %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq   $16, %rspmovq   %fs:40, %raxmovq    %rax, -8(%rbp)xorl  %eax, %eaxmovl  $10, -12(%rbp)leaq  -16(%rbp), %raxmovq %rax, %rsileaq  .LC0(%rip), %rdimovl    $0, %eaxcall    __isoc99_scanf@PLTmovl -16(%rbp), %eaxmovl -12(%rbp), %edxmovl %edx, %esimovl  %eax, %edicall  _Z12isPalindromeiitestb %al, %alje  .L7movl -16(%rbp), %eaxmovl -12(%rbp), %edxmovl %eax, %esileaq  .LC1(%rip), %rdimovl    $0, %eaxcall    printf@PLTjmp  .L8
.L7:leaq    .LC2(%rip), %rdicall    puts@PLT
.L8:movl    $0, %eaxmovq    -8(%rbp), %rcxxorq  %fs:40, %rcxje  .L10call    __stack_chk_fail@PLT
.L10:leave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1:.size main, .-main.ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0".section   .note.GNU-stack,"",@progbits.section .note.gnu.property,"a".align 8.long    1f - 0f.long    4f - 1f.long    5
0:.string    "GNU"
1:.align 8.long  0xc0000002.long     3f - 2f
2:.long  0x3
3:.align 8
4:

GCC __builtin_expect与kernel指令序列优化相关推荐

  1. SATA系列专题之六:浅析NCQ原生指令序列

    一.故事前传 在之前的文章中, 我们已经针对SATA相关内容进行了较为详细的解析,如果感兴趣的话,请见之前文章: 1,浅析SATA Physical Layer物理层OOB信号: 2,SATA Lin ...

  2. 【微机原理与接口技术学习实践】汇编语言程序设计初步——debug编写调试指令序列

    halo~我是bay_Tong桐小白 本文内容是桐小白个人对所学知识进行的总结和分享,知识点会不定期进行编辑更新和完善,了解最近更新内容可参看更新日志,欢迎各位大神留言.指点 [微机原理与接口技术学习 ...

  3. gcc -O0 -O1 -O2 -O3 四级优化选项及每级分别做什么优化【转】

    转自:http://blog.csdn.net/qinrenzhi/article/details/78334677 相关博客http://blog.chinaunix.net/uid-2495495 ...

  4. gcc -O0 -O1 -O2 -O3 四级优化选项及每级分别做什么优化

    相关博客http://blog.chinaunix.net/uid-24954950-id-2956476.html 相关博客http://blog.csdn.net/misiter/article/ ...

  5. GCC源码分析(十三) — 机器描述文件

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  6. gcc 优化选项 -O1 -O2 -O3 -Os 优先级

    Gcc 编译优化简介 gcc 提供了为了满足用户不同程度的的优化需要,提供了近百种优化选项,用来对{编译时间,目标文件长度,执行效率}这个三维模型进行不同的取舍和平衡.优化的方法不一而足,总体上将有以 ...

  7. GCC中SIMD指令的应用方法

    X86架构上的多媒体应用开发,如果能够使用SIMD指令进行优化, 性能将大大提高.目前,IA-32的SIMD指令包括MMX,SSE,SSE2等几级. 在GCC的开发环境中,有几种使用SIMD指令的方式 ...

  8. 如果我针对大小而不是速度进行优化,为什么GCC会生成15-20%的更快代码?

    本文翻译自:Why does GCC generate 15-20% faster code if I optimize for size instead of speed? I first noti ...

  9. GCC源码分析(十四) — rtx结构体,指令与栈分配

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

最新文章

  1. 挺好用的SQLSERVER数据库自动备份工具SQLBackupAndFTP(功能全面)
  2. 前端性能优化-减少HTTP请求数
  3. wifi管理系统_如何有效选择一款移动考勤管理系统
  4. 韩国ETRI提出实时Anchor-Free实例分割算法CenterMask,代码将开源
  5. open() api
  6. 风之大陆电脑版要下载c语言,风之大陆电脑版
  7. Java 线程池的复用原理
  8. 从网络上下载文件到本地
  9. Maven下载及安装配置
  10. 视频教程-IP地址划分(CCNA魔鬼训练营系列)-思科认证
  11. 同花顺 行情服务器系统,[分享]完美运行同花顺,Linux下的股友有福了
  12. w ndows10备份,Win10备份工具哪个最好?轻松备份会让你知道
  13. PDF文件怎么加密?这两种加密方法很靠谱
  14. selenium学习指南
  15. 主引导记录、启动扇区
  16. worldmap matlab,[转载]转:matlab画地图的工具:worldmap和m_map
  17. 解决CUDA error: no kernel image is available for execution on the device
  18. OPenCV 图像透视变换矫正
  19. 安全面试之基础总结篇【超详细!】
  20. VERI-ZEXE: Decentralized Private Computation with Universal Setup

热门文章

  1. [转]树结构表递归查询在ORACLE和MSSQL中的实现方法
  2. [LeetCode] Isomorphic Strings - 字符串操作:数组计数字符个数问题
  3. 2015年第六届蓝桥杯 - 省赛 - C/C++大学B组 - F. 加法变乘法
  4. TensorFlow错误:TypeError: __init__() got an unexpected keyword argument 'serialized_options'
  5. 【ARM】Tiny4412裸板编程之静态库(libc.a)
  6. 【Linux】一步一步学Linux——startx命令(199)
  7. config done shell_shell do...done $config
  8. echat 图表动态数据生成,渲染,自定义Y轴坐标值
  9. 每天一道LeetCode-----给定二维数组代表海域和岛屿,计算有多少个孤岛
  10. 2020-09-11