Settings: let idle SPUs sleep instead of spinning on a reservation

SPU GETLLAR Busy Waiting Percentage defaults to 100 upstream, meaning always
busy-wait. That suits a desktop, where the SPU threads have cores of their own
and spinning costs nothing else. Here six of them share eight cores with the PPUs
and the RSX, so a spinning SPU takes a core from the threads doing the work.

Measured on Spider-Man: Web of Shadows: process_mfc_cmd accounted for 55% of all
CPU across the process, and making its inner loop cheaper did not move the frame
rate -- the loop just ran more iterations in the same wall clock. That is what
identified it as a spin rather than as work, after two rounds of optimising the
iteration itself.

20 still favours a short busy-wait, so a reservation that frees quickly is caught
without a scheduler round trip, and only a wait that history says is long goes to
sleep. A deliberate change in All Core Settings still wins, since core overrides
replay after this.
This commit is contained in:
jpolo1224
2026-08-10 23:56:15 -04:00
parent e8c499056b
commit a873ae8250
@@ -1260,6 +1260,21 @@ data class Settings(
// above it would already be 60; setting it explicitly means the cap does not depend
// on the vblank path holding, which it did not. Enum node, so the value is quoted.
runCatching { net.rpcsx.RPCSX.instance.settingsSet("Video@@Frame limit", "\"60\"") }
// Let idle SPUs sleep instead of spinning on a reservation.
//
// Upstream defaults this to 100, which means always busy-wait. That is right for a
// desktop, where six SPU threads have cores of their own and spinning costs nothing
// else. Here they share eight cores with the PPUs and the RSX, so a spinning SPU is
// taking a core away from the threads doing the work.
//
// Measured on Spider-Man: Web of Shadows: process_mfc_cmd was 55% of all CPU across
// the process, and making its inner loop cheaper did not move the frame rate at all --
// the loop simply ran more iterations in the same wall clock, which is what identified
// it as a spin rather than as work.
//
// 20 still favours a short busy-wait, so a reservation that frees quickly is caught
// without a scheduler round trip; only a wait that history says is long goes to sleep.
runCatching { net.rpcsx.RPCSX.instance.settingsSet("Core@@SPU GETLLAR Busy Waiting Percentage", "20") }
// Compatible Savestate Mode is no longer forced off here; applyTo writes it from
// ps3.savestateCompatibleMode above, so the two costs it carries -- SPU performance
// and a 500MB to 3GB state file -- are the user's to accept rather than a decision