mirror of
https://github.com/esphome/esphome.git
synced 2026-05-30 07:16:11 +08:00
avoid template overhead
This commit is contained in:
@@ -2222,27 +2222,6 @@ template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value,
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Provides properly aligned, uninitialized static storage for a given type T.
|
|
||||||
*
|
|
||||||
* This struct is designed to replace dynamic heap allocations (`new T(...)`) for
|
|
||||||
* global or static singletons within ESPHome, preventing memory fragmentation.
|
|
||||||
* The underlying object must be explicitly constructed using placement new
|
|
||||||
* before access. No destructor is called — this is intentional since ESPHome
|
|
||||||
* singletons live for the entire device lifetime.
|
|
||||||
*/
|
|
||||||
template<class T> struct PlacementStorage {
|
|
||||||
/// @brief Raw byte storage, strictly aligned for type T.
|
|
||||||
alignas(T) unsigned char data[sizeof(T)];
|
|
||||||
|
|
||||||
/// @brief Retrieves a pointer to the storage as the target type.
|
|
||||||
/// The caller must ensure the object has been constructed via placement new before dereferencing.
|
|
||||||
T *get() { return reinterpret_cast<T *>(data); }
|
|
||||||
|
|
||||||
/// @brief Retrieves a const pointer to the storage as the target type.
|
|
||||||
const T *get() const { return reinterpret_cast<const T *>(data); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @name Internal functions
|
/// @name Internal functions
|
||||||
///@{
|
///@{
|
||||||
|
|
||||||
|
|||||||
@@ -584,18 +584,20 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj":
|
|||||||
# For 'new' allocations, use placement new into static storage
|
# For 'new' allocations, use placement new into static storage
|
||||||
# to avoid heap fragmentation on embedded devices.
|
# to avoid heap fragmentation on embedded devices.
|
||||||
the_type = id_.type
|
the_type = id_.type
|
||||||
storage_type = MockObj(f"esphome::PlacementStorage<{the_type}>")
|
storage_name = f"{id_.id}_storage_"
|
||||||
storage_id = ID(f"{id_.id}_storage_", type=storage_type)
|
|
||||||
|
|
||||||
|
# Declare aligned byte array for the object storage
|
||||||
CORE.add_global(
|
CORE.add_global(
|
||||||
VariableDeclarationExpression(storage_type, "", storage_id, static=True)
|
RawStatement(
|
||||||
|
f"alignas({the_type}) static unsigned char {storage_name}[sizeof({the_type})];"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
CORE.add_global(
|
CORE.add_global(
|
||||||
AssignmentExpression(
|
AssignmentExpression(
|
||||||
f"static {the_type}",
|
f"static {the_type}",
|
||||||
"*const ",
|
"*const ",
|
||||||
id_,
|
id_,
|
||||||
MockObj(f"{storage_id.id}.get()"),
|
MockObj(f"reinterpret_cast<{the_type} *>({storage_name})"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Extract args from the CallExpression and rebuild as placement new.
|
# Extract args from the CallExpression and rebuild as placement new.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main):
|
|||||||
main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml")
|
main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml")
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
"static deep_sleep::DeepSleepComponent *const deepsleep = deepsleep_storage_.get();"
|
"static deep_sleep::DeepSleepComponent *const deepsleep = reinterpret_cast<deep_sleep::DeepSleepComponent *>(deepsleep_storage_);"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
)
|
)
|
||||||
assert "new(deepsleep) deep_sleep::DeepSleepComponent();" in main_cpp
|
assert "new(deepsleep) deep_sleep::DeepSleepComponent();" in main_cpp
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ def test_globals_placement_new_with_template_args(
|
|||||||
# Globals uses Pvariable with Type.new(template_args, initial_value)
|
# Globals uses Pvariable with Type.new(template_args, initial_value)
|
||||||
# which exercises the template_args preservation in placement new.
|
# which exercises the template_args preservation in placement new.
|
||||||
assert "static globals::GlobalsComponent<int> *const my_global_int" in main_cpp
|
assert "static globals::GlobalsComponent<int> *const my_global_int" in main_cpp
|
||||||
assert "PlacementStorage<globals::GlobalsComponent<int>>" in main_cpp
|
assert "sizeof(globals::GlobalsComponent<int>)" in main_cpp
|
||||||
assert "new(my_global_int) globals::GlobalsComponent<int>" in main_cpp
|
assert "new(my_global_int) globals::GlobalsComponent<int>" in main_cpp
|
||||||
|
|
||||||
# Verify initial value is passed as constructor arg
|
# Verify initial value is passed as constructor arg
|
||||||
assert "42" in main_cpp
|
assert "42" in main_cpp
|
||||||
|
|
||||||
# Check other globals are also generated
|
# Check other globals are also generated
|
||||||
assert "PlacementStorage<globals::GlobalsComponent<float>>" in main_cpp
|
assert "sizeof(globals::GlobalsComponent<float>)" in main_cpp
|
||||||
assert "PlacementStorage<globals::GlobalsComponent<bool>>" in main_cpp
|
assert "sizeof(globals::GlobalsComponent<bool>)" in main_cpp
|
||||||
|
|||||||
@@ -242,9 +242,13 @@ def test_image_generation(
|
|||||||
main_cpp = generate_main(component_config_path("image_test.yaml"))
|
main_cpp = generate_main(component_config_path("image_test.yaml"))
|
||||||
assert "uint8_t_id[] PROGMEM = {0x24, 0x21, 0x24, 0x21" in main_cpp
|
assert "uint8_t_id[] PROGMEM = {0x24, 0x21, 0x24, 0x21" in main_cpp
|
||||||
assert (
|
assert (
|
||||||
"static esphome::PlacementStorage<image::Image> cat_img_storage_;" in main_cpp
|
"alignas(image::Image) static unsigned char cat_img_storage_[sizeof(image::Image)];"
|
||||||
|
in main_cpp
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"static image::Image *const cat_img = reinterpret_cast<image::Image *>(cat_img_storage_);"
|
||||||
|
in main_cpp
|
||||||
)
|
)
|
||||||
assert "static image::Image *const cat_img = cat_img_storage_.get();" in main_cpp
|
|
||||||
assert (
|
assert (
|
||||||
"new(cat_img) image::Image(uint8_t_id, 32, 24, image::IMAGE_TYPE_RGB565, image::TRANSPARENCY_OPAQUE);"
|
"new(cat_img) image::Image(uint8_t_id, 32, 24, image::IMAGE_TYPE_RGB565, image::TRANSPARENCY_OPAQUE);"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
|
|||||||
@@ -119,11 +119,12 @@ def test_code_generation(
|
|||||||
|
|
||||||
main_cpp = generate_main(component_fixture_path("mipi_dsi.yaml"))
|
main_cpp = generate_main(component_fixture_path("mipi_dsi.yaml"))
|
||||||
assert (
|
assert (
|
||||||
"static esphome::PlacementStorage<mipi_dsi::MIPI_DSI> p4_nano_storage_;"
|
"alignas(mipi_dsi::MIPI_DSI) static unsigned char p4_nano_storage_[sizeof(mipi_dsi::MIPI_DSI)];"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
"static mipi_dsi::MIPI_DSI *const p4_nano = p4_nano_storage_.get();" in main_cpp
|
"static mipi_dsi::MIPI_DSI *const p4_nano = reinterpret_cast<mipi_dsi::MIPI_DSI *>(p4_nano_storage_);"
|
||||||
|
in main_cpp
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
"new(p4_nano) mipi_dsi::MIPI_DSI(800, 1280, display::COLOR_BITNESS_565, 16);"
|
"new(p4_nano) mipi_dsi::MIPI_DSI(800, 1280, display::COLOR_BITNESS_565, 16);"
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ def test_status_led_generation(
|
|||||||
"""Test status_led generation."""
|
"""Test status_led generation."""
|
||||||
main_cpp = generate_main(component_config_path("status_led_test.yaml"))
|
main_cpp = generate_main(component_config_path("status_led_test.yaml"))
|
||||||
assert (
|
assert (
|
||||||
"static esphome::PlacementStorage<status_led::StatusLED> status_led_statusled_id_storage_;"
|
"alignas(status_led::StatusLED) static unsigned char status_led_statusled_id_storage_[sizeof(status_led::StatusLED)];"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
"static status_led::StatusLED *const status_led_statusled_id = status_led_statusled_id_storage_.get();"
|
"static status_led::StatusLED *const status_led_statusled_id = reinterpret_cast<status_led::StatusLED *>(status_led_statusled_id_storage_);"
|
||||||
in main_cpp
|
in main_cpp
|
||||||
)
|
)
|
||||||
assert "new(status_led_statusled_id) status_led::StatusLED(" in main_cpp
|
assert "new(status_led_statusled_id) status_led::StatusLED(" in main_cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user