Inline the FIFO cache hit path

fetch_u32 is called once per FIFO command word and the guest pushes about 52,000
of them per frame, while the cache refills only around 208 times. Better than
99% of calls are a compare and a load, but the function lived out of line in
RSXFIFO.cpp for the sake of the rare refill, so every one of those 52,000 was a
real call into another translation unit.

It measured 15.6% of RSX thread samples, which no amount of refill work
explains: 208 refills of a 1KB cache is a few tens of microseconds of copying.
The cost was call overhead, not work.

Splits the refill into fetch_u32_refill and leaves the hit path inline.
This commit is contained in:
jpolo1224
2026-08-08 16:32:09 -04:00
parent a8607c2fcb
commit 1c2f13fa5a
2 changed files with 24 additions and 2 deletions
+1 -1
View File
@@ -86,7 +86,7 @@ namespace rsx
}
}
std::pair<bool, u32> FIFO_control::fetch_u32(u32 addr)
std::pair<bool, u32> FIFO_control::fetch_u32_refill(u32 addr)
{
if (addr - m_cache_addr >= m_cache_size)
{
+23 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "util/types.hpp"
#include "util/endian.hpp"
#include "Emu/RSX/gcm_enums.h"
#include <span>
@@ -153,7 +154,28 @@ namespace rsx
u32 translate_address(u32 addr) const;
std::pair<bool, u32> fetch_u32(u32 addr);
/**
* Read one command word, serving it from the local cache when possible.
*
* The hit path is inlined and the refill is not, deliberately. This is called
* once per FIFO command word and the guest pushes on the order of 52,000 of
* them per frame, while the cache refills only about 208 times, so better than
* 99% of calls are a compare and a load. Leaving the whole thing out of line
* made every one of them a real call into another translation unit for the sake
* of the rare case, and fetch_u32 was 15.6% of RSX thread samples against only
* 18us of actual refill work per frame.
*/
std::pair<bool, u32> fetch_u32_refill(u32 addr);
inline std::pair<bool, u32> fetch_u32(u32 addr)
{
if (addr - m_cache_addr >= m_cache_size) [[unlikely]]
{
return fetch_u32_refill(addr);
}
return {true, read_from_ptr_unsafe<be_t<u32>>(+m_cache[0], addr - m_cache_addr)};
}
void invalidate_cache() { m_cache_size = 0; }
u32 get_pos() const { return m_internal_get; }