Use empty() instead of empty_relaxed() for wifi_loop_ fast path

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.
This commit is contained in:
J. Nick Koston
2026-04-01 12:08:39 -10:00
parent 63a0581440
commit 20884d5844
4 changed files with 4 additions and 17 deletions
@@ -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();
@@ -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();
-4
View File
@@ -82,10 +82,6 @@ template<class T, uint8_t SIZE> 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_); }
-6
View File
@@ -118,12 +118,6 @@ template<class T, uint8_t SIZE> 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);