Skip to content

scheduler: add optional preemptive scheduling - #2492

Open
stlankes wants to merge 2 commits into
hermit-os:mainfrom
stlankes:preempt
Open

scheduler: add optional preemptive scheduling#2492
stlankes wants to merge 2 commits into
hermit-os:mainfrom
stlankes:preempt

Conversation

@stlankes

Copy link
Copy Markdown
Contributor

The scheduler is tickless (a TSC-deadline one-shot timer), so a CPU-bound task that never blocks or yields keeps its core indefinitely. Add an optional preemptive feature that round-robins between ready tasks via a time slice.

A new Source::Preemption timer slot is armed for now + 10ms whenever the scheduler switches to a task while others are ready, and whenever custom_wakeup makes a task ready (so a freshly woken task preempts a non-yielding runner soon). When the slice expires, the timer handler's existing reschedule() switches to the next ready task. With no contention nothing is armed, so the system stays tickless.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark Results

Details
Benchmark Current: e236bed Previous: 2e23902 Performance Ratio
startup_benchmark Build Time 91.85 s 80.34 s 1.14
startup_benchmark File Size 0.78 MB 0.80 MB 0.98
Startup Time - 1 core 0.76 s (±0.03 s) 0.75 s (±0.02 s) 1.02
Startup Time - 2 cores 0.77 s (±0.03 s) 0.74 s (±0.02 s) 1.04
Startup Time - 4 cores 0.75 s (±0.02 s) 0.74 s (±0.02 s) 1.01
multithreaded_benchmark Build Time 93.68 s 82.11 s 1.14
multithreaded_benchmark File Size 0.88 MB 0.86 MB 1.03
Multithreaded Pi Efficiency - 2 Threads 88.55 % (±8.39 %) 85.89 % (±6.61 %) 1.03
Multithreaded Pi Efficiency - 4 Threads 44.21 % (±3.46 %) 43.43 % (±2.56 %) 1.02
Multithreaded Pi Efficiency - 8 Threads 25.75 % (±1.64 %) 25.76 % (±1.53 %) 1.00
micro_benchmarks Build Time 93.06 s 80.40 s 1.16
micro_benchmarks File Size 0.89 MB 0.86 MB 1.03
Scheduling time - 1 thread 70.49 ticks (±3.50 ticks) 62.65 ticks (±4.06 ticks) 1.13
Scheduling time - 2 threads 37.87 ticks (±3.12 ticks) 34.08 ticks (±4.10 ticks) 1.11
Micro - Time for syscall (getpid) 3.89 ticks (±0.52 ticks) 3.45 ticks (±0.58 ticks) 1.13
Memcpy speed - (built_in) block size 4096 74470.34 MByte/s (±51930.84 MByte/s) 82448.38 MByte/s (±56997.13 MByte/s) 0.90
Memcpy speed - (built_in) block size 1048576 29579.15 MByte/s (±24030.25 MByte/s) 30585.98 MByte/s (±24707.84 MByte/s) 0.97
Memcpy speed - (built_in) block size 16777216 25161.96 MByte/s (±20903.39 MByte/s) 26340.06 MByte/s (±21720.96 MByte/s) 0.96
Memset speed - (built_in) block size 4096 74859.10 MByte/s (±52170.99 MByte/s) 82292.76 MByte/s (±56891.50 MByte/s) 0.91
Memset speed - (built_in) block size 1048576 30282.45 MByte/s (±24452.37 MByte/s) 31323.85 MByte/s (±25145.86 MByte/s) 0.97
Memset speed - (built_in) block size 16777216 25886.80 MByte/s (±21358.68 MByte/s) 27104.68 MByte/s (±22209.94 MByte/s) 0.96
Memcpy speed - (rust) block size 4096 70847.21 MByte/s (±49425.80 MByte/s) 74097.96 MByte/s (±51811.44 MByte/s) 0.96
Memcpy speed - (rust) block size 1048576 29468.63 MByte/s (±24015.64 MByte/s) 30361.60 MByte/s (±24602.37 MByte/s) 0.97
Memcpy speed - (rust) block size 16777216 25943.98 MByte/s (±21571.40 MByte/s) 27625.34 MByte/s (±22806.88 MByte/s) 0.94
Memset speed - (rust) block size 4096 70954.43 MByte/s (±49499.07 MByte/s) 74373.47 MByte/s (±51976.48 MByte/s) 0.95
Memset speed - (rust) block size 1048576 30203.27 MByte/s (±24434.78 MByte/s) 31110.89 MByte/s (±25033.24 MByte/s) 0.97
Memset speed - (rust) block size 16777216 26680.99 MByte/s (±22021.65 MByte/s) 28386.93 MByte/s (±23265.03 MByte/s) 0.94
alloc_benchmarks Build Time 86.87 s 74.76 s 1.16
alloc_benchmarks File Size 0.86 MB 0.87 MB 0.98
Allocations - Allocation success 91.38 % 91.31 % 1.00
Allocations - Deallocation success 100.00 % 100.00 % 1
Allocations - Pre-fail Allocations 61.60 % 61.44 % 1.00
Allocations - Average Allocation time 10327.38 Ticks (±172.61 Ticks) 5860.58 Ticks (±98.43 Ticks) 1.76
Allocations - Average Allocation time (no fail) 10768.99 Ticks (±136.64 Ticks) 6554.81 Ticks (±92.86 Ticks) 1.64
Allocations - Average Deallocation time 2305.56 Ticks (±690.29 Ticks) 1805.01 Ticks (±250.35 Ticks) 1.28
mutex_benchmark Build Time 88.12 s 79.82 s 1.10
mutex_benchmark File Size 0.89 MB 0.86 MB 1.03
Mutex Stress Test Average Time per Iteration - 1 Threads 13.16 ns (±0.54 ns) 12.10 ns (±0.41 ns) 1.09
Mutex Stress Test Average Time per Iteration - 2 Threads 88.86 ns (±2.94 ns) 40.26 ns (±1.68 ns) 2.21

This comment was automatically generated by workflow using github-action-benchmark.

The scheduler is tickless (a TSC-deadline one-shot timer), so a CPU-bound
task that never blocks or yields keeps its core indefinitely. Add an
optional `preemptive` feature that round-robins between ready tasks via a
time slice.

A new `Source::Preemption` timer slot is armed for `now + 10ms` whenever the
scheduler switches to a task while others are ready, and whenever
`custom_wakeup` makes a task ready (so a freshly woken task preempts a
non-yielding runner soon). When the slice expires, the timer handler's
existing `reschedule()` switches to the next ready task. With no contention
nothing is armed, so the system stays tickless.
Comment thread src/scheduler/mod.rs Outdated
Comment on lines +413 to +414
let deadline = processor::get_timer_ticks() + PREEMPTION_SLICE_US;
timer_interrupts::create_timer_abs(timer_interrupts::Source::Preemption, deadline);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let deadline = processor::get_timer_ticks() + PREEMPTION_SLICE_US;
timer_interrupts::create_timer_abs(timer_interrupts::Source::Preemption, deadline);
timer_interrupts::create_timer(timer_interrupts::Source::Preemption, PREEMPTION_SLICE_US);

same for all the other calls. The function create_timer needs its #[cfg(feature = "net")] replaced with #[cfg(any(feature = "net", feature = "preemptive"))]

@mkroening mkroening left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address @Gelbpunkt's comment. Then, this is fine with me. 👍

@mkroening mkroening assigned stlankes and unassigned mkroening Aug 22, 2026
@stlankes

Copy link
Copy Markdown
Contributor Author

Why is feature net required?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants