Measured the cross-request prefill cost on a multi-stage local pipeline — numbers + LRUPromptCache orchestration #1549
thekiraproject
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
We run a local compound pipeline — one 9B serving all roles (classify →
reason-with-tools → examine → synthesize), 8-bit, on an M4 Pro 24GB via mlx-lm.
Before optimizing anything, we instrumented where model time actually goes, and
found that 44–47% of it was prefill — re-encoding byte-identical stage
prefixes (system prompts, tool schemas, few-shots) on every call of every query.
We'd been blind to it since migrating off llama.cpp's automatic cross-request
prefix reuse; in-process mlx-lm didn't carry that behavior for us, and nobody
reopened the "we get this for free" assumption.
Per-query measurement (pre-cache):
The fix used mlx-lm's own machinery: wired
LRUPromptCachein, pinned eachstage's static prefix once per process lifetime, and chained within-query so
agent-loop turn N+1 extends turn N's cache. The biggest single lever was a
static-first prompt partition — a cache only reuses an unbroken prefix, so
one dynamic block early in the prompt ends the reusable region. Reordering
(system ∥ schemas ∥ few-shots first, dynamic content last) took our worst stage
from 26% → 96% cacheable.
End-to-end after activation: standard −34%, agentic −45%, research −24%.
Two things that might be useful upstream:
visible — we were flying blind because our client reported prompt-eval as
zero. Anything that nudges these into view would save others the blind months
we had.
cacheable prefix. "Exact reuse is exact, so output can't change" stops being
a correctness argument the moment the prompt itself is reordered — the model
sees different bytes. We applied the partition unconditionally in both configs
and gated activation on a byte-equivalence output-parity battery before
turning it on.
Full write-up with the activation discipline and a defensive disclosure of the
persistence design:
https://github.com/thekiraproject/kira-project-site/blob/main/posts/2026-07-06-prefill-tax.md
Happy to share measurement methodology if it's useful — curious whether others
running multi-stage setups on mlx-lm see similar prefill shares.
All reactions