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)
- 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.
- 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.
- 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
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):
QWISP_BATCH=4The 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)
admit()consultsPrefixRAMStore/ 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 MLXQwispModel, a different cache layout.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 diffon 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