[core] Optimize WarnIfComponentBlockingGuard::finish() hot path

Split the rarely-taken warning path into a separate noinline cold
function so the hot path (called every component every loop iteration)
is minimal. Also make WARN_IF_BLOCKING_OVER_MS constexpr so the
compiler uses an immediate compare instead of a memory load, and
merge the two ESP_LOGW calls into one.

finish() shrinks from 108 to 30 bytes. Total flash savings: -116 bytes.
This commit is contained in:
J. Nick Koston
2026-02-17 21:09:07 -06:00
parent a3d7e76992
commit 2a5ae59c20
2 changed files with 22 additions and 23 deletions

View File

@@ -107,8 +107,8 @@ const uint8_t STATUS_LED_OK = 0x00;
const uint8_t STATUS_LED_WARNING = 0x08; // Bit 3
const uint8_t STATUS_LED_ERROR = 0x10; // Bit 4
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
uint32_t global_state = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@@ -556,37 +556,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("<null>") : 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("<null>") : 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() {}
void clear_setup_priority_overrides() {
// Free the setup priority map completely
delete setup_priority_overrides;

View File

@@ -79,7 +79,7 @@ extern const uint8_t STATUS_LED_ERROR;
// 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_;