CPU Scheduling Calculator (CFS)
Calculate CFS virtual runtime and estimated time slice from nice value and task weight.
Inputs
Default sysctl kernel.sched_latency_ns ≈ 24ms
Virtual Runtime (vruntime, ms)
40.000
Estimated Time Slice (ms)
4.800
Task Weight (from nice value)
1,024
Nice 0 Reference Weight
1,024
Scheduling Effect vs. Nice 0
Same priority as nice 0 — vruntime accrues 1:1 with actual runtime.
Step by step
Values used
Actual Runtime Accumulated (ms) = 40; Task Nice Value (-20 to 19) = 0; Runnable Tasks on This CPU = 5; sched_latency_ns Target (ms) = 24; sched_min_granularity_ns (ms) = 3
Virtual runtime
vruntime = actual_runtime × (nice_0_weight / task_weight)
Approximate time slice
time_slice ≈ max(min_granularity, sched_latency × (task_weight / nice_0_weight) / runnable_tasks)
Virtual Runtime (vruntime, ms)
= 40.000
Estimated Time Slice (ms)
= 4.800
Task Weight (from nice value)
= 1,024
Nice 0 Reference Weight
= 1,024
Scheduling Effect vs. Nice 0
= Same priority as nice 0 — vruntime accrues 1:1 with actual runtime.
How it works
Linux's Completely Fair Scheduler (CFS) doesn't use fixed time slices; instead every runnable task accrues virtual runtime (vruntime) at a rate inversely proportional to its weight (derived from nice value via the kernel's sched_prio_to_weight table, where nice 0 = weight 1024), and CFS always picks the runnable task with the lowest vruntime to run next — so a higher-weight (lower nice) task's vruntime grows more slowly, letting it run more often without ever using a literally longer time slice. The approximate time slice a task gets in one scheduling period is its weight's share of sched_latency_ns, floored by sched_min_granularity_ns so very high task counts don't shrink slices to unreasonably small durations.
Formulas
Virtual runtime
vruntime = actual_runtime × (nice_0_weight / task_weight)
- t
- actual runtime
- w_0
- nice 0 weight (1024)
- w_i
- this task's weight
Approximate time slice
time_slice ≈ max(min_granularity, sched_latency × (task_weight / nice_0_weight) / runnable_tasks)
- L
- sched_latency_ns target
- w_i
- task weight
- w_0
- nice 0 weight
- n
- runnable tasks
Frequently Asked Questions
Why doesn't CFS use fixed time slices like older schedulers?
Fixed time slices scale poorly with varying numbers of runnable tasks and don't cleanly support proportional priority. CFS instead tracks each task's accumulated vruntime and always runs whichever runnable task has the least of it, which naturally gives higher-weight (lower nice) tasks more CPU time over time without needing per-task slice-length bookkeeping.
What does a negative nice value actually do under CFS?
It increases the task's weight (nice -20 ≈ weight 88761 vs. nice 0's 1024), which makes its vruntime accrue roughly 87× slower per unit of real CPU time — so it stays at the front of the 'lowest vruntime runs next' queue far more often, effectively getting far more CPU share under contention.
What is sched_min_granularity_ns for?
It's a floor on how small a computed time slice can get. Without it, a system with hundreds of runnable tasks sharing sched_latency_ns could compute vanishingly small per-task slices, causing excessive context-switch overhead; the floor trades a bit of fairness/latency for reduced scheduling overhead under high task counts.