From ec6a448563ad57a40dd7d964ea4b2f9bb3dafb7c Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Wed, 8 Jul 2026 13:09:39 +0200 Subject: [PATCH 1/2] prio-queue: extract sift_up() from prio_queue_put() Factor out the bubble-up loop from prio_queue_put() into a standalone sift_up() function. This is a pure refactor with no behavior change, preparing for reuse in a subsequent commit. Suggested-by: Rene Scharfe Signed-off-by: Kristofer Karlsson --- prio-queue.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/prio-queue.c b/prio-queue.c index 199775d5afd22a..926fc04e8507cd 100644 --- a/prio-queue.c +++ b/prio-queue.c @@ -37,6 +37,17 @@ void clear_prio_queue(struct prio_queue *queue) queue->get_pending = 0; } +static void sift_up(struct prio_queue *queue, size_t ix) +{ + while (ix) { + size_t parent = (ix - 1) / 2; + if (compare(queue, parent, ix) <= 0) + break; + swap(queue, parent, ix); + ix = parent; + } +} + static void sift_down_root(struct prio_queue *queue) { size_t ix, child; @@ -66,8 +77,6 @@ static inline void flush_get(struct prio_queue *queue) void prio_queue_put(struct prio_queue *queue, void *thing) { - size_t ix, parent; - if (queue->get_pending) { queue->get_pending = 0; queue->array[0].ctr = queue->insertion_ctr++; @@ -85,13 +94,7 @@ void prio_queue_put(struct prio_queue *queue, void *thing) return; /* LIFO */ /* Bubble up the new one */ - for (ix = queue->nr_ - 1; ix; ix = parent) { - parent = (ix - 1) / 2; - if (compare(queue, parent, ix) <= 0) - break; - - swap(queue, parent, ix); - } + sift_up(queue, queue->nr_ - 1); } void *prio_queue_get(struct prio_queue *queue) From 89a22c6a7532afa530f1c04ee27177e141dd360c Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Wed, 8 Jul 2026 13:10:15 +0200 Subject: [PATCH 2/2] prio-queue: use cascade for unfused gets When flush_get() removes the root without an immediate replacement, use a cascade-then-sift-up strategy instead of sift-down. Standard sift-down places the last element at the root and sifts it down. This needs two comparisons per level (pick the smaller child, then compare against the element), even though the displaced element almost always ends up near the bottom where it came from. cascade_down() instead moves the vacancy down by promoting the smaller child at each level (one comparison per level), leaving the vacancy at a leaf. The last element is then placed at the vacancy and sift_up() floats it to its correct position, which is typically very little work since it already belongs near the bottom. This is the well-known "bottom-up" variant of sift-down [1]. [1] https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort Helped-by: Rene Scharfe Signed-off-by: Kristofer Karlsson --- prio-queue.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/prio-queue.c b/prio-queue.c index 926fc04e8507cd..230d6f5e33a676 100644 --- a/prio-queue.c +++ b/prio-queue.c @@ -66,13 +66,31 @@ static void sift_down_root(struct prio_queue *queue) } } +/* Cascade vacancy toward a leaf, promoting the smaller child at each level */ +static size_t cascade_down(struct prio_queue *queue) +{ + size_t ix, child; + + for (ix = 0; (child = ix * 2 + 1) < queue->nr_; ix = child) { + if (child + 1 < queue->nr_ && + compare(queue, child, child + 1) >= 0) + child++; + queue->array[ix] = queue->array[child]; + } + return ix; +} + static inline void flush_get(struct prio_queue *queue) { + size_t ix; + if (!queue->get_pending) return; queue->get_pending = 0; - queue->array[0] = queue->array[--queue->nr_]; - sift_down_root(queue); + --queue->nr_; + ix = cascade_down(queue); + queue->array[ix] = queue->array[queue->nr_]; + sift_up(queue, ix); } void prio_queue_put(struct prio_queue *queue, void *thing)