ESP 内存占用优化

[English]

引言

受限于嵌入式芯片的硬件资源,在运行复杂应用代码时可能会出现内存不足的情况。ESP-IDF 中的大部分默认配置项为性能优先取向,在实际产品中可以通过合理的内存配置,在性能与内存占用间取得较好的平衡。

警告

本文档所列的部分内存占用优化方法可能会降低系统性能及稳定性,在进行内存占用优化后应当进行充分的性能与稳定性测试,以确保满足应用需求。

获取当前剩余内存

在进行内存优化前,需要首先了解当前的内存占用情况,对于静态内存占用,可以在编译后使用 idf.py sizeidf.py size-components 命令对内存进行统计。

对于运行时内存占用,通过 xPortGetFreeHeapSize()xPortGetMinimumEverFreeHeapSize() 函数可以分别获得系统当前的最小内存和自系统启动以来的最小剩余堆内存大小。

如果需要更精细的分配内存,可以开启配置项 CONFIG_FREERTOS_USE_TRACE_FACILITYCONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS ,然后使用 vTaskList() 函数获取每个任务栈的高水位线。

可选的优化项

  1. Wi-Fi 内存优化

Wi-Fi 的性能受许多参数的影响,各参数之间存在相互制约。如果配置合理不仅可以提高性能,还可以增加应用程序的可用内存,提高稳定性。对于不同的芯片所适用的配置项存在较大差异,关于 Wi-Fi 内存优化参数配置与吞吐请根据具体芯片型号查看 如何提高 Wi-Fi 性能

  1. LwIP 内存优化

由于 RAM 按需从堆中分配,多数 lwIP 的 RAM 使用也按需分配。因此,更改 lwIP 设置减少 RAM 使用时,或许不会改变空闲时的 RAM 使用量,但可以改变高峰期的 RAM 使用量。关于 LwIP 部分内存优化请查看 LwIP 最小内存使用

  1. Mbed TLS 内存优化

Mbed TLS 是一个 C 代码库,用于实现加密基元、X.509 证书操作以及 SSL/TLS 和 DTLS 协议。当应用代码使用 Mbed 库进行加解密时,可以对配置进行修改以降低内存占用。关于 Mbed 部分内存优化请查看 Mbed 性能和内存调整

  1. BLE 内存优化

相较于 Bluedroid, NimBLE 占用更少的内存和固件尺寸,因此在内存较为紧张的应用中,推荐使用 NimBLE 协议栈。进一步的,可以禁用部分功能并调小缓冲区取得最佳的内存占用。对于仅使用 BLE 配网的场景,尝试进行如下配置:

减少最大连接数量以及相关参数:

  • CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1

  • CONFIG_BT_NIMBLE_MAX_BONDS=2

  • CONFIG_BT_NIMBLE_MAX_CCCDS=2

  • CONFIG_BT_NIMBLE_WHITELIST_SIZE=2

如果对蓝牙吞吐速率的要求不高,可以调小缓冲区尺寸和个数:

  • CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=12

  • CONFIG_BT_NIMBLE_MSYS_1_BLOCK_SIZE=100

  • CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=4

  • CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=12

  • CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=5

  • CONFIG_BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT=3

选择性的关闭不需要的功能:

  • CONFIG_BT_NIMBLE_ROLE_CENTRAL=n

  • CONFIG_BT_NIMBLE_ROLE_OBSERVER=n

  • CONFIG_BT_NIMBLE_SECURITY_ENABLE=n

  1. 其余配置项优化

出于提高性能和降低功耗的目的,部分函数默认放置在 IRAM 中,在必要情况下可以通过配置项将这部分函数重新移回 Flash 中:

  • CONFIG_SPI_SLAVE_ISR_IN_IRAM=n

  • CONFIG_ESP_WIFI_IRAM_OPT=n

  • CONFIG_ESP_WIFI_RX_IRAM_OPT=n

  • CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y

  • CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y

  • CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y

减小一些系统队列的长度配置项:

  • CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=16

开启 nano 格式选项:

  • NEWLIB_NANO_FORMAT=y

关闭不需要的功能:

  • CONFIG_WS_TRANSPORT=n

  1. 减小任务栈尺寸

在 ESP-IDF 默认的配置下,任务栈均保留了较大的裕量,一则是为了兼容不同的芯片,二则是为了方便用户添加应用代码。适当减小任务栈裕量可以让出内存供动态分配使用。

以下是一些常用的任务栈配置项:

  • CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE

  • CONFIG_BT_LE_CONTROLLER_TASK_STACK_SIZE

  • CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE

  • CONFIG_ESP_TIMER_TASK_STACK_SIZE

  • CONFIG_FREERTOS_IDLE_TASK_STACKSIZE

  • CONFIG_LWIP_TCPIP_TASK_STACK_SIZE

优化示例及结果统计

bleprph_wifi_coex 示例 测试优化效果,比对优化前后 Wi-Fi 与 BLE 均连接后的内存。

  1. 硬软件环境

  1. 配置项修改

  • CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y

  • CONFIG_EXAMPLE_ESP_PING_COUNT=5

  • CONFIG_BT_ENABLED=y

  • CONFIG_BT_NIMBLE_ENABLED=y

  • CONFIG_BT_NIMBLE_MAX_BONDS=2

  • CONFIG_BT_NIMBLE_MAX_CCCDS=2

  • CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=2048

  • CONFIG_BT_NIMBLE_ROLE_CENTRAL=n

  • CONFIG_BT_NIMBLE_ROLE_OBSERVER=n

  • CONFIG_BT_NIMBLE_SECURITY_ENABLE=n

  • CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=12

  • CONFIG_BT_NIMBLE_MSYS_1_BLOCK_SIZE=100

  • CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=4

  • CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=12

  • CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=5

  • CONFIG_BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT=3

  • CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n

  • CONFIG_BT_NIMBLE_WHITELIST_SIZE=2

  • CONFIG_BT_LE_CONTROLLER_TASK_STACK_SIZE=2048

  • CONFIG_ESP_COEX_SW_COEXIST_ENABLE=n

  • CONFIG_ESP_EVENT_POST_FROM_ISR=n

  • CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=n

  • CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y

  • CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y

  • CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=16

  • CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=1536

  • CONFIG_ESP_TIMER_TASK_STACK_SIZE=2048

  • CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=3

  • CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=6

  • CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=6

  • CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=n

  • CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=n

  • CONFIG_ESP_WIFI_IRAM_OPT=n

  • CONFIG_ESP_WIFI_RX_IRAM_OPT=n

  • CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=n

  • CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n

  • CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT=y

  • CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0

  • CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=n

  • CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=768

  • CONFIG_FREERTOS_USE_TRACE_FACILITY=y

  • CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y

  • CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y

  • CONFIG_LWIP_L2_TO_L3_COPY=y

  • CONFIG_LWIP_MAX_SOCKETS=8

  • CONFIG_LWIP_SO_LINGER=y

  • CONFIG_LWIP_SO_RCVBUF=y

  • CONFIG_LWIP_NETBUF_RECVINFO=y

  • CONFIG_LWIP_GARP_TMR_INTERVAL=30

  • CONFIG_LWIP_MLDV6_TMR_INTERVAL=30

  • CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=16

  • CONFIG_LWIP_DHCP_RESTORE_LAST_IP=y

  • CONFIG_LWIP_DHCPS=n

  • CONFIG_LWIP_IPV6_NUM_ADDRESSES=6

  • CONFIG_LWIP_IPV6_FORWARD=y

  • CONFIG_LWIP_TCP_SND_BUF_DEFAULT=6144

  • CONFIG_LWIP_TCP_SACK_OUT=y

  • CONFIG_LWIP_MAX_UDP_PCBS=24

  • CONFIG_LWIP_UDP_RECVMBOX_SIZE=12

  • CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048

  • CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=6

  • CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=8

  • CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=6288

  • CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=2048

  • CONFIG_MBEDTLS_DYNAMIC_BUFFER=y

  • CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y

  • CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=n

  • CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n

  • CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT=y

  1. 优化结果

下表统计了优化前后的系统剩余内存,统计结果表明,通过配置项优化可以节省内存约 60 KB。

剩余内存

优化前

优化后

差值

当前剩余(字节)

33232

95040

61808

最小剩余(字节)

25804

91252

65448