From ef626571b2b890c0c88586e2808d3cc536ac25b8 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Fri, 10 Jul 2020 09:43:36 -0700 Subject: [PATCH] kernel/sched: Optimize deadline comparison Needing to check the current cycle time (which involves a spinlock and register read on most architectures) is wasteful in the scheduler priority predicate, which is a hot path. If we "burn" one bit of precision (and document the rule), we can do the comparison without knowing the current time. 2^31 cycles is still far longer than a live deadline thread in any legitimate realtime app should ever live before being scheduled. Signed-off-by: Andy Ross --- include/kernel.h | 11 ++++++----- kernel/sched.c | 18 +++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/include/kernel.h b/include/kernel.h index 9dcd0a0b491..36a6c151a1d 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -718,11 +718,12 @@ __syscall void k_thread_priority_set(k_tid_t thread, int prio); * static priority. Threads at different priorities will be scheduled * according to their static priority. * - * @note Deadlines that are negative (i.e. in the past) are still seen - * as higher priority than others, even if the thread has "finished" - * its work. If you don't want it scheduled anymore, you have to - * reset the deadline into the future, block/pend the thread, or - * modify its priority with k_thread_priority_set(). + * @note Deadlines are stored internally using 32 bit unsigned + * integers. The number of cycles between the "first" deadline in the + * scheduler queue and the "last" deadline must be less than 2^31 (i.e + * a signed non-negative quantity). Failure to adhere to this rule + * may result in scheduled threads running in an incorrect dealine + * order. * * @note Despite the API naming, the scheduler makes no guarantees the * the thread WILL be scheduled within that deadline, nor does it take diff --git a/kernel/sched.c b/kernel/sched.c index fa825a22174..bc725814559 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -100,18 +100,18 @@ bool z_is_t1_higher_prio_than_t2(struct k_thread *thread_1, } #ifdef CONFIG_SCHED_DEADLINE - /* Note that we don't care about wraparound conditions. The - * expectation is that the application will have arranged to - * block the threads, change their priorities or reset their - * deadlines when the job is complete. Letting the deadlines - * go negative is fine and in fact prevents aliasing bugs. + /* If we assume all deadlines live within the same "half" of + * the 32 bit modulus space (this is a documented API rule), + * then the latest dealine in the queue minus the earliest is + * guaranteed to be (2's complement) non-negative. We can + * leverage that to compare the values without having to check + * the current time. */ if (thread_1->base.prio == thread_2->base.prio) { - int now = (int) k_cycle_get_32(); - int dt1 = thread_1->base.prio_deadline - now; - int dt2 = thread_2->base.prio_deadline - now; + int32_t d1 = thread_1->base.prio_deadline; + int32_t d2 = thread_2->base.prio_deadline; - return dt1 < dt2; + return (d2 - d1) >= 0; } #endif