From 20884d5844f67b92a99923bab12d0b60d1286709 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Apr 2026 12:08:39 -1000 Subject: [PATCH] Use empty() instead of empty_relaxed() for wifi_loop_ fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC on Xtensa emits memw for relaxed atomic loads too, so the savings from relaxed vs acquire are only 2 memw (~4 cycles) — not worth the weaker correctness guarantees. The real optimization is the early return that skips get_and_reset_dropped_count() and the pop() loop entirely. --- esphome/components/wifi/wifi_component_esp_idf.cpp | 5 ++--- esphome/components/wifi/wifi_component_libretiny.cpp | 6 ++---- esphome/core/freertos_queue.h | 4 ---- esphome/core/lock_free_queue.h | 6 ------ 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 23dd81c8664..e079f3cda2e 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -716,9 +716,8 @@ const char *get_disconnect_reason_str(uint8_t reason) { } void WiFiComponent::wifi_loop_() { - // Fast path: relaxed check avoids acquire fences (4x memw on Xtensa) when queue is empty. - // Safe from consumer side — worst case we process events one loop iteration late. - if (this->event_queue_.empty_relaxed()) + // Fast path: skip dropped count check and pop loop when queue is empty + if (this->event_queue_.empty()) return; uint16_t dropped = this->event_queue_.get_and_reset_dropped_count(); diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 1adfa573322..946d1ca50dd 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -778,10 +778,8 @@ network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask( network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } void WiFiComponent::wifi_loop_() { - // Fast path: skip queue drain when empty. - // On LockFreeQueue platforms, relaxed loads avoid memory fences. - // On FreeRTOSQueue platforms, this is a lightweight uxQueueMessagesWaiting check. - if (this->event_queue_.empty_relaxed()) + // Fast path: skip dropped count check and pop loop when queue is empty + if (this->event_queue_.empty()) return; uint16_t dropped = this->event_queue_.get_and_reset_dropped_count(); diff --git a/esphome/core/freertos_queue.h b/esphome/core/freertos_queue.h index a67718ea122..2f3faf818a3 100644 --- a/esphome/core/freertos_queue.h +++ b/esphome/core/freertos_queue.h @@ -82,10 +82,6 @@ template class FreeRTOSQueue { bool empty() const { return uxQueueMessagesWaiting(this->handle_) == 0; } - /// Fast empty check — same as empty() for FreeRTOS queues since - /// uxQueueMessagesWaiting is already a lightweight read. - bool empty_relaxed() const { return this->empty(); } - bool full() const { return uxQueueSpacesAvailable(this->handle_) == 0; } size_t size() const { return uxQueueMessagesWaiting(this->handle_); } diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index 62c3b21fae5..316186ea542 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -118,12 +118,6 @@ template class LockFreeQueue { bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); } - /// Fast empty check using relaxed loads — no memory fences on Xtensa. - /// Safe when called only from the consumer side: head_ is only written by the - /// consumer, and tail_ can only advance. Worst case we miss a just-pushed item - /// and catch it next loop iteration. - bool empty_relaxed() const { return head_.load(std::memory_order_relaxed) == tail_.load(std::memory_order_relaxed); } - bool full() const { uint8_t next_tail = next_index(tail_.load(std::memory_order_relaxed)); return next_tail == head_.load(std::memory_order_acquire);