mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user