GCC编译优化选项及汇编优化实例

2023-05-16

文章目录

  • 1 优化级别
    • -O1
    • -O2
    • -O3
    • -O0
    • -Os
    • -Ofast
    • -Og
    • -Oz
    • -On (n>=4)?
  • 2 汇编优化实例

最近在做LVGL的GUI,移植官方的SDK,然后CPU占用率竟然达到了99%,这就让整个GUI界面的反应十分缓慢。然后我开启了编译器的优化 -O3,CPU的使用率降低到了50%,今天来简单介绍一下GCC的编译优化级别。

编译器优化级别由命令行选项-On控制,其中n是所需的优化级别。打开优化标志会使编译器提高性能和优化代码大小,但会牺牲编译时间和部分语句的Debug功能。

1 优化级别

-O1

对一些大型函数,优化编译时间和程序大小。-O1等价于使用下面几个编译器flags:

-fauto-inc-dec
-fbranch-count-reg
-fcombine-stack-adjustments
-fcompare-elim
-fcprop-registers
-fdce
-fdefer-pop
-fdelayed-branch
-fdse
-fforward-propagate
-fguess-branch-probability
-fif-conversion
-fif-conversion2
-finline-functions-called-once
-fipa-modref
-fipa-profile
-fipa-pure-const
-fipa-reference
-fipa-reference-addressable
-fmerge-constants
-fmove-loop-invariants
-fmove-loop-stores
-fomit-frame-pointer
-freorder-blocks
-fshrink-wrap
-fshrink-wrap-separate
-fsplit-wide-types
-fssa-backprop
-fssa-phiopt
-ftree-bit-ccp
-ftree-ccp
-ftree-ch
-ftree-coalesce-vars
-ftree-copy-prop
-ftree-dce
-ftree-dominator-opts
-ftree-dse
-ftree-forwprop
-ftree-fre
-ftree-phiprop
-ftree-pta
-ftree-scev-cprop
-ftree-sink
-ftree-slsr
-ftree-sra
-ftree-ter
-funit-at-a-time

-O2

编译器执行的优化不需要为了速度而牺牲空间。与-O1相比,-O2优化提高了输出二进制文件的性能,但也增加了编译时间。除了-O1打开的编译器flag外,它还会打开

-falign-functions  -falign-jumps
-falign-labels  -falign-loops
-fcaller-saves
-fcode-hoisting
-fcrossjumping
-fcse-follow-jumps  -fcse-skip-blocks
-fdelete-null-pointer-checks
-fdevirtualize  -fdevirtualize-speculatively
-fexpensive-optimizations
-ffinite-loops
-fgcse  -fgcse-lm
-fhoist-adjacent-loads
-finline-functions
-finline-small-functions
-findirect-inlining
-fipa-bit-cp  -fipa-cp  -fipa-icf
-fipa-ra  -fipa-sra  -fipa-vrp
-fisolate-erroneous-paths-dereference
-flra-remat
-foptimize-sibling-calls
-foptimize-strlen
-fpartial-inlining
-fpeephole2
-freorder-blocks-algorithm=stc
-freorder-blocks-and-partition  -freorder-functions
-frerun-cse-after-loop
-fschedule-insns  -fschedule-insns2
-fsched-interblock  -fsched-spec
-fstore-merging
-fstrict-aliasing
-fthread-jumps
-ftree-builtin-call-dce
-ftree-loop-vectorize
-ftree-pre
-ftree-slp-vectorize
-ftree-switch-conversion  -ftree-tail-merge
-ftree-vrp
-fvect-cost-model=very-cheap

-O3

-O2的基础上,还需要在空间和速度上进行权衡,它还会打开:

-fgcse-after-reload
-fipa-cp-clone
-floop-interchange
-floop-unroll-and-jam
-fpeel-loops
-fpredictive-commoning
-fsplit-loops
-fsplit-paths
-ftree-loop-distribution
-ftree-partial-pre
-funswitch-loops
-fvect-cost-model=dynamic
-fversion-loops-for-strides

-O0

默认值,它将减少编译时间,调试将产生预期的结果。启用了部分flags,如-falign-loops-finline-functions-called-once,和-fmove-loop-invariants

-Os

减少二进制文件的大小,而不是执行速度。-Os启用所有-O2中的flags,但除去了以下会增加代码大小的flags:

-falign-functions  -falign-jumps
-falign-labels  -falign-loops
-fprefetch-loop-arrays  -freorder-blocks-algorithm=stc

-Ofast

无视严格的标准遵守。-Ofast启用所有-O3优化。它还支持对所有符合标准的程序无效的优化。除非指定了-fmax-stack-var-size-fno-protect-parens,否则它会开启-fast-math -flow -store-data-races和Fortran专用的-fstack-arrays。它关闭了-fsemantics -interposition

-Og

优化调试体验,它只是禁用可能干扰调试的优化。-Og提供合理的优化,同时保证良好的调试体验。对于生成可调试代码来说,它是比-O0更好的选择,因为一些收集调试信息的编译器通道在-O0处被禁用。-Og-O1的基础上还启用了以下几个编译器flags:

-fbranch-count-reg  -fdelayed-branch
-fdse  -fif-conversion  -fif-conversion2
-finline-functions-called-once
-fmove-loop-invariants  -fmove-loop-stores  -fssa-phiopt
-ftree-bit-ccp  -ftree-dse  -ftree-pta  -ftree-sra

-Oz

在GCC 12.1中引入,在-Os的基础上进一步优化程序大小。注意,相比-O2,它会严重降低运行时性能,因为如果这些指令需要更少的字节来编码,则会增加执行的指令数量。-Oz的行为类似于-Os,包括启用大多数-O2中的优化。

-On (n>=4)?

-O级别高于3没有任何效果。编译器可能会接受像-O4这样的cflag,但实际上它不会对它们做任何事情,它只对-O3进行优化。

  • 参考文章(内含各个flags的说明):https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

2 汇编优化实例

  • 编译器:x86-64 gcc 12.2

以下面的C代码为例,该代码实现s2s1的拷贝,最后返回s1的地址。

char *cpy(char *s1, const char *s2)
{
    char *ret= s1;
    while((*s1++ = *s2++));
    return ret;
}

-O0汇编

cpy:
		# 这里需要用到rbp寄存器,但是它里面的值可能caller还需要使用,将其压栈
        push    rbp
        # 使用栈rsp之后的内存来保存s1和s2
        mov     rbp, rsp
        # 即*(rbp-24) = s1,*(rbp-32) = s2
        mov     QWORD PTR [rbp-24], rdi
        mov     QWORD PTR [rbp-32], rsi
        # rax = s1的地址
        mov     rax, QWORD PTR [rbp-24]
        # 即*(rbp-8) = rax = s1
        mov     QWORD PTR [rbp-8], rax
        # 指令流水线对齐?
        nop
.L2:
		# 即rdx = s2
        mov     rdx, QWORD PTR [rbp-32]
        # 即rax = ++s2,即取s2的下一个值的地址
        lea     rax, [rdx+1]
        # 即QWORD PTR [rbp-32] = ++s2,为下一次赋值做准备
        mov     QWORD PTR [rbp-32], rax
        # 即rax = s1
        mov     rax, QWORD PTR [rbp-24]
        # 即rcx = ++s1,即取s1的下一个值的地址
        lea     rcx, [rax+1]
        # 即QWORD PTR [rbp-24] = ++s1,为下一次赋值做准备
        mov     QWORD PTR [rbp-24], rcx
        # 取s2中的一个字节到edx寄存器中
        movzx   edx, BYTE PTR [rdx]
        # 即将*s2的一个字节拷贝到*s1中
        mov     BYTE PTR [rax], dl
        # 最后取本次赋的值到eax,其中eax由低位al和高位ah组成,可以直接使用这两个寄存器标号
        movzx   eax, BYTE PTR [rax]
        # 逻辑与操作,当且仅当拷贝到最后的结束符\0(ASCII码为0)时,jne跳出循环,否则继续回去执行L2
        test    al, al
        jne     .L2
        # 返回值保存在rax,即s1的地址
        mov     rax, QWORD PTR [rbp-8]
        # rbp出栈,恢复函数调用前的状态
        pop     rbp
        ret

-O1汇编

cpy:
		# s1的地址保存在rdi寄存器中,赋值给rax寄存器
        mov     rax, rdi
        # 初始化edx寄存器
        mov     edx, 0
.L2:
		# 读取(s2+偏移rdx)地址中的内容
        movzx   ecx, BYTE PTR [rsi+rdx]
        # cl即ecx的低位,即*(s1+rdx)=*(s2+rdx)
        mov     BYTE PTR [rax+rdx], cl
        # 计数器增加
        add     rdx, 1
        # 逻辑与操作,当且仅当拷贝到最后的结束符\0(ASCII码为0)时,jne跳出循环,否则继续回去执行L2
        test    cl, cl
        jne     .L2
        ret

可以看出,如果编译器优化没有打开,代码执行的效率是比较低的,两个字符串指针各自计数,也没有必要用到栈和那么多寄存器。由于程序很简单,在本例中的-O1-O2-O3-Ofast的优化结果是一样的,当然我们需要根据实际情况更改优化等级,有时调试出现奇怪的、未知的结果可能就是编译优化导致的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GCC编译优化选项及汇编优化实例 的相关文章

  • FreeRTOS学习笔记——基础知识与移植(STM32F103)

    1 1 前后台系统 xff1a 早期嵌入式开发没有嵌入式操作系统的概念 xff0c 直接操作裸机 xff0c 在裸机上写程序 xff0c 比如用51单片机基本就没有操作系统的概念 通常把程序分为两部分 xff1a 前台系统和后台系统 简单的
  • STM32开源代码——MAX30100程序

    正点原子精英开发板 模块化封装 xff0c 入口函数简明 xff0c 易上手操作 展示main c代码 xff0c 完整代码请下载 xff08 数据打印到串口在MAX30100 PulseOximeter c xff09 点击下载代码 in
  • STM32开源代码——2.8寸TFTLCD屏虚拟键盘触摸程序

    正点原子精英开发板 模块化封装 xff0c 入口函数简明 xff0c 易上手操作 展示main c代码 xff0c 完整代码请下载 点击下载代码 include 34 sys h 34 include 34 delay h 34 inclu
  • STM32开源代码——光敏传感器

    正点原子精英开发板 模块化封装 xff0c 入口函数简明 xff0c 易上手操作 展示main c代码 xff0c 完整代码请下载 点击下载代码 include 34 led h 34 include 34 delay h 34 inclu
  • 个人项目——机智云开源APP基础修改教程(Android)

    之前写过一篇STM32接入机智云的教程 xff0c 最后说要有时间给大家写一篇修改机智云开源Demo APP的教程 xff0c 刚好楼主考完直流传动 xff0c 然后帮小学弟的一个32项目接入了机智云 xff0c 然后打算帮他修改一下Dem
  • FreeRTOS学习笔记——FreeRTOS任务创建和删除实验(静态方法)

    6 3 任务创建和删除实验 静态方法 6 3 1 实验程序设计 1 实验目的 上一小节我们讲了使用函数xTaskCreate 来创建任务 xff0c 本节在上一小节的基础上做简单的修改 xff0c 使用函数xTaskCreateStatic
  • FreeRTOS学习笔记——FreeRTOS 系统内核控制函数

    FreeRTOS 中有一些函数只供系统内核使用 xff0c 用户应用程序一般不允许使用 xff0c 这些API 函数就是系统内核控制函数 本章我们就来学习一下这些内核控制函数 xff0c 本章分为如下几部分 xff1a 10 1 内核控制函
  • FreeRTOS学习笔记——FreeRTOS 时间管理

    在使用FreeRTOS 的过程中我们通常会在一个任务函数中使用延时函数对这个任务延时 xff0c 当执行延时函数的时候就会进行任务切换 xff0c 并且此任务就会进入阻塞态 xff0c 直到延时完成 xff0c 任务重新进入就绪态 延时函数
  • 算法基础23-二分图

    二分图 二分图的判断二分图的最大匹配数 可以去看acwing题解 二分图的判断 span class token comment AcWing 860 染色法判定二分图 span span class token macro propert
  • Router 选择

    Connected Dominating Set Example of a Connected Dominating Set Router 必须形成一个 CDS xff08 Connected Dominating Set xff0c 连接
  • devtool: unset _PYTHON_SYSCONFIGDATA_NAME

    问题 在 Ubuntu 20 04 1 LTS 上进行编译Yocto时报错 xff0c 出现如下错误 xff1a bb data smart ExpansionError Failure expanding variable SRCPV e
  • VsCode 配置PySide6及测试

    目录 VSCode插件安装安装Python插件安装PySide6插件 PySide6安装PySide6配置VSCode创建UI文件 在这里插入图片描述 https img blog csdnimg cn cbf7cd76d7d84048ab
  • Ubuntu 14.04 Desktop的Raid1安装总结

    Ubuntu 14 04 Desktop的Raid1安装总结 安装基于Ubuntu14 04 Desktop的Raid1 由于采用UEFI GPT方式作为系统启动方式 xff0c 在安装过程中出现了很多异常情况 本文记录安装的过程 安装步骤
  • sem_wait sem_post信号量操作进本函数

    sem wait sem post 信号量的数据类型为结构sem t xff0c 它本质上是一个长整型的数 函数sem init xff08 xff09 用来初始化一个信号量 它的原型为 xff1a extern int sem init
  • 常见gcc编译警告整理(开始)

    1 warning no newline at end of file 在文件最后一行加上回车键 解释 xff1a 在 Rationale for the C99 standard 一文中 xff0c 有C99的相关信息 xff1a A b
  • 对于结构体变量赋值的误区

    以前在使用结构体时没有在结构体变量之间直接赋值 xff0c 今天同事在查看别人的代码时 xff0c 发现有两个结构体变量直接赋值的语句当时感觉这个语句不对 xff0c 认为在一个结构体里边 xff0c 既有一般的无符号整形与数组 xff0c
  • 线程同步(互斥锁与信号量的作用与区别)

    信号量用在多线程多任务同步的 xff0c 一个线程完成了某一个动作就通过信号量告诉别的线程 xff0c 别的线程再进行某些动作 xff08 大家都在semtake的时候 xff0c 就阻塞在 哪里 xff09 而互斥锁是用在多线程多任务互斥
  • 误解程序运行(从单片机到开始)

    误解程序运行 从单片机到开始 关于程序的执行 xff0c 以前想的不多 xff0c 没有意识到一个程序在运行时 xff0c 从哪里读指令 xff0c 数据又写在哪里 最近在看CSAPP时这个念头经常在脑袋中晃荡 从单片机上知道 xff0c
  • Out-of-Bounds Memory References and Buffer Overflow

    callee pushl edp save edp on stack movl esp edp pushl ebx save ebx subl 20 esp popl ebx restore ebx popl edp restore
  • 嘉立创专业板stm32拓展板十分钟快速入门

    本文是为不想深入学PCB但是迫不得已需要用PCB的人或者想要做一个32的拓展版的童鞋们的快速入门并做出板子的博客 xff0c 因此 xff0c 本文内容不会深入讨论元件 xff0c 只会教授基础流程 xff0c 并且没有画芯片 本文内容 x

随机推荐

  • 电子信息工程四年学习之思

    毕业后 xff0c 回顾四年学习历程发现 xff0c 当时以为的明白 xff0c 到现在都是那时的不明白 或许是自己的经历 xff08 参加比赛比较多 xff09 导致了现在的反思 但是 xff0c 回顾那个时候的课程设置 xff0c 却都
  • 将要到来的三大技术革命与联系

    http www csdn net article 2013 02 14 2814128 2013大数据 http www csdn net article 2013 02 15 2814135 bigdata is coming 大数据
  • Keil的常见编译警告

    1 warning 767 D conversion from pointer to smaller integer 解释 xff1a 将指针转换为较小的整数 影响 xff1a 可能造成的影响 xff1a 容易引起数据截断 xff0c 造成
  • 《大数据时代》之后

    现在想想也不记得当时是怎么找到 大数据时代 这本书的 xff0c 好像是在查找数据库方面的书 xff0c 看到亚马逊推荐的书里有这本 xff0c 发现最近才出版的就买一本回来看看 然而这个过程中 xff0c 其实自己已经得到了大数据带来的影
  • 《代码大全》笔记

    最近将去年毕业时 xff0c 大神推荐的 代码大全 看完了 xff08 已经过去一年了 xff0c 要十分感谢推荐 xff0c 还有凤林兄的 深入理解计算机系统 xff09 零零碎碎的时间 xff0c 发现很多东西虽然在书中标记了 xff0
  • 《编程精粹》思之代码与产品

    之前眼中有代码无产品 xff0c 现在眼中有产品有代码 xff0c 什么时候能做到有产品无代码 xff1f 还需要努力 刚开始实习的时候 xff0c 总喜欢在程序中使用 p 43 1 61 而不是p 1 来给入参 xff0c 甚至于用来给定
  • 树莓派4B+Intel神经计算棒(Stick2)+YoloV5可行性考察报告

    1 神经棒只能作为协处理器 在执行脚本时 xff0c 通过在命令后面加上 d 设备名 就可以指定硬件设备来加速推理 例如 命令行指定神经棒运行 demo squeezenet download convert run span class
  • 在Vue中获取v-for的index值

    lt el submenu index 61 34 item id 43 39 39 34 v for 61 34 item index in menuList 34 key 61 34 item id 34 gt lt template
  • 死区时间的分析与设置

    出现死区的主要原因是因为MOS管的源极和栅极之间的结电容 现在在栅极加上一个门电路 当门电路输出的信号跳变的瞬间 xff0c 电流是非常大的 xff0c 会导致MOS管发热 xff0c 所以需要在门电路后面再串联一个电阻 xff0c 这个电
  • MOS管特性和导通过程

    三极管是流控流器件 xff0c 它不能驱动功率太大的器件 xff0c 因为此时C极电流大 xff0c 而CE压降为0 3V左右 xff0c 在三极管上面消耗的功率就很大 xff0c 还容易发热 所以压控压型的MOS管就诞生了 特性 一开始给
  • 2014欢聚时代(YY)软件研发笔试题

    本文转载自 xff1a http blog csdn net arcsinsin article details 12714027
  • FOC 单电阻采样 位置环控制伺服电机

    最近在做微型伺服电机的控制 xff0c 需要平滑地将电机定位到某个位置 伺服电机 编码器 PCB都是自制的 这里我把整个的流程和遇到的问题记录一下 目录 硬件设计 STM32CubeMX配置 程序设计 测试遇到的问题 演示视频 硬件设计 这
  • C语言正余弦函数定点查表算法原理及实现

    如果用数学库中的sin和cos函数计算 xff0c 可能涉及浮点 乘法 除法运算 xff0c 运行速率较低 这里介绍一种正余弦查表算法 参考代码 xff1a SMT32 FOC电机库的mc math c 首先我们知道正弦和余弦的函数值差了9
  • Git合并操作之merge、rebase、squash详解

    文章目录 1 git merge和git rebase1 1 git merge1 2 git rebase1 2 1 rebase交互模式1 2 2 git pull rebase 2 git squash 有时在一个分支上完成一个功能的
  • QT 右键菜单栏的实现

    有时我们希望在窗口中右键弹出菜单 xff0c 这里来介绍一下QT中怎么实现 xff0c 首先创建一个MainWindow项目 1 在mainwindow h文件中添加槽声明 private slots void on MainWindow
  • 基于Cortex-M的RTOS上下文切换详解及FreeRTOS实例

    文章目录 1 Cortex M MCU特性 1 1 操作模式 1 2 寄存器 1 2 1 核心寄存器 1 2 2 浮点寄存器 Floating Point registers 1 2 3 特殊寄存器 Special Registers 1
  • ARM异常处理(4):SVC和PendSV的作用详解

    SVC Supervisor Call 和PendSV Pendable Service Call 是针对软件和操作系统的两个异常 1 SVC SVC用于生成系统函数调用 xff0c 例如 xff0c 用户程序不允许直接访问硬件 xff0c
  • NXP EDMA学习(3):Scatter/Gather模式之串口循环缓冲区的实现

    在实际应用中 循环缓冲区的使用十分广泛 如果DMA每次接收一定字节然后再在中断中写入自己定义的循环缓冲区 这样又失去了使用DMA的意义 所以硬件上的循环缓冲区的实现非常重要 大部分芯片支持DMA循环模式 而NXP的eDMA则可以通过Scat
  • MCUXPresso IDE(Eclipse):每次build都重新编译,重新生成makefile

    最近在使用MCUXPresso IDE开发项目 xff0c 但是有几个问题 xff0c 这里来汇总一下 1 只要修改了某个文件 xff0c 就会导致整个工程都要重新编译一遍 项目 gt Properties gt C C 43 43 Bui
  • GCC编译优化选项及汇编优化实例

    文章目录 1 优化级别 O1 O2 O3 O0 Os Ofast Og Oz On n gt 61 4 2 汇编优化实例 最近在做LVGL的GUI xff0c 移植官方的SDK xff0c 然后CPU占用率竟然达到了99 xff0c 这就让