latency of async_context_freertos can be improved #3111

Open
opened 2026-08-04 08:53:00 -05:00 by peterharperuk · 1 comment
peterharperuk commented 2026-08-04 08:53:00 -05:00 (Migrated from github.com)

Waking the async_context task from an ISR doesn't request a context switch, so the task is only made ready - it won't run until the next scheduler tick. The fix is fairly simple...

@@ -81,8 +81,13 @@ static void async_context_freertos_wake_up(async_context_t *self_base) {
     async_context_freertos_t *self = (async_context_freertos_t *)self_base;
     if (self->task_handle) {
         if (portCHECK_IF_IN_ISR()) {
-            vTaskNotifyGiveFromISR(self->task_handle, NULL);
-            xSemaphoreGiveFromISR(self->work_needed_sem, NULL);
+            // Ask for a context switch on the way out. Without this the woken
+            // task is merely made ready and does not run until the next tick,
+            // adding up to a whole tick period to every wake.
+            BaseType_t higher_priority_woken = pdFALSE;
+            vTaskNotifyGiveFromISR(self->task_handle, &higher_priority_woken);
+            xSemaphoreGiveFromISR(self->work_needed_sem, &higher_priority_woken);
+            portYIELD_FROM_ISR(higher_priority_woken);
         } else {
             // we don't want to wake ourselves up (we will only ever be called
             // from the async_context_task if we own the lock, in which case processing

The table below shows the latency of the time to wake the async task. Without the fix things are worse if configTICK_RATE_HZ is reduced (not sure why anyone would do this?)

┌────────┬────────┬────────┬─────────┐
│        │ median │  p99   │   max   │
├────────┼────────┼────────┼─────────┤
│ before fix │ 503 µs │ 992 µs │ 1001 µs │
├────────┼────────┼────────┼─────────┤
│ after fix │ 6 µs   │ 8 µs   │ 34 µs   │
└────────┴────────┴────────┴─────────┘

This doesn't improve cyw43 performance because the driver ensures it's always busy. This change would lead to more calls to the async task. The current behaviour is effectively batching interrupts - which might be a good thing in some cases?

Claude suggested this change while I was doing some performance analysis.

Waking the async_context task from an ISR doesn't request a context switch, so the task is only made ready - it won't run until the next scheduler tick. The fix is fairly simple... ``` @@ -81,8 +81,13 @@ static void async_context_freertos_wake_up(async_context_t *self_base) { async_context_freertos_t *self = (async_context_freertos_t *)self_base; if (self->task_handle) { if (portCHECK_IF_IN_ISR()) { - vTaskNotifyGiveFromISR(self->task_handle, NULL); - xSemaphoreGiveFromISR(self->work_needed_sem, NULL); + // Ask for a context switch on the way out. Without this the woken + // task is merely made ready and does not run until the next tick, + // adding up to a whole tick period to every wake. + BaseType_t higher_priority_woken = pdFALSE; + vTaskNotifyGiveFromISR(self->task_handle, &higher_priority_woken); + xSemaphoreGiveFromISR(self->work_needed_sem, &higher_priority_woken); + portYIELD_FROM_ISR(higher_priority_woken); } else { // we don't want to wake ourselves up (we will only ever be called // from the async_context_task if we own the lock, in which case processing ``` The table below shows the latency of the time to wake the async task. Without the fix things are worse if configTICK_RATE_HZ is reduced (not sure why anyone would do this?) ``` ┌────────┬────────┬────────┬─────────┐ │ │ median │ p99 │ max │ ├────────┼────────┼────────┼─────────┤ │ before fix │ 503 µs │ 992 µs │ 1001 µs │ ├────────┼────────┼────────┼─────────┤ │ after fix │ 6 µs │ 8 µs │ 34 µs │ └────────┴────────┴────────┴─────────┘ ``` This doesn't improve cyw43 performance because the driver ensures it's always busy. This change would lead to more calls to the async task. The current behaviour is effectively batching interrupts - which might be a good thing in some cases? Claude suggested this change while I was doing some performance analysis.
peterharperuk commented 2026-08-04 08:53:52 -05:00 (Migrated from github.com)

@kilograham What do you think about changing this?

@kilograham What do you think about changing this?
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#3111
No description provided.