Download raw body.
Change to quadratic scaling of estcpu penalty
@tech,
The 4.4BSD priority calculation in setpriority() uses linear scaling for both CPU consumption (p_estcpu) and administrative weight (nice) to get a total value.
While easy to reason, this linearity creates some less desirable characteristics under load:
- Short interactive bursts are penalized linearly, which is somewhat aggressive. A process that runs for 6 ticks is penalized 6 points, making tab-complete or editor redraws instantly look like CPU hogs to the scheduler.
- Positive nice values scale linearly. nice +19 is exactly 38 steps from default, which fails to adequately separate heavy background jobs from normal system daemon activity.
This diff replaces the linear math with quadratic curves while preserving the existing system maximums and negative nice behavior.
For CPU consumption, the proposed penalty is (estcpu^2) / estcpu_max. A 6-tick burst yields a penalty of 1 (previously 6). A 36-tick hog yields a penalty of 36 (identical to legacy maximum).
For positive nice values, change the offset to add a (nd^2 / 16) term to stretch them a bit further. At nice +5, the offset is +6 (previously +10). At nice +19, the offset is +41 (previously +38), additionally pushing on background tasks. Negative nice values retain their historic 2x weight multiplier to guarantee they can elevate below PUSER.
No changes in state variables, no new magic numbers, and bit shifts are used to keep the calculation computationally simple. As the function no longer implements 4BSD logic, move it to kern_sched. Comments added because comments are nice.
Tim
--
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -620,6 +620,39 @@ sched_proc_to_cpu_cost(struct cpu_info *ci, struct proc *p)
return (cost);
}
+/*
+ * Calculate the scheduling rank of a process based on its execution
+ * history and nice value.
+ *
+ * The priority penalty scales quadratically based on recent CPU
+ * consumption and positive nice values. This allows short interactive
+ * bursts to remain responsive while progressively penalizing sustained
+ * CPU hogs and background jobs.
+ */
+void
+setpriority(struct proc *p, uint32_t newcpu, uint8_t nice)
+{
+ unsigned int newprio;
+ int nd = nice - NZERO;
+ const uint32_t estcpu_max = NICE_WEIGHT * PRIO_MAX - SCHED_PPQ;
+
+ /* Apply quadratic CPU penalty. */
+ newprio = PUSER + (newcpu * newcpu) / estcpu_max;
+
+ if (nd < 0) {
+ /* Use weight for negative nice values. */
+ int pri = newprio + (NICE_WEIGHT * nd);
+ newprio = max(pri, 0);
+ } else {
+ /* Scale positive nice quadratically (nd + nd^2 / 16). */
+ newprio += nd + ((nd * nd) >> 4);
+ }
+
+ SCHED_ASSERT_LOCKED();
+ p->p_estcpu = newcpu;
+ p->p_usrpri = min(newprio, MAXPRI);
+}
+
/*
* Peg a proc to a cpu.
*/
diff --git a/sys/kern/sched_bsd.c b/sys/kern/sched_bsd.c
--- a/sys/kern/sched_bsd.c
+++ b/sys/kern/sched_bsd.c
@@ -499,21 +499,6 @@ setrunnable(struct proc *p)
p->p_slptime = 0;
}
-/*
- * Compute the priority of a process.
- */
-void
-setpriority(struct proc *p, uint32_t newcpu, uint8_t nice)
-{
- unsigned int newprio;
-
- newprio = min((PUSER + newcpu + NICE_WEIGHT * (nice - NZERO)), MAXPRI);
-
- SCHED_ASSERT_LOCKED();
- p->p_estcpu = newcpu;
- p->p_usrpri = newprio;
-}
-
/*
* We adjust the priority of the current process. The priority of a process
* gets worse as it accumulates CPU time. The cpu usage estimator (p_estcpu)
Change to quadratic scaling of estcpu penalty