[api] Extract keepalive ping to noinline function

Extract the keepalive ping sending logic from APIConnection::loop()
into a separate noinline send_keepalive_ping_() method. This code
only fires once per keepalive interval (~60s) making it cold relative
to the ~111 Hz loop rate. Moving it out reduces loop() from 337 to
258 bytes, keeping the hot path smaller and more cache-friendly.
This commit is contained in:
J. Nick Koston
2026-02-12 15:32:29 -06:00
parent 136d17366f
commit 94ea5bac3a
2 changed files with 19 additions and 11 deletions

View File

@@ -230,17 +230,7 @@ void APIConnection::loop() {
this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("is unresponsive; disconnecting"));
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
// Only send ping if we're not disconnecting
ESP_LOGVV(TAG, "Sending keepalive PING");
PingRequest req;
this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
if (!this->flags_.sent_ping) {
// If we can't send the ping request directly (tx_buffer full),
// schedule it at the front of the batch so it will be sent with priority
ESP_LOGW(TAG, "Buffer full, ping queued");
this->schedule_message_front_(nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
}
this->send_keepalive_ping_();
}
#ifdef USE_API_HOMEASSISTANT_STATES
@@ -256,6 +246,20 @@ void APIConnection::loop() {
#endif
}
void APIConnection::send_keepalive_ping_() {
// Only send ping if we're not disconnecting
ESP_LOGVV(TAG, "Sending keepalive PING");
PingRequest req;
this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
if (!this->flags_.sent_ping) {
// If we can't send the ping request directly (tx_buffer full),
// schedule it at the front of the batch so it will be sent with priority
ESP_LOGW(TAG, "Buffer full, ping queued");
this->schedule_message_front_(nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
}
}
void APIConnection::process_active_iterator_() {
// Caller ensures active_iterator_ != NONE
if (this->active_iterator_ == ActiveIterator::LIST_ENTITIES) {

View File

@@ -370,6 +370,10 @@ class APIConnection final : public APIServerConnectionBase {
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
}
// Send keepalive ping — extracted from loop() as noinline since it only fires
// once per keepalive interval (~60s), keeping the cold code out of the hot path.
void __attribute__((noinline)) send_keepalive_ping_();
// Process active iterator (list_entities/initial_state) during connection setup.
// Extracted from loop() — only runs during initial handshake, NONE in steady state.
void __attribute__((noinline)) process_active_iterator_();