Skip to content

prefix-cache-aware admission: parallel sub-agent fan-out should share the prefill, not re-pay it (measured: batch path loses 2.6x to serialize for lack of this) #121

Description

@penta2himajin

Motivation (measured, PR #120 harness)

Real OpenCode parallel fan-out = N sub-agent streams arriving simultaneously with a shared 12.8K-char system prefix and same-shape prompts, all greedy. Replayed against both server paths (64GB resident, real captured trace):

path identity vs strict total wall
serialize (default) 6/6 byte-identical 72.3s
QWISP_BATCH=4 3/6 diverge 190.6s (2.6x slower)

The batch path's loss is not decode speed — it is admission: ContinuousBatch.admit() runs a standalone full prefill per request (~9K tokens each, no prefix cache, no SuffixSpec), so N concurrent same-prefix requests pay N full prefills. The serialize path wins because #117's RAM-tier prefix cache absorbs the shared prefix (repeat TTFT ~0.2-0.4s). Agentic traffic is prefill-dominated with heavy shared prefixes — any admission that ignores that loses.

Design directions (in increasing ambition)

  1. Serialize-path admission ordering (cheap, likely small): when multiple requests are queued on the AsyncLock, grant the lock to the request that best extends the current arena path / RAM store (instead of FIFO). Maximizes warm restores when conversations interleave. May be a no-op in practice — FIFO already arrives prefix-grouped in the captured trace; measure before building.
  2. Batch-path admission via the prefix stores (the real fix if batch is revived): admit() consults PrefixRAMStore / in-path slots — restore the longest shared-prefix state into the slot, prefill only the delta. Removes the N× re-prefill penalty that caused the 2.6x loss. Requires the batch model's cache format to accept the Seedless blob (persistentStateData) or its own store — nontrivial: the batch path is MLX QwispModel, a different cache layout.
  3. Auto-route (product-level): detect concurrency + shared prefix → stay on serialize (cache-hot, lossless); route to batch only when the workload is decode-dominated with cold-unique prompts (the only regime where batch's uniform admission wins). Could be as simple as: batch only admits requests whose best prefix hit is below a threshold.

Note the strict ordering of value: (3) prevents the regression today with ~no code, (2) is what spec-in-batch would ALSO need (prefix reuse is a prerequisite for batch to beat serialize at all on this traffic), (1) is measure-first.

Acceptance gate

The #120 harness is the gate: replay diff on the captured OpenCode trace. A variant must (a) beat serialize's 72.3s total wall on the fan-out trace, or (b) for routing-only work, never lose to it; identity vs the strict serial reference reported alongside (6/6 for lossless paths; divergence count quantified otherwise).

Relations

🤖 Generated with Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestparkedWorkstream paused/on hold — infra shipped, not actively worked

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions