metal: stop expert-miss readahead racing the pread pool (+13% GLM streaming decode)#533
metal: stop expert-miss readahead racing the pread pool (+13% GLM streaming decode)#533jasontitus wants to merge 3 commits into
Conversation
The three miss-load paths issued F_RDADVISE for the exact ranges the pread pool reads immediately after. The advise fcntl is synchronous on the eval thread while the pool fetches the same bytes in parallel, so it only adds stall time: a thread sample during steady-state streaming decode shows 40% of the eval thread blocked in these fcntl calls. Set DS4_METAL_ENABLE_STREAMING_EXPERT_MISS_READAHEAD=1 to restore the old behavior. The other readahead sites are unchanged.
One extra line next to the streaming expert timing report (same DS4_METAL_STREAMING_EXPERT_TIMING_SUMMARY gate): dispatches, tasks, bytes, average dispatch wall time, effective queue depth (qd_avg = sum of per-task pread ms / pool wall ms) and delivered bandwidth. Makes streaming IO regressions measurable without dtrace.
Reader threads inherit the parent QoS: launched via taskpolicy -b or a background launchd job they end up on the throttled IO tier and every expert miss pays for it. Pin them to user-initiated QoS and IOPOL_IMPORTANT; DS4_METAL_DISABLE_STREAMING_EXPERT_PREAD_QOS opts out.
|
We ran the PR branch on the cell the writeup extrapolates to ("the win Box: M5 Pro 64 GB, macOS 26, Flash IQ2XXS (80.8 GiB file, cache budget 33.3 Short chat prompt (11 tokens,
Generation is a wash, but time to first token (short-prompt prefill is
Your dispatch stats show why. Same bytes in both arms (35.52 GiB, 16167 Why the stall you sampled does not show up here: the two branches have So the miss advise is not the problem, issuing it on the eval thread is. The dispatch stats are a great addition, they are what made this visible at |
During steady-state SSD-streaming decode the eval thread spends much of its
time in
fcntl(F_RDADVISE): every expert-cache miss advises the gate/up/downranges and then immediately hands the exact same ranges to the pread pool.
The advise fcntl is synchronous on the eval thread, while the pool reads the
bytes in parallel either way, so the advise only adds stall time. A 5 s
thread
sampleduring GLM 5.2 streaming decode (the same three call sitesexist on the
glm5.2branch):fcntl(F_RDADVISE)inreadahead_range(miss paths)waitUntilCompletedCommits
three miss-path advise sites (single miss, batch load, early load).
DS4_METAL_ENABLE_STREAMING_EXPERT_MISS_READAHEAD=1restores the oldbehavior. The prefill/layer readahead sites are untouched.
extra line under the existing
DS4_METAL_STREAMING_EXPERT_TIMING_SUMMARYgate: dispatches, tasks, bytes, effective queue depth (sum of per-task
pread ms / pool wall ms) and delivered bandwidth. This is how the numbers
below were measured; makes streaming IO regressions visible without
dtrace.
threads inherit the parent QoS, so under
taskpolicy -b/ backgroundlaunchd jobs every miss pays the throttled IO tier. Pin them to
user-initiated QoS +
IOPOL_IMPORTANT(
DS4_METAL_DISABLE_STREAMING_EXPERT_PREAD_QOSopts out).Measurements
One machine only: M1 Ultra Mac Studio, 128 GB RAM, 8 TB SSD (~7.2 GB/s
sequential), Metal backend.
GLM-5.2-UD-IQ2_XXS (rebased onto
glm5.2, where the change appliesclean;
--ssd-streaming --ssd-streaming-cache-experts 48GB --ssd-streaming-full-layers 16, prefill 2048 + 128 greedy):DeepSeek-V4-Flash Q4KExperts imatrix streaming on this branch
(
--ssd-streaming-cache-experts 48GB, prefill 512 + 128 greedy): nomeasurable difference (5.01 vs 5.21 gen t/s, inside the run-to-run spread —
two back-to-back pristine runs varied 2.66 vs 3.11 on the shorter form).
Expected: only ~25 GB of this quant misses RAM, so the miss rate — and the
advise tax — is small. The win concentrates on models far bigger than RAM.
Flash IQ2XXS imatrix resident (no
--ssd-streaming): bit-identicallogits and identical speed (25.7 t/s decode) — the changed code never runs
outside streaming mode.
Correctness: every A/B pair above was run with
--dump-frontier-logits-dirand the full frontier logits comparedbyte-for-byte — identical in all cases (the streaming baseline itself is
deterministic: two pristine runs match bit-for-bit).
Also tried while measuring: splitting each expert read into 2-8 striped
preads to raise queue depth. It lifts large preload dispatches
(5.1 -> 6.8 GB/s) but does not help decode misses, so it is not included.
Tests run
Speed/parity A/Bs with
ds4-benchas above.