mirror of
https://github.com/esphome/esphome.git
synced 2025-08-04 17:37:49 +00:00
[core] Convert components, devices, and areas vectors to static allocation (#10020)
This commit is contained in:
parent
dbaf2cdd50
commit
d86e1e29a9
@ -214,14 +214,6 @@ class Application {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Reserve space for components to avoid memory fragmentation
|
/// Reserve space for components to avoid memory fragmentation
|
||||||
void reserve_components(size_t count) { this->components_.reserve(count); }
|
|
||||||
|
|
||||||
#ifdef USE_AREAS
|
|
||||||
void reserve_area(size_t count) { this->areas_.reserve(count); }
|
|
||||||
#endif
|
|
||||||
#ifdef USE_DEVICES
|
|
||||||
void reserve_device(size_t count) { this->devices_.reserve(count); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Register the component in this Application instance.
|
/// Register the component in this Application instance.
|
||||||
template<class C> C *register_component(C *c) {
|
template<class C> C *register_component(C *c) {
|
||||||
@ -316,7 +308,7 @@ class Application {
|
|||||||
} \
|
} \
|
||||||
return nullptr; \
|
return nullptr; \
|
||||||
}
|
}
|
||||||
const std::vector<Device *> &get_devices() { return this->devices_; }
|
const auto &get_devices() { return this->devices_; }
|
||||||
#else
|
#else
|
||||||
#define GET_ENTITY_METHOD(entity_type, entity_name, entities_member) \
|
#define GET_ENTITY_METHOD(entity_type, entity_name, entities_member) \
|
||||||
entity_type *get_##entity_name##_by_key(uint32_t key, bool include_internal = false) { \
|
entity_type *get_##entity_name##_by_key(uint32_t key, bool include_internal = false) { \
|
||||||
@ -328,7 +320,7 @@ class Application {
|
|||||||
}
|
}
|
||||||
#endif // USE_DEVICES
|
#endif // USE_DEVICES
|
||||||
#ifdef USE_AREAS
|
#ifdef USE_AREAS
|
||||||
const std::vector<Area *> &get_areas() { return this->areas_; }
|
const auto &get_areas() { return this->areas_; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
auto &get_binary_sensors() const { return this->binary_sensors_; }
|
auto &get_binary_sensors() const { return this->binary_sensors_; }
|
||||||
@ -462,12 +454,7 @@ class Application {
|
|||||||
const char *comment_{nullptr};
|
const char *comment_{nullptr};
|
||||||
const char *compilation_time_{nullptr};
|
const char *compilation_time_{nullptr};
|
||||||
|
|
||||||
// size_t members
|
// std::vector (3 pointers each: begin, end, capacity)
|
||||||
size_t dump_config_at_{SIZE_MAX};
|
|
||||||
|
|
||||||
// Vectors (largest members)
|
|
||||||
std::vector<Component *> components_{};
|
|
||||||
|
|
||||||
// Partitioned vector design for looping components
|
// Partitioned vector design for looping components
|
||||||
// =================================================
|
// =================================================
|
||||||
// Components are partitioned into [active | inactive] sections:
|
// Components are partitioned into [active | inactive] sections:
|
||||||
@ -485,12 +472,54 @@ class Application {
|
|||||||
// and active_end_ is incremented
|
// and active_end_ is incremented
|
||||||
// - This eliminates branch mispredictions from flag checking in the hot loop
|
// - This eliminates branch mispredictions from flag checking in the hot loop
|
||||||
std::vector<Component *> looping_components_{};
|
std::vector<Component *> looping_components_{};
|
||||||
|
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||||
|
std::vector<int> socket_fds_; // Vector of all monitored socket file descriptors
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// std::string members (typically 24-32 bytes each)
|
||||||
|
std::string name_;
|
||||||
|
std::string friendly_name_;
|
||||||
|
|
||||||
|
// size_t members
|
||||||
|
size_t dump_config_at_{SIZE_MAX};
|
||||||
|
|
||||||
|
// 4-byte members
|
||||||
|
uint32_t last_loop_{0};
|
||||||
|
uint32_t loop_component_start_time_{0};
|
||||||
|
|
||||||
|
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||||
|
int max_fd_{-1}; // Highest file descriptor number for select()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 2-byte members (grouped together for alignment)
|
||||||
|
uint16_t loop_interval_{16}; // Loop interval in ms (max 65535ms = 65.5 seconds)
|
||||||
|
uint16_t looping_components_active_end_{0}; // Index marking end of active components in looping_components_
|
||||||
|
uint16_t current_loop_index_{0}; // For safe reentrant modifications during iteration
|
||||||
|
|
||||||
|
// 1-byte members (grouped together to minimize padding)
|
||||||
|
uint8_t app_state_{0};
|
||||||
|
bool name_add_mac_suffix_;
|
||||||
|
bool in_loop_{false};
|
||||||
|
volatile bool has_pending_enable_loop_requests_{false};
|
||||||
|
|
||||||
|
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||||
|
bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||||
|
// Variable-sized members
|
||||||
|
fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes
|
||||||
|
fd_set read_fds_{}; // Working fd_set for select(), copied from base_read_fds_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// StaticVectors (largest members - contain actual array data inline)
|
||||||
|
StaticVector<Component *, ESPHOME_COMPONENT_COUNT> components_{};
|
||||||
|
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
std::vector<Device *> devices_{};
|
StaticVector<Device *, ESPHOME_DEVICE_COUNT> devices_{};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_AREAS
|
#ifdef USE_AREAS
|
||||||
std::vector<Area *> areas_{};
|
StaticVector<Area *, ESPHOME_AREA_COUNT> areas_{};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
StaticVector<binary_sensor::BinarySensor *, ESPHOME_ENTITY_BINARY_SENSOR_COUNT> binary_sensors_{};
|
StaticVector<binary_sensor::BinarySensor *, ESPHOME_ENTITY_BINARY_SENSOR_COUNT> binary_sensors_{};
|
||||||
@ -556,41 +585,6 @@ class Application {
|
|||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
StaticVector<update::UpdateEntity *, ESPHOME_ENTITY_UPDATE_COUNT> updates_{};
|
StaticVector<update::UpdateEntity *, ESPHOME_ENTITY_UPDATE_COUNT> updates_{};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
|
||||||
std::vector<int> socket_fds_; // Vector of all monitored socket file descriptors
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// String members
|
|
||||||
std::string name_;
|
|
||||||
std::string friendly_name_;
|
|
||||||
|
|
||||||
// 4-byte members
|
|
||||||
uint32_t last_loop_{0};
|
|
||||||
uint32_t loop_component_start_time_{0};
|
|
||||||
|
|
||||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
|
||||||
int max_fd_{-1}; // Highest file descriptor number for select()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 2-byte members (grouped together for alignment)
|
|
||||||
uint16_t loop_interval_{16}; // Loop interval in ms (max 65535ms = 65.5 seconds)
|
|
||||||
uint16_t looping_components_active_end_{0};
|
|
||||||
uint16_t current_loop_index_{0}; // For safe reentrant modifications during iteration
|
|
||||||
|
|
||||||
// 1-byte members (grouped together to minimize padding)
|
|
||||||
uint8_t app_state_{0};
|
|
||||||
bool name_add_mac_suffix_;
|
|
||||||
bool in_loop_{false};
|
|
||||||
volatile bool has_pending_enable_loop_requests_{false};
|
|
||||||
|
|
||||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
|
||||||
bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes
|
|
||||||
|
|
||||||
// Variable-sized members at end
|
|
||||||
fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes
|
|
||||||
fd_set read_fds_{}; // Working fd_set for select(), copied from base_read_fds_
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Global storage of Application pointer - only one Application can exist.
|
/// Global storage of Application pointer - only one Application can exist.
|
||||||
|
@ -459,10 +459,8 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
config[CONF_NAME_ADD_MAC_SUFFIX],
|
config[CONF_NAME_ADD_MAC_SUFFIX],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Reserve space for components to avoid reallocation during registration
|
# Define component count for static allocation
|
||||||
cg.add(
|
cg.add_define("ESPHOME_COMPONENT_COUNT", len(CORE.component_ids))
|
||||||
cg.RawStatement(f"App.reserve_components({len(CORE.component_ids)});"),
|
|
||||||
)
|
|
||||||
|
|
||||||
CORE.add_job(_add_platform_defines)
|
CORE.add_job(_add_platform_defines)
|
||||||
|
|
||||||
@ -531,8 +529,8 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
all_areas.extend(config[CONF_AREAS])
|
all_areas.extend(config[CONF_AREAS])
|
||||||
|
|
||||||
if all_areas:
|
if all_areas:
|
||||||
cg.add(cg.RawStatement(f"App.reserve_area({len(all_areas)});"))
|
|
||||||
cg.add_define("USE_AREAS")
|
cg.add_define("USE_AREAS")
|
||||||
|
cg.add_define("ESPHOME_AREA_COUNT", len(all_areas))
|
||||||
|
|
||||||
for area_conf in all_areas:
|
for area_conf in all_areas:
|
||||||
area_id: core.ID = area_conf[CONF_ID]
|
area_id: core.ID = area_conf[CONF_ID]
|
||||||
@ -549,9 +547,9 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
if not devices:
|
if not devices:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Reserve space for devices
|
# Define device count for static allocation
|
||||||
cg.add(cg.RawStatement(f"App.reserve_device({len(devices)});"))
|
|
||||||
cg.add_define("USE_DEVICES")
|
cg.add_define("USE_DEVICES")
|
||||||
|
cg.add_define("ESPHOME_DEVICE_COUNT", len(devices))
|
||||||
|
|
||||||
# Process each device
|
# Process each device
|
||||||
for dev_conf in devices:
|
for dev_conf in devices:
|
||||||
|
@ -240,7 +240,10 @@
|
|||||||
|
|
||||||
#define USE_DASHBOARD_IMPORT
|
#define USE_DASHBOARD_IMPORT
|
||||||
|
|
||||||
// Default entity counts for static analysis
|
// Default counts for static analysis
|
||||||
|
#define ESPHOME_COMPONENT_COUNT 50
|
||||||
|
#define ESPHOME_DEVICE_COUNT 10
|
||||||
|
#define ESPHOME_AREA_COUNT 10
|
||||||
#define ESPHOME_ENTITY_ALARM_CONTROL_PANEL_COUNT 1
|
#define ESPHOME_ENTITY_ALARM_CONTROL_PANEL_COUNT 1
|
||||||
#define ESPHOME_ENTITY_BINARY_SENSOR_COUNT 1
|
#define ESPHOME_ENTITY_BINARY_SENSOR_COUNT 1
|
||||||
#define ESPHOME_ENTITY_BUTTON_COUNT 1
|
#define ESPHOME_ENTITY_BUTTON_COUNT 1
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -100,6 +101,8 @@ template<typename T, size_t N> class StaticVector {
|
|||||||
using value_type = T;
|
using value_type = T;
|
||||||
using iterator = typename std::array<T, N>::iterator;
|
using iterator = typename std::array<T, N>::iterator;
|
||||||
using const_iterator = typename std::array<T, N>::const_iterator;
|
using const_iterator = typename std::array<T, N>::const_iterator;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<T, N> data_{};
|
std::array<T, N> data_{};
|
||||||
@ -114,6 +117,7 @@ template<typename T, size_t N> class StaticVector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const { return count_; }
|
size_t size() const { return count_; }
|
||||||
|
bool empty() const { return count_ == 0; }
|
||||||
|
|
||||||
T &operator[](size_t i) { return data_[i]; }
|
T &operator[](size_t i) { return data_[i]; }
|
||||||
const T &operator[](size_t i) const { return data_[i]; }
|
const T &operator[](size_t i) const { return data_[i]; }
|
||||||
@ -123,6 +127,12 @@ template<typename T, size_t N> class StaticVector {
|
|||||||
iterator end() { return data_.begin() + count_; }
|
iterator end() { return data_.begin() + count_; }
|
||||||
const_iterator begin() const { return data_.begin(); }
|
const_iterator begin() const { return data_.begin(); }
|
||||||
const_iterator end() const { return data_.begin() + count_; }
|
const_iterator end() const { return data_.begin() + count_; }
|
||||||
|
|
||||||
|
// Reverse iterators
|
||||||
|
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||||
|
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||||
|
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||||||
|
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user