_浅谈单片机的gcc优化级别__以双音频信号发生器为例

 一、简介

        gcc有多种优化级别,一般不选择的情况下,IDE默认是按照-Og或这-O2优化的。

        以gcc编译器为例,浅谈一下优化级别,我们常见的优化一般是指gcc的-O2、-Og。除此之外,gcc还有-Os等一系列优化,链接器也有优化级别。

        基于单片机的开发,如果关注的是生成代码的大小,那么可以考虑-Os和-Oz。如果在乎性能的话,可以尝试-O2以上的优化级别

-O优化

一共有级别。当然上面还有-O4甚至-O5.

  • -O0:无优化,适合调试。
  • -O1:基本优化,适合快速迭代开发。
  • -O2:中级优化,适合大多数生产环境。
  • -O3:高级优化,适合对性能要求较高的应用。
  • -Ofast:极端优化,适合对性能要求极高且对标准合规性要求不高的应用。
  • -Og:调试优化,适合开发和调试阶段。
  • -Os:优化生成的代码大小,而不是性能。
  • -Oz:极度优化大小,进一步优化生成的代码大小,比 -Os更激进,可能会牺牲一些性能。
  • -flto:在链接阶段进行优化,允许进行跨文件的优化,进一步提高性能,但耗时间
  • -fprofile-generate(生成配置文件) 和 -fprofile-use(基于配置文件的优化)

从下面生成的代码体积来看优化对程序的影响

【-O0】

【-O1】

【-O2】

【-O3】

【-Ofast】

【-Og】

【-Os】

【-Oz】

-flto

        启用链接时优化,它会跨文件进行优化,会把所有东西混杂起来再优化,同时也会影响调试信息的生成。这就意味着,前面的-O级优化还能把编译出的二进制文件与你的源码对应起来,现在只能与反汇编对应。

        如下图,你甚至能看到CPU的那12个寄存器,此时展现在调试窗口的就是反汇编了。如果你想要让IDE调试程序时与源码对应,那么就需要加上-g编译标志

做个对比,虽然仅从代码量来观察是片面了许多,但多少能反映一些(原先代码忘记做速度测试了)。

上图是未开-flto,下图是开了-flto

【-O1】

【-O2】

【-O3】

【-Ofast】

【-Os】

【-Oz】

只不过有时候同样一份代码,用不同的方式优化可能还会报错,比如下面是-Og -flto优化,因为链接库的某种不知名原因

上述的-O级优化其实是由一系列单项优化组成的,可以组合,更适合竞赛宝宝体质的#pragram

-fwhole-program
目标:在整个程序范围内进行优化。
特点:
允许编译器在链接阶段对整个程序进行全局优化。
通常与 -O3 一起使用,以获得更高的性能。
-fprofile-generate 和 -fprofile-use
目标:基于运行时数据进行优化。
特点:
-fprofile-generate:生成配置文件。
-fprofile-use:使用生成的配置文件进行优化。
可以显著提高性能,特别是在热点路径上。
-fipa-cp-algorithm
目标:改进跨过程常量传播算法。
特点:
用于改进跨过程的常量传播,提高代码性能。
-fipa-pta
目标:改进指针分析。
特点:
用于改进指针分析,提高代码性能。 
-funroll-loops
目标:展开循环。
特点:通过展开循环减少循环开销,提高性能。
-finline-functions
目标:内联函数。
特点:自动内联小函数,减少函数调用开销。
-fomit-frame-pointer
目标:省略帧指针。
特点:在函数调用中省略帧指针,减少寄存器使用,提高性能。
-fstrict-aliasing
目标:启用严格的别名规则。
特点:允许编译器进行更激进的优化,假设不同类型的指针不会指向同一内存地址。
-ftree-vectorize
目标:启用向量化优化。
特点:将循环中的标量操作转换为向量操作,利用 SIMD 指令集提高性能。
-floop-interchange
目标:交换循环顺序。
特点:优化嵌套循环的顺序,提高缓存利用率。
-floop-strip-mine
目标:分割循环。
特点:将大循环分割成多个小循环,提高缓存利用率。
-floop-block
目标:块划分循环。
特点:将循环体划分为多个块,提高缓存利用率。
-fgraphite
目标:启用 Graphite 循环变换框架。
特点:使用高级循环变换技术优化循环性能。
-fipa-sra
目标:启用跨过程结构体拆解。
特点:在跨过程调用中拆解结构体,减少内存访问开销。

链接器也有一系列优化,就是不常用,包括上面提到的一系列组合,对于单片机开发来说

-Wl,--hash-style=both
目标:使用两种哈希风格。
特点:链接器使用两种哈希风格(SYSV 和 GNU),提高符号查找效率。
-Wl,--no-undefined
目标:禁止未定义的符号。
特点:链接器在链接时检查未定义的符号,确保所有符号都已定义。
-Wl,--no-merge-exidx-entries
目标:禁止合并异常索引条目。
特点:防止链接器合并异常索引条目,确保异常处理的准确性。
-Wl,--sort-common
目标:按大小排序公共符号。
特点:链接器按大小排序公共符号,提高内存布局的效率。
-Wl,--sort-section=name
目标:按名称排序节区。
特点:链接器按名称排序节区,提高内存布局的效率。
-Wl,--no-keep-memory
目标:释放内存。
特点:链接器在链接过程中释放不再需要的内存,减少内存占用。

 二、测试

        这里做了一点点简单不那么严谨的小测试,使用的测试工程为下面链接中的双音频信号发生器ichliebedich-DaCapo/STM32F407VET6: stm32f407vet6 (github.com)

-O0:

       结果很感人,烧录时一切正常

        但按下按键后还没怎么执行就卡住了。

        在卡住之后,我们停下来可以清楚地看到堆栈爆了(栈区溢出,下方蓝色的msp寄存区),直接的影响就是LVGL处理事件时,访问数组直接越界。换句话说,如果下次碰到了LVGL数组越界,那么就要怀疑是栈区溢出了。

现在看一看编译大小

        -gc-sections是去除不用的段,--print-memory-usage是打印内存分布,Map=${BIN_DIR}/${PROJECT_NAME}.map是生成map映射文件。当然,前面都得有-Wl

add_link_options(-Wl,-gc-sections,--print-memory-usage,-Map=${BIN_DIR}/${PROJECT_NAME}.map)

-O1:

        同样的代码,使用-O1可以很明显地看到优化情况

        下面将以以内置的FPS组件显示,在128个数据点、线性插值算法、800Hz(只是虚拟的,不是真的)下进行测试

        FPS组件代码是基于LVGL的文本框组件写的一个小类,没有做什么性能上的优化,但简单测试衡量一下性能变化还是可以做到的。


/*** @brief 工具类*/
class Tools
{
public:static inline auto fps_init(Font font, Coord x = 0, Coord y = 40, Coord width = 60, Coord height = 20) -> void;// 显示fpsstatic inline auto fps(bool time = true) -> void;static inline auto restart_fps() -> void;static inline auto set_right() -> void;static inline auto set_left() -> void;static inline auto set_center() -> void;static inline auto clear_fps() -> void;private:// 获取时间static inline auto get_tick() -> uint32_t;// 单线程更新事件static inline auto update_tick() -> void;private:static inline Obj_t label_fps{};static inline uint32_t count = 0;static inline uint32_t tick = 0;
};/*** @brief fps功能初始化* @param font 指定字库中要有fps和十位数字,字体大小为13即可* @param x x轴* @param y y轴* @note 默认文本框为60*20,即宽60,高20,且文本为左对齐。*/
auto Tools::fps_init(Font font, Coord x, Coord y, Coord width, Coord height) -> void
{Text label;label.init_font(font);
#if SIMPLE_FPSlabel.init(label_fps, x, y, width, height, "");
#elselabel.init(label_fps, x, y, 60, 80, "fps\n0");
#endif}/*** @brief fps显示* @note 启用该功能之前必须先调用fps_init进行必要的初始化。启用fps显示,即在需要的地方调用本函数*      默认显示一帧需要的时间单位为ms*/
auto Tools::fps(bool time) -> void
{
#if SIMPLE_FPSchar buf[7];if (time){// 显示一帧的时间sprintf(buf, "%.2fms", 1.0 * get_tick() / (count++));} else{// 显示帧率sprintf(buf, "%.2f", 1000.0 * (count++) / get_tick());}Text::set_text(buf, label_fps);
#elsechar buf[9];// 显示一帧的时间sprintf(buf, "fps\n%.2f", 1.0*get_tick() / (count++));// 显示帧率
//    sprintf(buf, "fps\n%.2f", 1000.0*(count++))/get_tick();Text::set_text(buf, label_fps);
#endif
}auto Tools::get_tick() -> uint32_t
{uint32_t temp_tick = lv_tick_get();// 防止溢出if (temp_tick < tick)temp_tick += (0xFFFF'FFFF - tick);elsetemp_tick -= tick;return temp_tick;
}auto Tools::update_tick() -> void
{tick = lv_tick_get();
}/*** @brief 重启fps*/
auto Tools::restart_fps() -> void
{update_tick();count = 0;
}auto Tools::set_right() -> void
{Text::set_text_align(LV_TEXT_ALIGN_RIGHT, label_fps);
}auto Tools::set_center() -> void
{Text::set_text_align(LV_TEXT_ALIGN_CENTER, label_fps);
}auto Tools::set_left() -> void
{Text::set_text_align(LV_TEXT_ALIGN_LEFT, label_fps);
}auto Tools::clear_fps() -> void
{Text::set_text("", label_fps);}

可以看出,一帧所用时间为36.88ms左右,并且屏幕右侧有严重的漏墨现象,这是由于绘制像素点函数LCD_Set_Pixel不够卖力(主频不够高)导致的

-O2:

        接下来使用-O2级别,同上面相比,我们可以看到RAM和Flash都略微增加了少许

 

        接下来测试一下性能,从右上角的36.79ms可以看出,相比-O1可能有了那么一点点提升(因为不能排除误差),从观察效果来看,漏墨现象也是挺严重的。在我印象中,应该比-O1强一些才对,可能是这次没发挥好(不同工程、相同的优化级别,显现的效果是不同的)

 -Og:

        接下来我们看看平时最常用的调试级别优化能拿出怎样的成绩吧。首先是代码体积比-O1还小了一点,内存占用相同。

        接下来看看性能,37.16ms,可能是由于调试信息的原因性能就略逊一筹,不过与-O1、-O2也大差不差

-O3:

        接下来有请-O3大佬, 一出手就是非同凡响,RAM占用些许提升,ROM大幅提升

从性能上来看,竟然与前面差不多,那么可以说明一个问题,现在性能的瓶颈不在于算法,而在于打点速度。真是失策,测了这么多有种白费的感觉。

 ----------------

        不过接下来换种算法测试一下,就以样条算法为例,这个与贝塞尔曲线差不多的速度,比线性插值慢,但稳定多了,帧率最后会趋于一个稳定的值,所以测试结果相对要可靠一些。

        由于工程不变,所以就不继续展示代码大小了

 -O1

-O2

-Og

-O3

-Ofast

代码体积比-O3多了一点点,-O3 -ffast-math、-O4、-O5在代码体积上与-O3完全一致

该优化被clang淘汰掉了,取而代之的是-O3 -ffast-math。gcc还有是-Ofast的

-Os

代码确实小了一些

看看性能,63.63ms与-Og差不多

-Oz

看来代码的体积已经被压缩到极致了

性能与-Os差不多,但与-Ofast比起来就相对明显

         至于-flto优化这个我现在无法测试,因为改了文件组织编译方式,把大部分文件都分别编译为静态库,然后再统一链接成elf文件。所以无法使用-flto,一使用就会出现找不到定义的错误。之前没改CMakelists前,使用-flto,代码体积上确有优化,但性能没有测试过。

    add_compile_options(-flto )

        -O3及以上的优化要慎重对待,上次基于样条算法编写一个模板函数,只有在-O2下可以正常运行,开-O3以上 会卡死,开-O2以下堆栈会爆,真是让人摸不到头脑。后来也不知改了什么,或许是改动了其他函数间接导致这个模板函数又行了,在-O1到-Ofast均可正常使用。

        编译器优化,很玄

         另提一嘴,在使用总大小相同的缓冲数组情况下,LVGL的双缓冲要优于单缓冲。设置双缓冲也很简单,只有在旁边另加一个静态数组,然后把数组名填入到lv_disp_draw_buf_init的第三个参数中

/*** @brief 初始化显示驱动* @tparam flush 涂色函数,有LCD驱动提供* @note 为了让lambda表达式可以不用捕获外部函数,只能使用函数模板。如果使用函数指针来传递就必须要显示捕获*/
template<void (*flush)(uint16_t, uint16_t, uint16_t, uint16_t, const uint16_t *)>
auto GUI::disp_drv_init() -> void
{// 在缓冲数组总大小同等的情况下,双缓冲明显优于单缓冲static lv_disp_draw_buf_t draw_buf_dsc;static lv_color_t buf_2_1[MY_DISP_HOR_RES * MY_DISP_BUF_SIZE];static lv_color_t buf_2_2[MY_DISP_HOR_RES * MY_DISP_BUF_SIZE];lv_disp_draw_buf_init(&draw_buf_dsc, buf_2_1, buf_2_2,MY_DISP_HOR_RES * MY_DISP_BUF_SIZE);   /*Initialize the display buffer*/lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/disp_drv.hor_res = MY_DISP_HOR_RES;disp_drv.ver_res = MY_DISP_VER_RES;
// C环境下就不要使用lambda表达式,自行定义flush函数disp_drv.flush_cb = [](lv_disp_drv_t *, const lv_area_t *area, lv_color_t *color_p){flush(area->x1, area->y1, area->x2, area->y2, (const uint16_t *) color_p);};disp_drv.draw_buf = &draw_buf_dsc;lv_disp_drv_register(&disp_drv);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/467202.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用JavaScript、Nodejs写一个本地tcp服务,用于前端WebSocket调试

效果&#xff1a; 准备工作&#xff1a; 新建一个文件夹&#xff0c;在根目录安装依赖&#xff1a; npm install ws express 依赖介绍&#xff1a; WS是一个轻量级、高效的WebSocket库&#xff0c;适用于Node.js环境。 express 是一个流行的Node.js Web应用程序框架。 新…

企业常见的主数据管理挑战及解决方案

在当今高度数字化的商业环境中&#xff0c;数据已成为企业决策、运营和战略规划的核心。主数据管理&#xff08;MDM&#xff09;作为管理核心业务数据的一种方式&#xff0c;帮助企业确保其关键数据在整个组织中保持一致、准确和可信。然而&#xff0c;许多企业在实施主数据管理…

Python http打印(http打印body)flask demo(http调试demo、http demo、http printer)

文章目录 代码解释 代码 # flask_http_printer.pyfrom flask import Flask, request, jsonify import jsonapp Flask(__name__)app.route(/printinfo, methods[POST]) def print_info():# 分隔符separator "-" * 60# 获取请求头headers request.headers# 获取 JS…

从无音响Windows 端到 有音响macOS 端实时音频传输播放

以下是从 Windows 端到 macOS 端传输音频的优化方案&#xff0c;基于上述链接中的思路进行调整&#xff1a; Windows 端操作 安装必要软件 安装 Python&#xff08;确保版本兼容且已正确配置环境变量&#xff09;。安装 PyAudio 库&#xff0c;可通过 pip install pyaudio 命令…

用 Python 从零开始创建神经网络(二)

用 Python 从零开始创建神经网络&#xff08;二&#xff09; 引言1. Tensors, Arrays and Vectors&#xff1a;2. Dot Product and Vector Additiona. Dot Product &#xff08;点积&#xff09;b. Vector Addition &#xff08;向量加法&#xff09; 3. A Single Neuron with …

python爬虫自动库DrissionPage保存网页快照mhtml/pdf/全局截图/打印机另存pdf

目录 零一、保存网页快照的三种方法二、利用打印机保存pdf的方法 零 最近星球有人问如何使用页面打印功能&#xff0c;另存为pdf 一、保存网页快照的三种方法 解决方案已经放在星球内&#xff1a;https://articles.zsxq.com/id_55mr53xahr9a.html当然也可以看如下代码&…

现代Web开发:WebSocket 实时通信详解

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 现代Web开发&#xff1a;WebSocket 实时通信详解 现代Web开发&#xff1a;WebSocket 实时通信详解 现代Web开发&#xff1a;WebS…

Hadoop完全分布式环境搭建步骤

【图书介绍】《Spark SQL大数据分析快速上手》-CSDN博客 大数据与数据分析_夏天又到了的博客-CSDN博客 本文介绍Hadoop完全分布式环境搭建方法&#xff0c;这个Hadoop环境用于安装配置Spark。假设读者已经安装好Visual Box 7.0.6虚拟环境与一个CentOS 7虚拟机&#xff08;如果…

133.鸿蒙基础01

鸿蒙基础 1.自定义构建函数1. 构建函数-[Builder ](/Builder )2. 构建函数-传参传递(单向)3. 构建函数-传递参数(双向)4. 构建函数-传递参数练习5. 构建函数-[BuilderParam ](/BuilderParam ) 传递UI 2.组件状态共享1. 状态共享-父子单向2. 状态共享-父子双向3. 状态共享-后代组…

如何保证kafka生产者数据可靠性

ack参数的设置&#xff1a; 0&#xff1a;生产者发送过来的数据&#xff0c;不需要等数据落盘应答 假如发送了Hello 和 World两个信息&#xff0c;Leader直接挂掉&#xff0c;数据就会丢失 生产者 ---> Kafka集群 一放进去就跑 数据可靠性分析&#xff1a;丢数 1&#…

业务模块部署

一、部署前端 1.1 window部署 下载业务模块前端包。 &#xff08;此包为耐威迪公司发布&#xff0c;请联系耐威迪客服或售后获得&#xff09; 包名为&#xff1a;业务-xxxx-business &#xff08;注&#xff1a;xxxx为发布版本号&#xff09; 此文件部署位置为&#xff1a;……

后台管理系统窗体程序:文章管理 > 文章列表

目录 文章列表的的功能介绍&#xff1a; 1、进入页面 2、页面内的各种功能设计 &#xff08;1&#xff09;文章表格 &#xff08;2&#xff09;删除按钮 &#xff08;3&#xff09;编辑按钮 &#xff08;4&#xff09;发表文章按钮 &#xff08;5&#xff09;所有分类下拉框 &a…

Windows10/11开启卓越性能模式 windows开启卓越性能电源模式 工作电脑开启卓越性能模式 电脑开启性能模式

Windows10/11开启卓越性能模式 windows开启卓越性能电源模式 工作电脑开启卓越性能模式 电脑开启性能模式 1、所要用到的激活工具2、开启电脑卓越性能模式Windows11Windows10在电源模式中选择卓越性能模式 3、将系统版本切换为 工作站版本 1、所要用到的激活工具 KMS激活工具(…

D62【python 接口自动化学习】- python基础之数据库

day62 SQL 基础 学习日期&#xff1a;20241108 学习目标&#xff1a;MySQL数据库-- 131 SQL基础和DDL 学习笔记&#xff1a; SQL的概述 SQL语言的分类 SQL的语法特征 DDL - 库管理 DDL - 表管理 总结 SQL是结构化查询语言&#xff0c;用于操作数据库&#xff0c;通用于绝大…

计算机图形学 实验二 三维模型读取与控制

目录 一、实验内容 二、具体内容 (在实验2.3的基础上进行修改) 1、OFF格式三维模型文件的读取 2、三维模型的旋转动画 3、键盘鼠标的交互 4、模型的修改 三、代码 一、实验内容 读取实验提供的off格式三维模型&#xff0c;并对其赋色。利用鼠标和键盘的交互&#xff0…

Redis 中 Bitmap 原理和应用

Bitmap Redis中的Bitmap&#xff08;位图&#xff09;是一种较为特殊数据类型&#xff0c;它以最小单位bit来存储数据&#xff0c;我们知道一个字节由 8个 bit 组成&#xff0c;和传统数据结构用字节存储相比&#xff0c;这使得它在处理大量二值状态&#xff08;true、false 或…

Springboot3.3.5 启动流程(源码分析)

一图搞懂 SpringBoot 启动流程&#xff08;清晰明了&#xff09;&#xff1a; createWebServer &#xff08;ServletWebApplicationContext&#xff09;流程 finishBeanFactoryInitialization&#xff08;ServletWebApplicationContext&#xff09;Bean装配流程 真正干活的&am…

CSS实现图片3D立体效果

概述 本文主要讲述如何通过 CSS 简单的设置就可以实现图片的 3D 立体效果。 3D 立体效果 当鼠标移入某一个图片上时,其余图片会像该图片倾斜。 具体实现 静图如下: 倒影效果图片会有一个倒影效果,其代码如下: <style>img {-webkit-box-reflect: below 1px linea…

java: 无法访问org.springframework.web.bind.annotation.RequestMapping

一、报错问题 java: 无法访问org.springframework.web.bind.annotation.RequestMapping 二、原因分析 SpringBoot使用了3.0或者3.0以上&#xff0c;因为Spring官方发布从Spring6以及SprinBoot3.0开始最低支持JDK17。所以仅需要将SpringBoot版本降低为3.0以下即可&#xff08;或…

Node.js:Express 服务 路由

Node.js&#xff1a;Express 服务 & 路由 创建服务处理请求req对象 静态资源托管托管多个资源挂载路径前缀 路由模块化 Express是Node.js上的一个第三方框架&#xff0c;可以快速开发一个web框架。本质是一个包&#xff0c;可以通过npm直接下载。 创建服务 Express创建一…