Skip to content

perf(kernel): the NV2A ack thread backs off when busy, off Windows - #113

Merged
phobos665 merged 1 commit into
mainfrom
perf/ack-busy-backoff
Nov 2, 2026
Merged

phobos665 merged 1 commit into
mainfrom
perf/ack-busy-backoff

Conversation

@phobos665

Copy link
Copy Markdown
Owner

The NV2A ack thread polls the GPU register words a title writes as plain memory. Nothing tells it when one changes, so while a title is busy it yields in a loop, which costs a fifth to a third of a host core for the whole run. That is power and thermal headroom the guest's own core needs on a laptop or a phone.

This turns on the busy backoff that was already there: RECOMP_NV2A_ACK_BUSY_US now defaults to 50 µs everywhere except Windows. After eight passes in a row that find nothing, the thread sleeps 50 µs between passes. Any change it finds puts it straight back to yielding.

It was left off because nobody had measured the titles that pace on the GPU fence. OutRun 2 is one of them, and it shows no cost.

Measurements

Apple M4, macOS. Runs alternate between 0 and 50 so machine load lands on both sides equally. Each run was muted and in the background.

Title Ack thread, share of a core Frame rate
OutRun 2 (fence-paced) 8% → 1.7% 60 fps either way, same frame times with the cap on and off (2 runs each)
TimeSplitters 2 20–28% → 2–3% 60 capped either way; uncapped 119 → 117 fps
Constantine 25–35% → 2% 60 capped either way; uncapped 94 → 92 fps

The only cost is 1.5–3% of the uncapped top end in TimeSplitters 2 and Constantine, from one run each per setting. Nothing is measurable at 60.

Scope

  • Windows is unchanged. The default there stays 0 until someone measures it on Windows, so this needs no Windows regression.
  • Not measured: Burnout 2, the other fence-paced title.
  • To revert: RECOMP_NV2A_ACK_BUSY_US=0 restores the old loop on any host. RECOMP_NV2A_ACK_STATS=1 prints the thread's CPU use and how often a change was found right after a sleep.

RECOMP_NV2A_ACK_BUSY_US defaults to 50 everywhere but Windows. It was off
because the titles that pace on the GPU fence had not been measured; on an
M4, alternated runs of 0 against 50:

  OutRun 2 (fence-paced): 60 fps, same frame times capped and uncapped;
                          the thread 8% of a core -> 1.7%
  TimeSplitters 2:        20-28% -> 2-3%, uncapped 119 -> 117 fps
  Constantine:            25-35% -> 2%,   uncapped 94 -> 92 fps

Nothing measurable at 60 in any of them. Windows keeps 0 until it has been
measured there; RECOMP_NV2A_ACK_BUSY_US=0 restores the old yield loop.
@phobos665
phobos665 merged commit 5046949 into main Nov 2, 2026
5 of 7 checks passed
@phobos665
phobos665 deleted the perf/ack-busy-backoff branch November 2, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant