mirror of
https://github.com/elwrcl/scx_soryu.git
synced 2026-10-31 10:58:03 +03:00
110 lines
4.8 KiB
Markdown
110 lines
4.8 KiB
Markdown
# `--gpu-boost-cap`: don't deboost the render thread
|
|
|
|
A GPU feeder is a task that submitted GPU work within the last 100 ms, as seen
|
|
by a `tp_btf/i915_request_add` hook: a game's render thread, the compositor,
|
|
Xwayland. BMQ deboosts a task for burning whole slices, which is exactly what a
|
|
render thread does on a heavy frame, and strict priority then queues it behind
|
|
every better level. `--gpu-boost-cap` stops a recent feeder from being
|
|
deboosted past the given boost.
|
|
|
|
**Result: `-1` removes most of the stutter, and is the default.**
|
|
|
|
The idea came from reading the Infinity scheduler (galpt/infinity-scheduler),
|
|
whose v4.8 couples CPU and GPU accounting. Its GPU half patches `drm_sched`,
|
|
which i915 does not use, so none of that applies to Ivy Bridge. What carried
|
|
over was the question of whether the CPU side lets the GPU's feeders wait.
|
|
|
|
## What was run
|
|
|
|
Half-Life 2 (native GL, Mesa crocus), the same 60 s camera pan each round, on
|
|
the Ivy Bridge target (i5-3210M, 2c/4t, HD 4000, kernel 7.2.8). Frame times
|
|
from MangoHud's CSV log; scheduler numbers from `SORYU_LOG` at one report per
|
|
second. Both settings ran from the same binary, alternating off, on, off, on,
|
|
so the knob is the only thing that differs.
|
|
|
|
## Finding the cause
|
|
|
|
The first instrument only saw wake->run, and feeders looked fine there: p99
|
|
around 0.5-0.8 ms with or without load. The render thread's problem is not
|
|
waking up. It rarely sleeps mid-frame; it burns its slice, stops still
|
|
runnable, and waits to get the CPU back -- a path that never passes
|
|
`ops.runnable` and so was invisible.
|
|
|
|
Measuring that requeue wait, split by the feeder's boost at the moment it
|
|
stopped (one 60 s pan, knob off):
|
|
|
|
| | p99, median second | p99.9, worst second | seconds with p99.9 > 4 ms |
|
|
| --- | --- | --- | --- |
|
|
| boosted (boost < 0) | 0.92 ms | 3.1 ms | 0 of 72 |
|
|
| deboosted (boost >= 0) | 1.0 ms | **25.2 ms** | **6 of 61** |
|
|
|
|
Boosted, the wait never went past a few slices. Deboosted, it reached two
|
|
frames at 85 fps. Feeder boost spent that run at a median of -7.6, so the
|
|
render thread was crossing into the deboosted class constantly.
|
|
|
|
## Frames, idle desktop
|
|
|
|
| | median frame | p99.9 frame | frames > 2x median | frames > 25 ms | 0.1% low |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| off, round 1 | 13.2 ms | 44.0 ms | 15 | 27 | 22.6 fps |
|
|
| **-1, round 1** | **12.5 ms** | **26.1 ms** | **6** | **6** | **37.8 fps** |
|
|
| off, round 2 | 12.7 ms | 43.2 ms | 23 | 26 | 21.7 fps |
|
|
| **-1, round 2** | **12.5 ms** | **25.7 ms** | **8** | **8** | **37.9 fps** |
|
|
|
|
The two rounds of each setting agree closely. Frames over 25 ms fell by about
|
|
70%.
|
|
|
|
On the scheduler side, deboosted feeder requeues went from ~785/s to ~1/s,
|
|
while the cap itself fired only ~35 times a second -- a small intervention.
|
|
|
|
## Under load
|
|
|
|
The same pan with `stress-ng --cpu 4 --timeout 60s` running alongside:
|
|
|
|
| | avg fps | 1% low | 0.1% low | stress-ng ops/s (real) | ops/s (usr+sys) |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| off | 60.8 | 30.0 | 21.2 | 1026 | 778 |
|
|
| **-1** | **73.3** | **46.6** | **36.8** | 750 | 753 |
|
|
|
|
## The cost
|
|
|
|
stress-ng lost 27% of its throughput. Its ops per CPU-second barely moved (778
|
|
against 753), so it did not run slower -- it got less CPU, 60 CPU-seconds
|
|
instead of 79 over the minute, and the game took the difference. That is the
|
|
trade the knob exists to make.
|
|
|
|
Nothing starved. The worst wait was ~130 jiffies in both rounds, which is the
|
|
`starve_ns` bound itself, and `starve_serve` moved from 1.2/s to 1.5/s.
|
|
|
|
On an idle desktop the other side of the trade shows up in the `deboosted`
|
|
class: its wake->run p99 went from ~0.5 ms to 1.0-1.3 ms.
|
|
|
|
The cap applies to anything that submits GPU work, including an OpenCL render
|
|
(DaVinci, beignet/pocl). A build running alongside one will lose a similar
|
|
share. That was judged acceptable for a desktop; `--gpu-boost-cap=12` turns it
|
|
off.
|
|
|
|
## Dropped: waking out of a GPU wait
|
|
|
|
The first version also hooked `i915_request_wait_begin/end` and
|
|
`dma_fence_wait_start/end`, to time feeders waking from a GPU wait. Over 20
|
|
minutes of play that saw 1251 such wakeups against 434k submissions: crocus
|
|
does not block in the kernel for its fences, so there was nothing there to
|
|
schedule. The hooks were removed.
|
|
|
|
## For reference: EEVDF
|
|
|
|
With soryu unloaded, the same game under `stress-ng --cpu 4` ran at 8-11 fps
|
|
(read off the overlay, one run), against 40-50 for soryu without the cap. On an
|
|
idle desktop EEVDF's 1% low was 47.7 against 43.2 for soryu without the cap,
|
|
which is the stutter this change removes; with it, soryu's 1% low is 46.6-47.3.
|
|
|
|
## Open
|
|
|
|
- One game. A Vulkan title through hasvk, and a game whose render thread
|
|
rarely burns a full slice, would say whether -1 is right in general or only
|
|
for this workload.
|
|
- No AMD numbers. Without `/sys/kernel/btf/i915` the hook does not load and the
|
|
cap has no feeders to act on; an amdgpu equivalent would need its own
|
|
tracepoint.
|