[esp32] Move heap functions to flash, saving ~6KB

This is the culmination of months of work to reduce heap churn
throughout the ESPHome codebase. By systematically eliminating
unnecessary dynamic allocations (StaticVector, FixedVector, const
char* instead of std::string, pre-allocated buffers, etc.), heap
functions are now called so infrequently that they can safely be
moved from IRAM to flash.

Enable CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH by default, which moves
malloc/free/realloc from IRAM to flash. This is safe because:
- Heap functions should never be called from ISRs
- CONFIG_SPI_MASTER_ISR_IN_IRAM is not enabled
- Audio/video use pre-allocated ring buffers, not dynamic allocation

Measured results: +6,124 bytes of heap freed.

Add heap_in_iram advanced option as an escape hatch for users who
need heap functions in IRAM for specific use cases.
This commit is contained in:
J. Nick Koston
2026-01-03 15:09:43 -10:00
parent f11abc7dbf
commit ba1bbaf67d

View File

@@ -644,6 +644,7 @@ CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select"
CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir"
CONF_FREERTOS_IN_IRAM = "freertos_in_iram"
CONF_RINGBUF_IN_IRAM = "ringbuf_in_iram"
CONF_HEAP_IN_IRAM = "heap_in_iram"
CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
# VFS requirement tracking
@@ -745,6 +746,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean,
cv.Optional(CONF_FREERTOS_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_RINGBUF_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_HEAP_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_EXECUTE_FROM_PSRAM, default=False): cv.boolean,
cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range(
min=8192, max=32768
@@ -1090,6 +1092,12 @@ async def to_code(config):
# Place in flash to save IRAM (default)
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH", True)
# Place heap functions into flash to save IRAM (~4-6KB savings)
# Safe as long as heap functions are not called from ISRs (which they shouldn't be)
# Users can set heap_in_iram: true as an escape hatch if needed
if not conf[CONF_ADVANCED][CONF_HEAP_IN_IRAM]:
add_idf_sdkconfig_option("CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH", True)
# Setup watchdog
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT", True)
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_PANIC", True)