mirror of
https://github.com/esphome/esphome.git
synced 2025-06-15 06:46:59 +02:00
Reserve memory for component and platform vectors (#9042)
This commit is contained in:
parent
a488c8cd5c
commit
3411e45a0a
@ -235,6 +235,7 @@ async def register_alarm_control_panel(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_alarm_control_panel(var))
|
||||
CORE.register_platform_component("alarm_control_panel", var)
|
||||
await setup_alarm_control_panel_core_(var, config)
|
||||
|
||||
|
||||
|
@ -554,6 +554,7 @@ async def register_binary_sensor(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_binary_sensor(var))
|
||||
CORE.register_platform_component("binary_sensor", var)
|
||||
await setup_binary_sensor_core_(var, config)
|
||||
|
||||
|
||||
|
@ -108,6 +108,7 @@ async def register_button(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_button(var))
|
||||
CORE.register_platform_component("button", var)
|
||||
await setup_button_core_(var, config)
|
||||
|
||||
|
||||
|
@ -443,6 +443,7 @@ async def register_climate(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_climate(var))
|
||||
CORE.register_platform_component("climate", var)
|
||||
await setup_climate_core_(var, config)
|
||||
|
||||
|
||||
|
@ -189,6 +189,7 @@ async def register_cover(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_cover(var))
|
||||
CORE.register_platform_component("cover", var)
|
||||
await setup_cover_core_(var, config)
|
||||
|
||||
|
||||
|
@ -158,7 +158,9 @@ async def setup_datetime_core_(var, config):
|
||||
async def register_datetime(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(getattr(cg.App, f"register_{config[CONF_TYPE].lower()}")(var))
|
||||
entity_type = config[CONF_TYPE].lower()
|
||||
cg.add(getattr(cg.App, f"register_{entity_type}")(var))
|
||||
CORE.register_platform_component(entity_type, var)
|
||||
await setup_datetime_core_(var, config)
|
||||
cg.add_define(f"USE_DATETIME_{config[CONF_TYPE]}")
|
||||
|
||||
|
@ -113,6 +113,7 @@ async def register_event(var, config, *, event_types: list[str]):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_event(var))
|
||||
CORE.register_platform_component("event", var)
|
||||
await setup_event_core_(var, config, event_types=event_types)
|
||||
|
||||
|
||||
|
@ -296,6 +296,7 @@ async def register_fan(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_fan(var))
|
||||
CORE.register_platform_component("fan", var)
|
||||
await setup_fan_core_(var, config)
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ from esphome.const import (
|
||||
CONF_WEB_SERVER,
|
||||
CONF_WHITE,
|
||||
)
|
||||
from esphome.core import coroutine_with_priority
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
|
||||
@ -270,6 +270,7 @@ async def setup_light_core_(light_var, output_var, config):
|
||||
async def register_light(output_var, config):
|
||||
light_var = cg.new_Pvariable(config[CONF_ID], output_var)
|
||||
cg.add(cg.App.register_light(light_var))
|
||||
CORE.register_platform_component("light", light_var)
|
||||
await cg.register_component(light_var, config)
|
||||
await setup_light_core_(light_var, output_var, config)
|
||||
|
||||
|
@ -115,6 +115,7 @@ async def register_lock(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_lock(var))
|
||||
CORE.register_platform_component("lock", var)
|
||||
await _setup_lock_core(var, config)
|
||||
|
||||
|
||||
|
@ -103,6 +103,7 @@ async def register_media_player(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_media_player(var))
|
||||
CORE.register_platform_component("media_player", var)
|
||||
await setup_media_player_core_(var, config)
|
||||
|
||||
|
||||
|
@ -277,6 +277,7 @@ async def register_number(
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_number(var))
|
||||
CORE.register_platform_component("number", var)
|
||||
await setup_number_core_(
|
||||
var, config, min_value=min_value, max_value=max_value, step=step
|
||||
)
|
||||
|
@ -111,6 +111,7 @@ async def register_select(var, config, *, options: list[str]):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_select(var))
|
||||
CORE.register_platform_component("select", var)
|
||||
await setup_select_core_(var, config, options=options)
|
||||
|
||||
|
||||
|
@ -167,7 +167,6 @@ DEVICE_CLASSES = [
|
||||
]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
sensor_ns = cg.esphome_ns.namespace("sensor")
|
||||
StateClasses = sensor_ns.enum("StateClass")
|
||||
STATE_CLASSES = {
|
||||
@ -840,6 +839,7 @@ async def register_sensor(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_sensor(var))
|
||||
CORE.register_platform_component("sensor", var)
|
||||
await setup_sensor_core_(var, config)
|
||||
|
||||
|
||||
|
@ -159,6 +159,7 @@ async def register_switch(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_switch(var))
|
||||
CORE.register_platform_component("switch", var)
|
||||
await setup_switch_core_(var, config)
|
||||
|
||||
|
||||
|
@ -126,6 +126,7 @@ async def register_text(
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_text(var))
|
||||
CORE.register_platform_component("text", var)
|
||||
await setup_text_core_(
|
||||
var, config, min_length=min_length, max_length=max_length, pattern=pattern
|
||||
)
|
||||
|
@ -215,6 +215,7 @@ async def register_text_sensor(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_text_sensor(var))
|
||||
CORE.register_platform_component("text_sensor", var)
|
||||
await setup_text_sensor_core_(var, config)
|
||||
|
||||
|
||||
|
@ -111,6 +111,7 @@ async def register_update(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_update(var))
|
||||
CORE.register_platform_component("update", var)
|
||||
await setup_update_core_(var, config)
|
||||
|
||||
|
||||
|
@ -163,6 +163,7 @@ async def register_valve(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_valve(var))
|
||||
CORE.register_platform_component("valve", var)
|
||||
await _setup_valve_core(var, config)
|
||||
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
@ -516,6 +517,9 @@ class EsphomeCore:
|
||||
self.loaded_platforms: set[str] = set()
|
||||
# A set of component IDs to track what Component subclasses are declared
|
||||
self.component_ids = set()
|
||||
# Dict to track platform entity counts for pre-allocation
|
||||
# Key: platform name (e.g. "sensor", "binary_sensor"), Value: count
|
||||
self.platform_counts: defaultdict[str, int] = defaultdict(int)
|
||||
# Whether ESPHome was started in verbose mode
|
||||
self.verbose = False
|
||||
# Whether ESPHome was started in quiet mode
|
||||
@ -545,6 +549,7 @@ class EsphomeCore:
|
||||
self.platformio_options = {}
|
||||
self.loaded_integrations = set()
|
||||
self.component_ids = set()
|
||||
self.platform_counts = defaultdict(int)
|
||||
PIN_SCHEMA_REGISTRY.reset()
|
||||
|
||||
@property
|
||||
@ -669,16 +674,17 @@ class EsphomeCore:
|
||||
def using_esp_idf(self):
|
||||
return self.target_framework == "esp-idf"
|
||||
|
||||
def add_job(self, func, *args, **kwargs):
|
||||
def add_job(self, func, *args, **kwargs) -> None:
|
||||
self.event_loop.add_job(func, *args, **kwargs)
|
||||
|
||||
def flush_tasks(self):
|
||||
def flush_tasks(self) -> None:
|
||||
try:
|
||||
self.event_loop.flush_tasks()
|
||||
except RuntimeError as e:
|
||||
raise EsphomeError(str(e)) from e
|
||||
|
||||
def add(self, expression):
|
||||
def add(self, expression, prepend=False) -> "Statement":
|
||||
"""Add an expression or statement to the main setup() block."""
|
||||
from esphome.cpp_generator import Expression, Statement, statement
|
||||
|
||||
if isinstance(expression, Expression):
|
||||
@ -688,11 +694,14 @@ class EsphomeCore:
|
||||
f"Add '{expression}' must be expression or statement, not {type(expression)}"
|
||||
)
|
||||
|
||||
self.main_statements.append(expression)
|
||||
if prepend:
|
||||
self.main_statements.insert(0, expression)
|
||||
else:
|
||||
self.main_statements.append(expression)
|
||||
_LOGGER.debug("Adding: %s", expression)
|
||||
return expression
|
||||
|
||||
def add_global(self, expression, prepend=False):
|
||||
def add_global(self, expression, prepend=False) -> "Statement":
|
||||
from esphome.cpp_generator import Expression, Statement, statement
|
||||
|
||||
if isinstance(expression, Expression):
|
||||
@ -822,6 +831,14 @@ class EsphomeCore:
|
||||
def has_id(self, id):
|
||||
return id in self.variables
|
||||
|
||||
def register_platform_component(self, platform_name: str, var) -> None:
|
||||
"""Register a component for a platform and track its count.
|
||||
|
||||
:param platform_name: The name of the platform (e.g., 'sensor', 'binary_sensor')
|
||||
:param var: The variable (component) being registered (currently unused but kept for future use)
|
||||
"""
|
||||
self.platform_counts[platform_name] += 1
|
||||
|
||||
@property
|
||||
def cpp_main_section(self):
|
||||
from esphome.cpp_generator import statement
|
||||
|
@ -198,6 +198,73 @@ class Application {
|
||||
void register_update(update::UpdateEntity *update) { this->updates_.push_back(update); }
|
||||
#endif
|
||||
|
||||
/// Reserve space for components to avoid memory fragmentation
|
||||
void reserve_components(size_t count) { this->components_.reserve(count); }
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void reserve_binary_sensor(size_t count) { this->binary_sensors_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void reserve_switch(size_t count) { this->switches_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void reserve_button(size_t count) { this->buttons_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
void reserve_sensor(size_t count) { this->sensors_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void reserve_text_sensor(size_t count) { this->text_sensors_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
void reserve_fan(size_t count) { this->fans_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
void reserve_cover(size_t count) { this->covers_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void reserve_climate(size_t count) { this->climates_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
void reserve_light(size_t count) { this->lights_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void reserve_number(size_t count) { this->numbers_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void reserve_date(size_t count) { this->dates_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void reserve_time(size_t count) { this->times_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void reserve_datetime(size_t count) { this->datetimes_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void reserve_select(size_t count) { this->selects_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void reserve_text(size_t count) { this->texts_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
void reserve_lock(size_t count) { this->locks_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
void reserve_valve(size_t count) { this->valves_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void reserve_media_player(size_t count) { this->media_players_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void reserve_alarm_control_panel(size_t count) { this->alarm_control_panels_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
void reserve_event(size_t count) { this->events_.reserve(count); }
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
void reserve_update(size_t count) { this->updates_.reserve(count); }
|
||||
#endif
|
||||
|
||||
/// Register the component in this Application instance.
|
||||
template<class C> C *register_component(C *c) {
|
||||
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
||||
|
@ -329,6 +329,12 @@ async def _add_automations(config):
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
|
||||
@coroutine_with_priority(-100.0)
|
||||
async def _add_platform_reserves() -> None:
|
||||
for platform_name, count in sorted(CORE.platform_counts.items()):
|
||||
cg.add(cg.RawStatement(f"App.reserve_{platform_name}({count});"), prepend=True)
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
async def to_code(config):
|
||||
cg.add_global(cg.global_ns.namespace("esphome").using)
|
||||
@ -347,6 +353,12 @@ async def to_code(config):
|
||||
config[CONF_NAME_ADD_MAC_SUFFIX],
|
||||
)
|
||||
)
|
||||
# Reserve space for components to avoid reallocation during registration
|
||||
cg.add(
|
||||
cg.RawStatement(f"App.reserve_components({len(CORE.component_ids)});"),
|
||||
)
|
||||
|
||||
CORE.add_job(_add_platform_reserves)
|
||||
|
||||
CORE.add_job(_add_automations, config)
|
||||
|
||||
|
@ -579,13 +579,13 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable:
|
||||
return Pvariable(id_, rhs)
|
||||
|
||||
|
||||
def add(expression: Expression | Statement):
|
||||
def add(expression: Expression | Statement, prepend: bool = False):
|
||||
"""Add an expression to the codegen section.
|
||||
|
||||
After this is called, the given given expression will
|
||||
show up in the setup() function after this has been called.
|
||||
"""
|
||||
CORE.add(expression)
|
||||
CORE.add(expression, prepend)
|
||||
|
||||
|
||||
def add_global(expression: SafeExpType | Statement, prepend: bool = False):
|
||||
|
Loading…
x
Reference in New Issue
Block a user