From bfa446a28ea27d3ee5bfc9d104036fd233ec4f40 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 20 Mar 2026 18:59:45 -1000 Subject: [PATCH] [core] Switch to always_inline only, drop play_complex overrides The play_complex overrides cost flash per template instantiation across every automation. The always_inline on the forwarding chain (Trigger::trigger, Automation::trigger, ActionList::play) is a fixed cost that collapses 3 frames into 1. --- esphome/core/automation.h | 12 +++++++++--- esphome/core/base_automation.h | 21 --------------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 7934fdbec9..954565aca0 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -322,7 +322,9 @@ template class Automation; template class Trigger { public: /// Inform the parent automation that the event has triggered. - void trigger(const Ts &...x) { + // Force-inline: collapses the Trigger→Automation→ActionList forwarding + // chain into a single frame, reducing automation call stack depth. + __attribute__((always_inline)) void trigger(const Ts &...x) { if (this->automation_parent_ == nullptr) return; this->automation_parent_->trigger(x...); @@ -429,7 +431,9 @@ template class ActionList { this->add_action(action); } } - void play(const Ts &...x) { + // Force-inline: part of the Trigger→Automation→ActionList forwarding + // chain collapsed to reduce automation call stack depth. + __attribute__((always_inline)) void play(const Ts &...x) { if (this->actions_begin_ != nullptr) this->actions_begin_->play_complex(x...); } @@ -473,7 +477,9 @@ template class Automation { void stop() { this->actions_.stop(); } - void trigger(const Ts &...x) { this->actions_.play(x...); } + // Force-inline: part of the Trigger→Automation→ActionList forwarding + // chain collapsed to reduce automation call stack depth. + __attribute__((always_inline)) void trigger(const Ts &...x) { this->actions_.play(x...); } bool is_running() { return this->actions_.is_running(); } diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index b809c56f1a..985f26e711 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -217,13 +217,6 @@ template class LambdaAction : public Action { public: explicit LambdaAction(std::function &&f) : f_(std::move(f)) {} - // Override play_complex to call play() non-virtually (qualified call), - // eliminating one virtual dispatch frame from the call stack. - void play_complex(const Ts &...x) override { - this->num_running_++; - LambdaAction::play(x...); - this->play_next_(x...); - } void play(const Ts &...x) override { this->f_(x...); } protected: @@ -237,13 +230,6 @@ template class StatelessLambdaAction : public Action { public: explicit StatelessLambdaAction(void (*f)(Ts...)) : f_(f) {} - // Override play_complex to call play() non-virtually (qualified call), - // eliminating one virtual dispatch frame from the call stack. - void play_complex(const Ts &...x) override { - this->num_running_++; - StatelessLambdaAction::play(x...); - this->play_next_(x...); - } void play(const Ts &...x) override { this->f_(x...); } protected: @@ -257,13 +243,6 @@ template class ContinuationAction : public Action { public: explicit ContinuationAction(Action *parent) : parent_(parent) {} - // Override play_complex to call play() non-virtually (qualified call), - // eliminating one virtual dispatch frame from the call stack. - void play_complex(const Ts &...x) override { - this->num_running_++; - ContinuationAction::play(x...); - this->play_next_(x...); - } void play(const Ts &...x) override { this->parent_->play_next_(x...); } protected: