如何优化代码和RAM大小
如果供应商为我自己的项目提供了一个起点,那就太好了。工作'眨眼'始终是一个伟大的首发。方便总是有代价,而且“眨眼”就是夸大“切换GPIO引脚”的代码大小。对于具有少量RAM和FLASH的设备,这可能会引起关注:如果'blinky'占用那么多,我的应用程序是否适合该设备?不要担心:可以轻松地修剪掉(或任何其他项目)。

恩智浦LPC845-BRK主板上的Binky
我在这里使用一个'blinky'项目作为一个例子:修剪技巧也适用于任何其他类型的项目。
在本教程中,我在BRK(突破)板上使用NXP LPC845:

恩智浦LPC845-BRK板
1、Blinky示例
我所使用的是基于Eclipse的NXP MCUXpresso IDE:

选择SDK板
我使用供应商默认设置创建了'blinky'项目:

Blinky项目
一个'blinky'应该闪烁一个LED,对任何项目来说都是一个好的开手机。构建相当小的项目,代码大小如下:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
10536 B |
64 KB |
16.08% |
|
SRAM: |
2424 B |
16 KB |
14.79% |
|
text |
data |
bss |
dec |
hex |
filename |
|
10532 |
4 |
2420 |
12956 |
329c |
lpc845breakout_led_blinky.axf |
该信息也在控制台中显示,分为文本,数据和bss:
10K的'blinky'看起来有点夸张。但是我们现在将在接下来的步骤中修改它。
2、大小信息
有关大小信息的含义,请阅读“ text,data和bss:Code and Data Size Explained ”。查看我的设备上使用空间的正常方法是检查链接器映射文件(* .map):

链接器映射文件
但是这个map文件很难阅读,而且对于专家来说更是如此:它列出了具有地址和大小的部分:

链接器映射文件内容
使用MCUXpresso IDE V11,有一个很好的“图像信息”视图,它基本上是一个更好的ma'p文件信息查看器:

图像信息查看
我可以过滤和排序数据,这让我知道代码和数据使用了多少空间:

图像信息存储器内容
当然,它需要一些关于应用程序应该做什么的知识。我总是浏览视图中的项目列表,看看是否有任何我不希望的东西:也许应用程序正在使用可以删除的东西。
3、源代码
对于一个简单的眨眼,这是相当小的。首先要检查程序正在做什么。main.c有这个:
/*
* Copyright 2017 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/ #include "board.h"
#include "fsl_gpio.h" #include "pin_mux.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define BOARD_LED_PORT 1U
#define BOARD_LED_PIN 2U /*******************************************************************************
* Prototypes
******************************************************************************/ /*******************************************************************************
* Variables
******************************************************************************/
volatile uint32_t g_systickCounter; /*******************************************************************************
* Code
******************************************************************************/
void SysTick_Handler(void)
{
if (g_systickCounter != 0U)
{
g_systickCounter--;
}
} void SysTick_DelayTicks(uint32_t n)
{
g_systickCounter = n;
while (g_systickCounter != 0U)
{
}
} /*!
* @brief Main function
*/
int main(void)
{
/* Define the init structure for the output LED pin*/
gpio_pin_config_t led_config = {
kGPIO_DigitalOutput,
,
}; /* Board pin init */
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole(); /* Init output LED GPIO. */
GPIO_PortInit(GPIO, BOARD_LED_PORT);
GPIO_PinInit(GPIO, BOARD_LED_PORT, BOARD_LED_PIN, &led_config); /* Set systick reload value to generate 1ms interrupt */
if (SysTick_Config(SystemCoreClock / 1000U))
{
while ()
{
}
} while ()
{
/* Delay 1000 ms */
SysTick_DelayTicks(1000U);
GPIO_PortToggle(GPIO, BOARD_LED_PORT, 1u << BOARD_LED_PIN);
}
}
基本上,代码正在初始化引脚,时钟,设置SysTick定时器,然后在循环中执行'blinky',使用Systick计数器延迟闪烁周期。
4、调试控制台
但我可以看到它初始化一个调试控制台(以及它的UART硬件):
BOARD_InitDebugConsole();
去掉这些,我们就可以得到:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
5616 B |
64 KB |
8.57% |
|
SRAM: |
2400 B |
16 KB |
14.65% |
在许多情况下,演示应用程序会设置一些通信通道,但之后就不会使用它们。链接器可以很好地删除未使用的对象(函数/变量),但前提是它们没有被引用。
5、半主控和printf()
接下来要看的是是否存在任何半主机或printf()。该项目正在使用'Redlib',这是一个优化的库,与'标准'newlib或较小标准的newlib-nano相比:

Redlib
尽管如此,该库可能会增加代码大小,因为它使用半主机(通过调试器发送消息)。查看Memory视图,我可以直接或间接地看到所需的所有这些标准I / O函数:

stdio功能
拥有该功能的所有钩子只有在使用它时才有意义,并且“blinky”不会使用它。因此,摆脱半主机和所有未使用的标准I / O意味着使用'none'变体:

没有标准I / O的库
这让我们了解到这一点:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
3372 B |
64 KB |
5.15% |
|
SRAM: |
2208 B |
16 KB |
13.48% |
或者使用较小的变体或实现。有关此问题的更多背景信息,请参阅本文末尾的链接。
6、DEBUG和NDEBUG
接下来要检查编译器是否定义了列出的DEBUG。事实上,情况就是这样:

DEBUG定义
使用该定义集,SDK和示例驱动程序中有许多额外的代码,它们使用'assert()'宏检查好的值:

SDK代码中断言的用法
在这里,图像信息视图再次有用:它向我展示了使用assert()的所有地方:

断言用法
实际上,在代码中使用断言来尽早捕获编程错误是一种很好的做法。但是所有的assert()代码确实加起来了。要关闭额外的代码(和安全带!),我将宏更改为NDEBUG:

NDEBUG
这让我们了解到一点:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
3144 B |
64 KB |
4.80% |
|
SRAM: |
2208 B |
16 KB |
13.48% |
7、中断和向量
图像信息视图再次是一个很好的起点。我正在检查使用过的中断。Blinky正在使用预期的SysTick中断。但是仍然使用UART中断?

使用中断
大多数中断都实现为“weak”:实现为默认/空,可以被应用程序覆盖。但UART没有意义,因为”blinky”没有使用任何UART通信?
事实证明,NXP SDK默认启用了UART事务API:

UART Transactional API设置
事务API允许在通信组块/事务中发送/接收UART数据。但我们不需要在我们的眨眼中,所以让我们把它关掉:

关闭UART Transactional API
这样一来,内存情况为:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
2964 B |
64 KB |
4.52% |
|
SRAM: |
2184 B |
16 KB |
13.33% |
但我认为CMSIS(设置中断优先级,通用时钟设置)非常有用,所以我不在这里触摸它。应用程序中最大的功能是SysTick代码用来将定时器的优先级设置为最低优先级,以节省另外220个字节:

CMSIS作为最大的单一功能代码大小贡献者
8、优化
到目前为止,我已经删除了不需要的或未使用的功能。接下来我可以打开编译器优化。默认情况下,项目设置为-O0:

编译器优化
-O0表示无优化:代码直观且易于调试。
-O1主要优化函数进入/退出代码,并且能够在不影响调试的情况下减少代码大小。在这个例子中,它将代码大小减少了一半!
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1540 B |
64 KB |
2.35% |
|
SRAM: |
2184 B |
16 KB |
13.33% |
-O2优化更多并尽可能地将事物保存在寄存器中。因为应用程序中的功能相当小,所以改进并不大:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1516 B |
64 KB |
2.31% |
|
SRAM: |
2184 B |
16 KB |
13.33% |
-O3通过额外的内联优化最佳。-O3的目标是速度,所以难怪代码大小再次增加:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1792 B |
64 KB |
2.73% |
|
SRAM: |
2184 B |
16 KB |
13.33% |
代码大小优化的最佳选择是-Os(针对大小进行优化):
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1456 B |
64 KB |
2.22% |
|
SRAM: |
2184 B |
16 KB |
13.33% |
现在看起来很合理!当然现在有一些方法可以为“裸露的裸眼”切断更多,但是现有的一切(启动代码,时钟和GPIO初始化)对于真正的应用程序是有意义的,所以我现在停在这里。
9、RAM:堆和堆栈
看起来不正确的是SRAM的使用。'heap'使用了一大块:

堆内存使用情况
该堆用于动态内存分配(malloc())。嵌入式编程的一般规则是避免它。但它默认在这里。它可以在链接器设置中关闭:演示使用1K用于堆和堆栈。由于我没有使用malloc(),我可以将堆大小设置为0x0。对于真正依赖于应用程序的保留堆栈。在ARM Cortex上,MSP用于启动/主控和中断(参见“ ARM Cortex-M中断和FreeRTOS ”)。0x100(256字节)应该足够我的眨眼。

堆和堆栈大小
这让我了解到一点:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1456 B |
64 KB |
2.22% |
|
SRAM: |
392 B |
16 KB |
2.39% |
如果它是关于进一步减小堆栈大小,我可以查看调用图信息,它给出了有关使用多少堆栈空间的信息:

堆栈大小的图形显示
有一些项目的大小信息未知(标有“?”)因为它们在库中。验证实际堆栈使用情况的方法是编写模式(例如0xffff'ffff),然后运行应用程序一段时间:

使用的堆栈
这表明实际使用了72个字节。有一点余地,在这种情况下将堆栈大小设置为128字节看起来是合理的。这给出了:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1456 B |
64 KB |
2.22% |
|
SRAM: |
264 B |
16 KB |
1.61% |
堆栈溢出可能是嵌入式应用程序中最常见的问题。如果可以的话,可以为堆栈提供尽可能多的RAM。如果缩小尺寸,请确保进行了足够的分析以证明堆叠尺寸合理。
10、MTB
剩下的一件事就是使用RAM空间:MTB缓冲区。微跟踪缓冲区用于跟踪,这非常有用(请参阅“ 使用MTB跟踪调试ARM Cortex-M0 +硬故障 ”)。可以使用宏禁用缓冲区:

mtb.c

__MTB_DISABLE
这让我对此:
|
Memory region |
Used Size |
Region Size |
%age Used |
|
PROGRAM_FLASH: |
1456 B |
64 KB |
2.22% |
|
SRAM: |
136 B |
16 KB |
0.83% |
我想在这里我们可以很开心
11、摘要
供应商的例子很棒:它们给了我一个很好的起点。它们没有经过优化,这是故意的。但它们可能带有我不需要的功能和功能。了解使用切断功能或调整设置来优化应用程序的不同方法对于优化RAM和FLASH使用非常有用。在本教程中,我展示了如何将'blinky'降低到大约1KB闪存和大约136字节的SRAM。当然这一切都取决于功能和用法,但我认为现在为我的应用程序添加额外的功能是一个非常合理的状态。
我希望这些提示可能对您的项目有用。
12、链接
- 文本,数据和bss:代码和数据大小说明
- 拆箱恩智浦LPC845-BRK板
- 教程:使用恩智浦LPC845-BRK主板闪烁
- 使用恩智浦Kinetis SDK V2.0进行半主机(再次!)
- 为什么我不喜欢printf()
- XFormat,轻量级printf()和sprintf()替代品
- 优化Kinetis gcc启动
- 新的恩智浦MCUXpresso Eclipse IDE v11.0
声明: 此篇由 Erich Styger的《Tutorial: How to Optimize Code and RAM Size》翻译。原文地址为:https://mcuoneclipse.com/2019/08/17/tutorial-how-to-optimize-code-and-ram-size/。权属归原作者所有。
欢迎关注:

如何优化代码和RAM大小的更多相关文章
- 为duilib的MenuDemo增加消息响应,优化代码和显示效果
转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/38253297 第一部分 我在前一段时间研究了怎么制作duilib的菜单, ...
- Keil C减小代码编译量大小的方法(gai)
keil-C减小代码编译大小的方法整理 方法一:(通过优化代码减小) 1.1少做乘除运算,使用左/右移位来实现乘除 Eg ,普通:a = 0x80*4: 优化:a = 0x80<<2: 1 ...
- Webpack 4教程:为什么要优化代码
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/07/30/webpack-4-course-part ...
- Android 性能优化:使用 Lint 优化代码、去除多余资源
前言 在保证代码没有功能问题,完成业务开发之余,有追求的程序员还要追求代码的规范.可维护性. 今天,以“成为优秀的程序员”为目标的拭心将和大家一起精益求精,学习使用 Lint 优化我们的代码. 什么是 ...
- 如何优化代码中大量的if/else,switch/case?
前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单 ...
- JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍
概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...
- 效率包括了代码的GC 大小与内存大小,执行速度等等。其中执行速度不是关注 的重点
效率包括了代码的GC 大小与内存大小,执行速度等等.其中执行速度不是关注的重点
- JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧
概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...
- 通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数--菜单功能'menufile
通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁 ...
随机推荐
- CVE-2019-0708:RDP终极EXP复现
0x00 前言 每次复现都会遇到各种各样的问题,这次也不例外,经过多次尝试才复现成功,因此把可能的解决方法也和大家分享一下,有想要一起复现学习/投稿的可以联系我哈 0x01 影响版本 Windows ...
- SQL进阶系列之4HAVING字句的力量
写在前面 SQL是面向集合的语言,与面向过程和面向对象语言都不一样 寻找缺失的编号 /* 寻找缺失的编号 */ CREATE TABLE SeqTbl (seq INTEGER PRIMARY KEY ...
- HDU4814——数学,模拟进制转换
本题围绕:数学公式模拟进制转换 HDU4814 Golden Radio Base 题目描述 将一个十进制的非负整数转换成E(黄金分割数)进制的数 输入 不大于10^9的非负整数,处理到文件尾 输出 ...
- 《基于 Web Service 的学分制教务管理系统的研究与实现》论文笔记(十一)
标题:基于 Web Service 的学分制教务管理系统的研究与实现 一.基本内容 时间:2014 来源:苏州大学 关键词:: 教务管理系统 学分制 Web Service 二.研究内容 1.教务管理 ...
- python 批量打印PDF
有一批PDF文件,好几百个,每个只打印第2,3页,双面打印. 网上搜索一波,方案如下: 安装Ghostscript,GhostView,使用gsprint命令打印pdf文件. gsprint命令参数说 ...
- AST11103 Problem Solving
AST11103 Problem Solving with Programming SkillsAdditional Individual Assignment: Min-Game Programmi ...
- linux 读取文件
linux读取文件是经常要用到的操作,以下示例(说明看注释): #读取文件snlist.txt中的每一行内容赋给sn变量 while read sn do echo ">>> ...
- Problem I. Wiki with Special Poker Cards
Problem I. Wiki with Special Poker CardsInput file: standard input Time limit: 1 secondOutput file: ...
- 是Mscoreei.dll的正确版本吗?
在安装.NET 4.0或更高版本之后,您可能会注意到.NET进程有点不寻常.下面是用.NET 2.0编译器编译的简单“Hello World”可执行文件的加载模块的部分列表. 开始-结束模块名称 60 ...
- 防火墙firewalld
增加外部可访问的端口 启动: systemctl start firewalld 查看状态: systemctl status firewalld 停止: systemctl stop firewal ...