This commit is contained in:
elwrcl committed 2026-09-04 23:53:20 +03:00
1 parent b64e40d88d
commit 3a89080495
7 files changed
+590 -3

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
# `--boost-on-wake`: the typo wins
BMQ's `sched_task_ttwu()` is meant to boost a waking task in proportion to how
long it slept. That is what makes sleeping for 1 us worth nothing and closes
the sleep-gaming hole. The line reads
```c
s64 delta = this_rq()->clock_task > p->last_ran;
```
A comparison, not a subtraction. `delta` is 0 or 1, the shift takes it to 0,
and the function has been dead code in every shipped BMQ build. `soryu` made it
a knob rather than silently picking a side: `--boost-on-wake=true` is what BMQ
meant to do, `false` is what BMQ actually does.
**Result: off is better, and it is not close.**
## What was run
The scheduler compiling itself, twice per configuration, on the Ivy Bridge
target (i5-3210M, 2c/4t, kernel 7.1.6). Each run logged one report every 10 s
with `--report-sec 10`, giving ~34 intervals per run. Rounds 1 and 2 were run
in separate working trees.
Every number below is per-interval, not cumulative. That matters: before the
histograms were differenced, a run printed one figure blended across every
phase the build went through, which described none of them.
## Latency
Interactive (`boosted` class) wake->run, median across intervals:
| | p99 | p99.9 | tail >1 ms |
| --- | --- | --- | --- |
| on, round 1 | 524 us | 1.30 ms | 0.152% |
| on, round 2 | 524 us | 1.80 ms | 0.168% |
| **off, round 1** | **459 us** | **0.92 ms** | **0.075%** |
| **off, round 2** | **459 us** | **0.92 ms** | **0.077%** |
The two `off` runs agree to the last digit on all three columns.
Pairing the runs interval by interval on the >1 ms tail:
| | intervals | off better | on better | sign test | median ratio |
| --- | --- | --- | --- | --- | --- |
| round 1 | 34 | 31 | 3 | p = 7.7e-07 | 0.53 |
| round 2 | 34 | 32 | 2 | p = 6.9e-08 | 0.45 |
| pooled | 68 | 63 | 5 | p = 7.7e-14 | 0.47 |
Worst single interval, which is the number a stutter would live in:
| | worst tail >1 ms | worst p99.9 | worst `max_wait` |
| --- | --- | --- | --- |
| on, round 1 | 0.299% | 4.2 ms | 21 j |
| on, round 2 | 1.787% | 21.0 ms | 38 j |
| off, round 1 | 0.656% | 7.3 ms | 22 j |
| off, round 2 | 0.129% | 0.92 ms | 21 j |
Each configuration produced one bad round, and `on`'s was worse: two
consecutive intervals at 21 ms p99.9.
## Throughput
| | round 1 | round 2 |
| --- | --- | --- |
| on | 5 m 34 s | 5 m 45 s |
| off | 5 m 39 s | 5 m 32 s |
The ordering flips between rounds. With n = 2 per configuration and a ~10 s
spread already known from earlier runs, there is no throughput difference to
report in either direction. The latency result stands on 68 paired intervals;
the timing result stands on nothing, and is not claimed.
## Health
`insert_fail = 0`, `starve_serve` 22-26 across all four runs, `max_wait` 21-22
jiffies except the one 38 j interval noted above. No stalls, no ejections.
## Why the default flipped
Two independent arguments point the same way. The measurement says off. And
off is BMQ as actually shipped -- the behaviour a decade of field use already
ran, rather than a resurrection nobody has tested. The knob stays so the
question can be reopened on other hardware.
## Open question
The mechanism is not understood. The wake boost grants up to 24 steps at once,
so a waking task is slammed to the floor rather than climbing at +1 per sleep,
and the guess is that this makes a task's level change often enough that
`cpu_level[]` is stale when `try_preempt()` reads it. That is a guess. It would
be tested with a per-task counter of how often the level computed at enqueue
differs from the one seen at `ops.running`.
Note this is not the boost distribution collapsing. Under compile load both
configurations sit bimodal at roughly 54% on the -12 floor and 39% on the +12
ceiling, with about 5% spread across the twenty-three values between, and
`preempt_none` stays near 1.2% in both.
That bimodality was expected to disable preemption -- if everything sits at one
level, nothing outranks anything and `try_preempt()` finds nobody worth an IPI.
It does not, because the range saturates at *both* ends rather than one: the
crowd on the floor always has a ceiling hog to displace. Saturation at one end
would have killed preemption; saturation at both preserves it.