From f7459670d34b47f66210eaaca7586be33f84205c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Feb 2026 10:10:22 -0600 Subject: [PATCH] [core] Optimize WarnIfComponentBlockingGuard::finish() hot path (#14040) Co-authored-by: Claude Opus 4.6 --- esphome/core/component.cpp | 38 ++++++++++++++++++-------------------- esphome/core/component.h | 7 ++++--- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index f283a69064..b458ea2a84 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -82,8 +82,8 @@ void store_component_error_message(const Component *component, const char *messa // setup_priority, component state, and status LED constants are now // constexpr in component.h -const uint16_t WARN_IF_BLOCKING_OVER_MS = 50U; ///< Initial blocking time allowed without warning -const uint16_t WARN_IF_BLOCKING_INCREMENT_MS = 10U; ///< How long the blocking time must be larger to warn again +static constexpr uint16_t WARN_IF_BLOCKING_INCREMENT_MS = + 10U; ///< How long the blocking time must be larger to warn again float Component::get_loop_priority() const { return 0.0f; } @@ -529,37 +529,35 @@ void PollingComponent::stop_poller() { uint32_t PollingComponent::get_update_interval() const { return this->update_interval_; } void PollingComponent::set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } -WarnIfComponentBlockingGuard::WarnIfComponentBlockingGuard(Component *component, uint32_t start_time) - : started_(start_time), component_(component) {} +static void __attribute__((noinline, cold)) warn_blocking(Component *component, uint32_t blocking_time) { + bool should_warn; + if (component != nullptr) { + should_warn = component->should_warn_of_blocking(blocking_time); + } else { + should_warn = true; // Already checked > WARN_IF_BLOCKING_OVER_MS in caller + } + if (should_warn) { + ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms), max is 30 ms", + component == nullptr ? LOG_STR_LITERAL("") : LOG_STR_ARG(component->get_component_log_str()), + blocking_time); + } +} + uint32_t WarnIfComponentBlockingGuard::finish() { uint32_t curr_time = millis(); - uint32_t blocking_time = curr_time - this->started_; - #ifdef USE_RUNTIME_STATS // Record component runtime stats if (global_runtime_stats != nullptr) { global_runtime_stats->record_component_time(this->component_, blocking_time, curr_time); } #endif - bool should_warn; - if (this->component_ != nullptr) { - should_warn = this->component_->should_warn_of_blocking(blocking_time); - } else { - should_warn = blocking_time > WARN_IF_BLOCKING_OVER_MS; + if (blocking_time > WARN_IF_BLOCKING_OVER_MS) { + warn_blocking(this->component_, blocking_time); } - if (should_warn) { - ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", - component_ == nullptr ? LOG_STR_LITERAL("") : LOG_STR_ARG(component_->get_component_log_str()), - blocking_time); - ESP_LOGW(TAG, "Components should block for at most 30 ms"); - } - return curr_time; } -WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() {} - #ifdef USE_SETUP_PRIORITY_OVERRIDE void clear_setup_priority_overrides() { // Free the setup priority map completely diff --git a/esphome/core/component.h b/esphome/core/component.h index d4dad3c9a6..b99641a275 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -79,7 +79,7 @@ inline constexpr uint8_t STATUS_LED_ERROR = 0x10; // Remove before 2026.8.0 enum class RetryResult { DONE, RETRY }; -extern const uint16_t WARN_IF_BLOCKING_OVER_MS; +inline constexpr uint16_t WARN_IF_BLOCKING_OVER_MS = 50U; class Component { public: @@ -550,12 +550,13 @@ class PollingComponent : public Component { class WarnIfComponentBlockingGuard { public: - WarnIfComponentBlockingGuard(Component *component, uint32_t start_time); + WarnIfComponentBlockingGuard(Component *component, uint32_t start_time) + : started_(start_time), component_(component) {} // Finish the timing operation and return the current time uint32_t finish(); - ~WarnIfComponentBlockingGuard(); + ~WarnIfComponentBlockingGuard() = default; protected: uint32_t started_;