Appearance
aowlspt_rdcache.h
Source: abi/aowlspt_rdcache.h — 222 lines, 12 file-scope functions.
What this header owns
Reproduced verbatim from the header's own banner comment — these notes are frequently the only written record of why the subsystem is shaped the way it is.
text
aowlspt_rdcache.h -- a CACHE in front of the readability syscall, for the
host's per-frame GAME-DATA reads only.
WHY. `aowl_is_readable` (abi/aowlspt_shim.h) is one `VirtualQuery` SYSCALL per
call, and the host's guarded-read discipline means one call per POINTER HOP.
MEASURED on the deployed build (natesp's own sub-phase meter, aowlspt-host.log):
sub[scan]: mean=23052.9us max=37983.2us n=1387
for a census bounded at 64 contacts. The census makes roughly ten guarded hops
per contact (slot, IsYourPlayer, MovementContext + PreviousPosition, and the
five-hop Profile->Info->Settings side/role walk), i.e. ~600 VirtualQuery calls
per pass. That is the cost, and it is a syscall cost, not a work cost.
The identical remedy has already been measured twice in this repo
(mods/maps/sp/rdcache.h): -85% on maps, -96% on admin. This file is that same
cache, moved to the host so the host's own hot paths can use it.
THE ASYMMETRY IS THE WHOLE DESIGN, and it is not a tuning knob:
A STALE POSITIVE IS A CRASH. A STALE NEGATIVE IS A DECLINE.
so positives get a SHORT ttl (250ms) and are stored ONLY for regions of 64KB
or more -- a heap segment or an image section, the things that do not get
unmapped between two frames. A small region that happened to read positive is
answered honestly and then FORGOTTEN, counted in `uncacheable`. Negatives are
cheap to be wrong about, so they get 2000ms.
SIZING. A 32-slot round-robin v1 FAILED in the maps mod: it hit within a call
and missed across calls, because the working set is the set of distinct 64KB
chunks the walk touches, which is hundreds. 1024 sets x 2 ways is sized for
that, and `evict-live` MEASURES whether it still is -- a rising evict-live is
the table being too small, reported rather than assumed.
DRIFT. The predicate below is replicated clause-for-clause from
`aowl_is_readable`, applied to an mbi we already hold so a miss costs ONE
syscall rather than two. To keep the copy from silently drifting, one miss in
AOWL_RDC_AUDIT_EVERY also calls the real `aowl_is_readable` and compares.
`aowl_rdc_disagree()` MUST read 0; non-zero means this file has drifted and is
a measurement, not a claim.
SCOPE -- READ THIS BEFORE ADDING A CALLER. This is for reading GAME DATA in a
per-frame path. It is deliberately NOT wired into:
- detour binding or prologue verification (aowlspt_prologue.h, codegen),
- `aowl_is_code_pointer`,
- anything that decides whether to WRITE or PATCH.
Those are one-off, are not hot, and are exactly the places where a stale
positive is unrecoverable. They keep calling the uncached shim, unchanged.
IT OPENS NO GUARD AND INSTALLS NOTHING. No detour, no name resolved, no RVA
called, nothing in the game dereferenced. It sits strictly outside any
`aowl_p_p_seh` (that guard is NOT re-entrant), holds only fixed-size static
storage, and allocates nothing, managed or otherwise.Constants
AOWLSPT_RDCACHE_HAOWL_RDC_AUDIT_EVERYAOWL_RDC_CHUNK_SHIFTAOWL_RDC_MIN_POS_SIZEAOWL_RDC_NEG_TTL_MSAOWL_RDC_POS_TTL_MSAOWL_RDC_SETSAOWL_RDC_WAYS
Functions
| Signature | Line |
|---|---|
void aowl_rdc_flush(void) | 99 |
int32_t aowl_rdc_pred(const MEMORY_BASIC_INFORMATION* mbi, uintptr_t a, uintptr_t need) | 108 |
uint32_t aowl_rdc_set_of(uintptr_t tag) | 123 |
int32_t aowl_rdc_readable(void* p, int32_t size) | 132 |
int64_t aowl_rdc_hits(void) | 212 |
int64_t aowl_rdc_misses(void) | 213 |
int64_t aowl_rdc_flushes(void) | 214 |
int64_t aowl_rdc_uncacheable(void) | 215 |
int64_t aowl_rdc_evict_live(void) | 216 |
int64_t aowl_rdc_expired(void) | 217 |
int64_t aowl_rdc_audits(void) | 218 |
int64_t aowl_rdc_disagree(void) | 219 |

