SPU: stop an out-buffer verdict from spinning GETLLAR forever

The out-buffer check answers 'unlikely to be a loop', and that answer is not
free. It resets the spin count and leaves the busy-waiting switch at umax, so the
caller skips busy_wait, skips the sleep path, and returns immediately: the SPU
re-executes GETLLAR at full rate with no backoff. The spin count never reaches 4,
so the spin optimisation is never evaluated, and the 400ms fallback that would
force a sleep is never reached either. One SPU in that state holds a core flat
out, and the setting meant to control this has nothing to act on.

Spider-Man: Web of Shadows sits in that case. Its GETLLAR sites use an LSA in the
top 64K of local store, which is what the check looks for, and process_mfc_cmd
measured 55% of all CPU across the process while the game ran at 10-15fps.

Re-entering the same site with the same stack 32 times is itself the evidence
that it is a loop, whatever the LSA looks like. After that the verdict is dropped
and the normal spin detection decides between busy-waiting and sleeping. Any real
change of site or stack resets the count, so a genuine OUT buffer still gets the
original treatment.
This commit is contained in:
jpolo1224
2026-08-11 00:01:45 -04:00
parent a873ae8250
commit ba5e4ebd66
2 changed files with 29 additions and 1 deletions
+24 -1
View File
@@ -4330,6 +4330,7 @@ bool spu_thread::process_mfc_cmd()
// Seemingly not
getllar_busy_waiting_switch = umax;
getllar_spin_count = 0;
getllar_outbuf_hits = 0;
return true;
}
@@ -4337,6 +4338,7 @@ bool spu_thread::process_mfc_cmd()
{
getllar_busy_waiting_switch = umax;
getllar_spin_count = 0;
getllar_outbuf_hits = 0;
return true;
}
@@ -4375,8 +4377,29 @@ bool spu_thread::process_mfc_cmd()
getllar_cs_first = cs.empty() ? umax : cs[0].second;
}
if (getllar_cs_first != umax && last_getllar_lsa > getllar_cs_first)
// Stop believing the verdict once it is contradicted by repetition.
//
// Saying "not a loop" here is not free. It resets the spin count and
// leaves the switch at umax, and the caller then skips both busy_wait
// and the sleep path and returns immediately, so the SPU re-executes
// GETLLAR at full rate with no backoff at all. The spin count never
// reaches 4, so the optimisation is never evaluated, and the 400ms
// "don't be stubborn" fallback that would force a sleep is never
// reached either. One SPU in that state holds a core flat out.
//
// Web of Shadows sits in exactly that case: its GETLLAR sites use an
// LSA in the top 64K of local store that looks like a caller's OUT
// buffer, and process_mfc_cmd measured 55% of all CPU across the
// process while the game ran at 10-15fps.
//
// Re-entering the same site with the same stack this many times is
// itself the evidence that it is a loop, whatever the LSA looks like,
// so hand it back to the normal spin detection and let that decide
// between busy-waiting and sleeping.
if (getllar_cs_first != umax && last_getllar_lsa > getllar_cs_first &&
getllar_outbuf_hits < 32)
{
getllar_outbuf_hits++;
getllar_busy_waiting_switch = umax;
getllar_spin_count = 0;
return true;
+5
View File
@@ -822,6 +822,11 @@ public:
u32 getllar_cs_sp = umax;
u32 getllar_cs_lr = umax;
u32 getllar_cs_first = umax;
// Consecutive "not a loop" verdicts from that check at the same site. The verdict suppresses
// both the busy-wait and the sleep, so believing it forever leaves the SPU re-entering
// GETLLAR at full rate; repetition is the evidence that it is wrong.
u32 getllar_outbuf_hits = 0;
u32 getllar_busy_waiting_switch = umax; // umax means the test needs evaluation, otherwise it's a boolean
u64 getllar_evaluate_time = 0;