如何优化代码和RAM大小
如果供應(yīng)商為我自己的項(xiàng)目提供了一個(gè)起點(diǎn),那就太好了。工作'blinky'始終是一個(gè)偉大的首發(fā)。方便總是有代價(jià),而且“blinky”就是夸大“切換GPIO引腳”的代碼大小。對(duì)于具有少量RAM和FLASH的設(shè)備,這可能會(huì)引起關(guān)注:如果'blinky'占用那么多,我的應(yīng)用程序是否適合該設(shè)備?不要擔(dān)心:可以輕松地修剪掉(或任何其他項(xiàng)目)。
恩智浦LPC845-BRK主板上的Binky
我在這里使用一個(gè)'blinky'項(xiàng)目作為一個(gè)例子:修剪技巧也適用于任何其他類(lèi)型的項(xiàng)目。
在本教程中,我在BRK(突破)板上使用NXPLPC845:
恩智浦LPC845-BRK板
1、Blinky示例
我所使用的是基于Eclipse的NXP MCUXpresso IDE:
選擇SDK板
我使用供應(yīng)商默認(rèn)設(shè)置創(chuàng)建了'blinky'項(xiàng)目:
Blinky項(xiàng)目
一個(gè)'blinky'應(yīng)該閃爍一個(gè)LED,對(duì)任何項(xiàng)目來(lái)說(shuō)都是一個(gè)好的開(kāi)手機(jī)。構(gòu)建相當(dāng)小的項(xiàng)目,代碼大小如下:
| 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 |
該信息也在控制臺(tái)中顯示,分為文本,數(shù)據(jù)和bss:
10K的'blinky'看起來(lái)有點(diǎn)夸張。但是我們現(xiàn)在將在接下來(lái)的步驟中修改它。
2、大小信息
有關(guān)大小信息的含義,請(qǐng)閱讀“?text,data和bss:Code and Data Size Explained?”。查看我的設(shè)備上使用空間的正常方法是檢查鏈接器映射文件(* .map):
鏈接器映射文件
但是這個(gè)map文件很難閱讀,而且對(duì)于專(zhuān)家來(lái)說(shuō)更是如此:它列出了具有地址和大小的部分:
鏈接器映射文件內(nèi)容
使用MCUXpresso IDE V11,有一個(gè)很好的“圖像信息”視圖,它基本上是一個(gè)更好的ma'p文件信息查看器:
圖像信息查看
我可以過(guò)濾和排序數(shù)據(jù),這讓我知道代碼和數(shù)據(jù)使用了多少空間:
圖像信息存儲(chǔ)器內(nèi)容
當(dāng)然,它需要一些關(guān)于應(yīng)用程序應(yīng)該做什么的知識(shí)。我總是瀏覽視圖中的項(xiàng)目列表,看看是否有任何我不希望的東西:也許應(yīng)用程序正在使用可以刪除的東西。
3、源代碼
對(duì)于一個(gè)簡(jiǎn)單的眨眼,這是相當(dāng)小的。首先要檢查程序正在做什么。main.c有這個(gè):
/* * 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,0,};/* 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 (1){}}while (1){/* Delay 1000 ms */SysTick_DelayTicks(1000U);GPIO_PortToggle(GPIO, BOARD_LED_PORT, 1u << BOARD_LED_PIN);} }基本上,代碼正在初始化引腳,時(shí)鐘,設(shè)置SysTick定時(shí)器,然后在循環(huán)中執(zhí)行'blinky',使用Systick計(jì)數(shù)器延遲閃爍周期。
4、調(diào)試控制臺(tái)
但我可以看到它初始化一個(gè)調(diào)試控制臺(tái)(以及它的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% |
在許多情況下,演示應(yīng)用程序會(huì)設(shè)置一些通信通道,但之后就不會(huì)使用它們。鏈接器可以很好地刪除未使用的對(duì)象(函數(shù)/變量),但前提是它們沒(méi)有被引用。
5、半主控和printf()
接下來(lái)要看的是是否存在任何半主機(jī)或printf()。該項(xiàng)目正在使用'Redlib',這是一個(gè)優(yōu)化的庫(kù),與'標(biāo)準(zhǔn)'newlib或較小標(biāo)準(zhǔn)的newlib-nano相比:
Redlib
盡管如此,該庫(kù)可能會(huì)增加代碼大小,因?yàn)樗褂冒胫鳈C(jī)(通過(guò)調(diào)試器發(fā)送消息)。查看Memory視圖,我可以直接或間接地看到所需的所有這些標(biāo)準(zhǔn)I / O函數(shù):
stdio功能
擁有該功能的所有鉤子只有在使用它時(shí)才有意義,并且“blinky”不會(huì)使用它。因此,擺脫半主機(jī)和所有未使用的標(biāo)準(zhǔn)I / O意味著使用'none'變體:
沒(méi)有標(biāo)準(zhǔn)I / O的庫(kù)
這讓我們了解到這一點(diǎn):
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 3372 B | 64 KB | 5.15% |
| SRAM: | 2208 B | 16 KB | 13.48% |
?
或者使用較小的變體或?qū)崿F(xiàn)。有關(guān)此問(wèn)題的更多背景信息,請(qǐng)參閱本文末尾的鏈接。
6、DEBUG和NDEBUG
接下來(lái)要檢查編譯器是否定義了列出的DEBUG。事實(shí)上,情況就是這樣:
DEBUG定義
使用該定義集,SDK和示例驅(qū)動(dòng)程序中有許多額外的代碼,它們使用'assert()'宏檢查好的值:
SDK代碼中斷言的用法
在這里,圖像信息視圖再次有用:它向我展示了使用assert()的所有地方:
斷言用法
實(shí)際上,在代碼中使用斷言來(lái)盡早捕獲編程錯(cuò)誤是一種很好的做法。但是所有的assert()代碼確實(shí)加起來(lái)了。要關(guān)閉額外的代碼(和安全帶!),我將宏更改為NDEBUG:
NDEBUG
這讓我們了解到一點(diǎn):
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 3144 B | 64 KB | 4.80% |
| SRAM: | 2208 B | 16 KB | 13.48% |
7、中斷和向量
圖像信息視圖再次是一個(gè)很好的起點(diǎn)。我正在檢查使用過(guò)的中斷。Blinky正在使用預(yù)期的SysTick中斷。但是仍然使用UART中斷?
使用中斷
大多數(shù)中斷都實(shí)現(xiàn)為“weak”:實(shí)現(xiàn)為默認(rèn)/空,可以被應(yīng)用程序覆蓋。但UART沒(méi)有意義,因?yàn)椤眀linky”沒(méi)有使用任何UART通信?
事實(shí)證明,NXP SDK默認(rèn)啟用了UART事務(wù)API:
UART Transactional API設(shè)置
事務(wù)API允許在通信組塊/事務(wù)中發(fā)送/接收UART數(shù)據(jù)。但我們不需要在我們的眨眼中,所以讓我們把它關(guān)掉:
關(guān)閉UART TransactionalAPI
這樣一來(lái),內(nèi)存情況為:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 2964 B | 64 KB | 4.52% |
| SRAM: | 2184 B | 16 KB | 13.33% |
但我認(rèn)為CMSIS(設(shè)置中斷優(yōu)先級(jí),通用時(shí)鐘設(shè)置)非常有用,所以我不在這里觸摸它。應(yīng)用程序中最大的功能是SysTick代碼用來(lái)將定時(shí)器的優(yōu)先級(jí)設(shè)置為最低優(yōu)先級(jí),以節(jié)省另外220個(gè)字節(jié):
CMSIS作為最大的單一功能代碼大小貢獻(xiàn)者
8、優(yōu)化
到目前為止,我已經(jīng)刪除了不需要的或未使用的功能。接下來(lái)我可以打開(kāi)編譯器優(yōu)化。默認(rèn)情況下,項(xiàng)目設(shè)置為-O0:
編譯器優(yōu)化
-O0表示無(wú)優(yōu)化:代碼直觀且易于調(diào)試。
-O1主要優(yōu)化函數(shù)進(jìn)入/退出代碼,并且能夠在不影響調(diào)試的情況下減少代碼大小。在這個(gè)例子中,它將代碼大小減少了一半!
?
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1540 B | 64 KB | 2.35% |
| SRAM: | 2184 B | 16 KB | 13.33% |
-O2優(yōu)化更多并盡可能地將事物保存在寄存器中。因?yàn)閼?yīng)用程序中的功能相當(dāng)小,所以改進(jìn)并不大:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1516 B | 64 KB | 2.31% |
| SRAM: | 2184 B | 16 KB | 13.33% |
-O3通過(guò)額外的內(nèi)聯(lián)優(yōu)化最佳。-O3的目標(biāo)是速度,所以難怪代碼大小再次增加:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1792 B | 64 KB | 2.73% |
| SRAM: | 2184 B | 16 KB | 13.33% |
代碼大小優(yōu)化的最佳選擇是-Os(針對(duì)大小進(jìn)行優(yōu)化):
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 2184 B | 16 KB | 13.33% |
現(xiàn)在看起來(lái)很合理!當(dāng)然現(xiàn)在有一些方法可以為“裸露的裸眼”切斷更多,但是現(xiàn)有的一切(啟動(dòng)代碼,時(shí)鐘和GPIO初始化)對(duì)于真正的應(yīng)用程序是有意義的,所以我現(xiàn)在停在這里。
9、RAM:堆和堆棧
看起來(lái)不正確的是SRAM的使用。'heap'使用了一大塊:
堆內(nèi)存使用情況
該堆用于動(dòng)態(tài)內(nèi)存分配(malloc())。嵌入式編程的一般規(guī)則是避免它。但它默認(rèn)在這里。它可以在鏈接器設(shè)置中關(guān)閉:演示使用1K用于堆和堆棧。由于我沒(méi)有使用malloc(),我可以將堆大小設(shè)置為0x0。對(duì)于真正依賴于應(yīng)用程序的保留堆棧。在ARM Cortex上,MSP用于啟動(dòng)/主控和中斷(參見(jiàn)“?ARMCortex-M中斷和FreeRTOS?”)。0x100(256字節(jié))應(yīng)該足夠我的眨眼。
堆和堆棧大小
這讓我了解到一點(diǎn):
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 392 B | 16 KB | 2.39% |
如果它是關(guān)于進(jìn)一步減小堆棧大小,我可以查看調(diào)用圖信息,它給出了有關(guān)使用多少堆棧空間的信息:
堆棧大小的圖形顯示
有一些項(xiàng)目的大小信息未知(標(biāo)有“?”)因?yàn)樗鼈冊(cè)趲?kù)中。驗(yàn)證實(shí)際堆棧使用情況的方法是編寫(xiě)模式(例如0xffff'ffff),然后運(yùn)行應(yīng)用程序一段時(shí)間:
使用的堆棧
這表明實(shí)際使用了72個(gè)字節(jié)。有一點(diǎn)余地,在這種情況下將堆棧大小設(shè)置為128字節(jié)看起來(lái)是合理的。這給出了:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 264 B | 16 KB | 1.61% |
堆棧溢出可能是嵌入式應(yīng)用程序中最常見(jiàn)的問(wèn)題。如果可以的話,可以為堆棧提供盡可能多的RAM。如果縮小尺寸,請(qǐng)確保進(jìn)行了足夠的分析以證明堆疊尺寸合理。
10、MTB
剩下的一件事就是使用RAM空間:MTB緩沖區(qū)。微跟蹤緩沖區(qū)用于跟蹤,這非常有用(請(qǐng)參閱“?使用MTB跟蹤調(diào)試ARM Cortex-M0 +硬故障?”)。可以使用宏禁用緩沖區(qū):
mtb.c
__MTB_DISABLE
這讓我對(duì)此:
| Memory region | Used Size | Region Size | %age Used |
| PROGRAM_FLASH: | 1456 B | 64 KB | 2.22% |
| SRAM: | 136 B | 16 KB | 0.83% |
我想在這里我們可以很開(kāi)心
11、摘要
供應(yīng)商的例子很棒:它們給了我一個(gè)很好的起點(diǎn)。它們沒(méi)有經(jīng)過(guò)優(yōu)化,這是故意的。但它們可能帶有我不需要的功能和功能。了解使用切斷功能或調(diào)整設(shè)置來(lái)優(yōu)化應(yīng)用程序的不同方法對(duì)于優(yōu)化RAM和FLASH使用非常有用。在本教程中,我展示了如何將'blinky'降低到大約1KB閃存和大約136字節(jié)的SRAM。當(dāng)然這一切都取決于功能和用法,但我認(rèn)為現(xiàn)在為我的應(yīng)用程序添加額外的功能是一個(gè)非常合理的狀態(tài)。
我希望這些提示可能對(duì)您的項(xiàng)目有用。
12、鏈接
- 文本,數(shù)據(jù)和bss:代碼和數(shù)據(jù)大小說(shuō)明
- 拆箱恩智浦LPC845-BRK板
- 教程:使用恩智浦LPC845-BRK主板閃爍
- 使用恩智浦Kinetis SDK V2.0進(jìn)行半主機(jī)(再次!)
- 為什么我不喜歡printf()
- XFormat,輕量級(jí)printf()和sprintf()替代品
- 優(yōu)化Kinetis gcc啟動(dòng)
- 新的恩智浦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/。權(quán)屬歸原作者所有。
歡迎關(guān)注:
總結(jié)
以上是生活随笔為你收集整理的如何优化代码和RAM大小的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c语言around用法,KET基础语法:
- 下一篇: 滤波器开发之一:基于算数平均的平滑滤波器