Skip to content

SSD session cache telemetry: where restores spend their time, where they land, and how much the cache writes - #647

Open
jvmenen wants to merge 4 commits into
youssofal:mainfrom
jvmenen:feat/ssd-cache-telemetry
Open

jvmenen wants to merge 4 commits into
youssofal:mainfrom
jvmenen:feat/ssd-cache-telemetry

Conversation

@jvmenen

@jvmenen jvmenen commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Why

The SSD session cache reports one number per restore, ssd_restore_s. That cannot tell whether a slow restore waits on the disk, on copying into arrays or on the GPU. It also cannot tell, on hybrid models where recurrent layers resume only at a stored checkpoint, how much of a matched prompt is computed again. And it does not show how much the cache writes compared with its hourly write budget. Those are the numbers that decide what to improve next.

How it works, in plain terms

  • Restore time, split. While a restore runs, the codec adds its time to three counters: reading blobs, copying them into arrays, and the GPU work that joins them. The remainder (manifest, payload, refused rows) is reported separately, so the parts add up to restore_s. Outside a restore nothing is timed.
  • Where restores land. For every restore from RAM or SSD, the request records how many prompt tokens matched, at which checkpoint the state resumed, the gap between the two, and whether the entry came from the same conversation or another one with the same prompt head. This covers the regular lanes, the Gemma 4 assistant backend and the batched MTP lane; on non-hybrid models the restore point is the matched length and the gap is 0.
  • Write volume. The writer counts the bytes it writes and the bytes it skips because the blob is already on disk, split into recurrent checkpoints and the rest, over a rolling hour.

What changes

  • Request log: ssd_restore_read_s, ssd_restore_copy_s, ssd_restore_eval_s, ssd_restore_other_s, ssd_restore_bytes, and prefix_restore_matched_tokens, prefix_restore_point_tokens, prefix_restore_checkpoint_gap_tokens, prefix_restore_partial, prefix_restore_origin.
  • The restore log line gains read_s, copy_s, eval_s and bytes.
  • /health and /admin/cache/ssd:
    • restore totals and the last split;
    • write_volume, with written and deduplicated bytes per hour and since start, and the share of the write budget used;
    • session_bank.restore_landings counters per source.
  • New MLX-free module mtplx/cache_bank/telemetry.py; small changes in the codec, cold tier, session bank, generation, the Gemma 4 assistant path and the server. No restore or write decision changes.

Cost

On an M5 Pro: 3.6 ms on a restore of 20,000 blobs (one second or more), and 1.3 ms per written entry on the writer thread.

Tests

tests/test_cold_tier_telemetry.py and tests/test_restore_landings_other_lanes.py (23 tests, temporary directories only) check that:

  • the split adds up to restore_s and its bytes match the blobs on disk;
  • a partial restore counts only what it reads;
  • the rolling hour drops old buckets;
  • landings are counted per source and origin, also on the Gemma 4 and batched MTP paths.

Full suite: 11,258 passed, 0 failed.

Every SSD restore now says where its time went: reading blobs from disk,
copying them into MLX arrays and the GPU evaluation, plus the bytes read.
The split is in the "SessionBank SSD restored" log line, in the restore
record and in the request log (ssd_restore_read_s, ssd_restore_copy_s,
ssd_restore_eval_s, ssd_restore_other_s, ssd_restore_bytes), with running
totals in the cold-tier stats. The codec adds to a context-local counter
that only a restore sets, so the lazy boundary loader and the encode stay
untimed.

Every RAM and SSD restore records where it landed: tokens matched, the
recurrent checkpoint it resumed at, the gap between the two, the suffix
prefilled and whether the entry came from the same conversation. The
request log carries prefix_restore_* fields; /health sums them per source
under session_bank.restore_landings.

The writer counts the bytes it put on disk and the bytes it found already
there, checkpoint blobs apart from the rest, in one-minute buckets over a
rolling hour, next to the hourly write budget (write_volume in /health
ssd_session_cache and /admin/cache/ssd).
…tched MTP lane

Both lanes restore through the session bank outside generation's
restore_or_prefill and recorded no landing, so their requests carried
zeros. They now build the same served record (served_restore_record),
count the landing in the bank's restore_landings and fill the request
fields: prefix_restore_* always, and the SSD restore split with
ssd_cache_hit, ssd_restore_s and ssd_cached_tokens when the entry came
from the SSD tier.

Gemma 4 has no recurrent layers: its restores land on the matched length,
recorded as restore point equal to the match and a gap of 0. The Gemma 4
prompt state takes the request's session id for the same/other-session
origin; the restore itself does not use it. A failure in the batched
lane's telemetry never turns its restore into a miss.
… SSD

A KV-only model (no recurrent state) restored through the regular path
lands on the matched length: restore point equals the match, the gap is
0 and hybrid is false, with the same/other-session origin. The SSD
restores also fill the restore split. Covers the exact and the prefix
restore from RAM and from the SSD tier.
A deduplicated blob was either referenced by a memo (never evaluated,
copied or hashed) or captured and hashed and only then found on disk.
The write volume counted both as deduplicated, and the spill path never
counted its dedupe hits at all, so the wasted capture and hash work was
invisible.

Each write now reports referenced, hashed-duplicate and hashed-new blobs
and bytes, with the capture and hash seconds of the hashed ones
(write_* counters and last_write_split in /health ssd_session_cache and
/admin/cache/ssd), and write_volume splits its deduplicated bytes into
deduped_referenced_bytes_* and deduped_hashed_bytes_*. deduped_blob_hits
now counts spill writes too. Two perf_counter calls per captured blob.
jvmenen pushed a commit to jvmenen/MTPLX that referenced this pull request Oct 20, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant