You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"WS-B step 3: shared-prefix KV across lanes" has been carried in HANDOFF.md's
follow-up list as a single line with no spec anywhere — not in notes/22, not in an
issue, not in code comments. It has been re-inherited across sessions as a name.
This issue writes it down so it can be decided instead of re-inherited. It is framed
as a Step 0 (the same shape as #143): establish value and mechanism before designing
anything. The expected outcome is not-planned — both criteria below currently look
closed. Recording that reasoning is the deliverable; if it survives, a design issue gets
opened separately.
What it would actually mean
Grounded in the code rather than the name:
Each lane owns its KV outright — SeedlessLaneBatch.swift: "B concurrent sequences
("lanes"), each with its OWN caches (KV arena + GDN ...)".
So step 3 is not a speed lever. It would save bytes: N lanes serving a common
prefix hold N copies of the same KV. The payoff would be more concurrency inside QWISP_LANE_KV_MB (default 8GB on >=48GB boxes, 4GB below), at ~20KB/token
(LaneBatchSlots.kvBytesPerToken, 10 attn layers x KV=2 x D=256 x 2B x {K,V}).
Anything proposing to keep a released lane's state alive and adopt it is a different,
already-terminal idea — see Rejected below.
Kill criterion 1 — is duplicated prefix KV a material cost?
Peak footprint (never ps rss): 39,936 MB on a 64GB box. [...] the arenas
themselves are only 3 x 30795 x 20KB ~= 1.9GB, so the excess is prefill/MoE scratch,
not KV.
If those three lanes had shared their entire prefix, the saving would be at most
2/3 x 1.9GB ~= 1.2GB out of a 39.9GB peak — about 3%, in exchange for a change the
mechanism section says we cannot legally make. That also undercuts the premise that QWISP_LANE_KV_MB is what bounds concurrency: at that measurement KV was ~5% of peak
and prefill/MoE scratch dominated.
Weakness of that evidence, and the measurement that closes it (M1 — do this first):
the notes/22 run did not use a shared prefix, so it measures duplication only
incidentally. M1 must measure the case this issue is actually about:
Report, per lane count B in {2, 3, 4}: peak footprint, total arena bytes
(arenaBytes), and the duplicated share = (B-1) x sharedPrefixLen x 20KB.
Use footprint <pid>, never ps rss (it undercounts wired GPU pages — HANDOFF).
Kill if duplicated prefix KV is < 10% of peak footprint. Rationale for the bar:
the only known mechanisms (below) require touching a frozen kernel or risking L1, so a
single-digit-percent memory win cannot justify it. If it lands above 10%, this criterion
survives and criterion 2 decides.
Kill criterion 2 — can it be built without touching a frozen kernel or L1?
sdpa_rows (SeedlessMetalForward.swift) takes keys/values as one contiguous buffer [KV, totalSeq, D] plus head/seq strides and a per-row baseN. There is no block
table and no indirection. A lane's KV must therefore be one contiguous region, so [shared prefix][private suffix] cannot be composed from two buffers. Two mechanisms
exist and both are blocked:
(a) Add a page/block table to sdpa_rows (the vLLM paged-attention shape). sdpa_rows is named explicitly in the frozen forward path — CLAUDE.md Prohibition 3
and HANDOFF Do-Not-Touch ("sdpa_rows especially").
(b) Split into two dispatches (shared prefix, private suffix) and combine partial
softmaxes. This changes the accumulation order, which is exactly the failure mode
recorded in memory strict-lossless-verify-fix — batched kernels here are not
order-stable with respect to shape. Strict L1 is defined as bit-exact reproduction
of the quantised greedy token stream, so an order change is a lossless violation
unless proven otherwise, and proving it is a larger project than the win.
Kill unless a third mechanism is identified that touches neither sdpa_rows nor the
accumulation order. Aliasing tricks are not automatically a third mechanism: any scheme
that gives two lanes the same physical prefix bytes while letting them append different
suffixes still has to make each lane's view contiguous.
Rejected (do not re-propose as step 3)
Live warm-lane reuse — keeping a released lane's state and adopting it on prefix
match. Terminally rejected: GDN state is a recurrence and cannot be rewound to the
prompt boundary, and end-of-decode KV rows are decode-kernel-produced, so they are not
bit-identical to the canonical hybrid-prefill stream. 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's blob-at-prompt-boundary is
the only L1-safe shape. See notes/22 §REJECTED and memory lane-live-warm-reuse-nogo.
Distinct from this issue (that one shares across time, this one across lanes) but
killed by the same L1 constraint family.
Definition of done
Either:
Closed not-planned with M1's numbers and the mechanism verdict recorded here, and
the line removed from HANDOFF.md's follow-up list — this is the expected and
acceptable outcome; or
M1 shows >10% duplication and a third mechanism exists → open a design issue and
link it here. Do not design inside this issue.
Why this issue exists
"WS-B step 3: shared-prefix KV across lanes" has been carried in
HANDOFF.md'sfollow-up list as a single line with no spec anywhere — not in
notes/22, not in anissue, not in code comments. It has been re-inherited across sessions as a name.
This issue writes it down so it can be decided instead of re-inherited. It is framed
as a Step 0 (the same shape as #143): establish value and mechanism before designing
anything. The expected outcome is not-planned — both criteria below currently look
closed. Recording that reasoning is the deliverable; if it survives, a design issue gets
opened separately.
What it would actually mean
Grounded in the code rather than the name:
SeedlessLaneBatch.swift: "B concurrent sequences("lanes"), each with its OWN caches (KV arena + GDN ...)".
sharedStore/convStoreinLaneBatchSlots) is alreadyshared across lanes — but what it shares is a restore blob, which removes the
re-prefill time for a shared prefix. Each lane still materialises its own copy of
the resulting KV.
prefix hold N copies of the same KV. The payoff would be more concurrency inside
QWISP_LANE_KV_MB(default 8GB on >=48GB boxes, 4GB below), at ~20KB/token(
LaneBatchSlots.kvBytesPerToken, 10 attn layers x KV=2 x D=256 x 2B x {K,V}).Anything proposing to keep a released lane's state alive and adopt it is a different,
already-terminal idea — see Rejected below.
Kill criterion 1 — is duplicated prefix KV a material cost?
Existing evidence says no.
notes/22§4 (post-fix bench, 3 simultaneous 30.8K admits):If those three lanes had shared their entire prefix, the saving would be at most
2/3 x 1.9GB ~= 1.2GB out of a 39.9GB peak — about 3%, in exchange for a change the
mechanism section says we cannot legally make. That also undercuts the premise that
QWISP_LANE_KV_MBis what bounds concurrency: at that measurement KV was ~5% of peakand prefill/MoE scratch dominated.
Weakness of that evidence, and the measurement that closes it (M1 — do this first):
the notes/22 run did not use a shared prefix, so it measures duplication only
incidentally. M1 must measure the case this issue is actually about:
tools/lane_stage_b_probe.mjs/scripts/bench_lane_stage_b.shalready generate it;QWISP_LANE_PREFIX_MBcontrols the store).footprint, total arena bytes(
arenaBytes), and the duplicated share = (B-1) x sharedPrefixLen x 20KB.footprint <pid>, neverps rss(it undercounts wired GPU pages — HANDOFF).Kill if duplicated prefix KV is < 10% of peak footprint. Rationale for the bar:
the only known mechanisms (below) require touching a frozen kernel or risking L1, so a
single-digit-percent memory win cannot justify it. If it lands above 10%, this criterion
survives and criterion 2 decides.
Kill criterion 2 — can it be built without touching a frozen kernel or L1?
sdpa_rows(SeedlessMetalForward.swift) takes keys/values as one contiguous buffer[KV, totalSeq, D]plus head/seq strides and a per-rowbaseN. There is no blocktable and no indirection. A lane's KV must therefore be one contiguous region, so
[shared prefix][private suffix]cannot be composed from two buffers. Two mechanismsexist and both are blocked:
sdpa_rows(the vLLM paged-attention shape).sdpa_rowsis named explicitly in the frozen forward path — CLAUDE.md Prohibition 3and HANDOFF Do-Not-Touch ("
sdpa_rowsespecially").softmaxes. This changes the accumulation order, which is exactly the failure mode
recorded in memory
strict-lossless-verify-fix— batched kernels here are notorder-stable with respect to shape. Strict L1 is defined as bit-exact reproduction
of the quantised greedy token stream, so an order change is a lossless violation
unless proven otherwise, and proving it is a larger project than the win.
Kill unless a third mechanism is identified that touches neither
sdpa_rowsnor theaccumulation order. Aliasing tricks are not automatically a third mechanism: any scheme
that gives two lanes the same physical prefix bytes while letting them append different
suffixes still has to make each lane's view contiguous.
Rejected (do not re-propose as step 3)
match. Terminally rejected: GDN state is a recurrence and cannot be rewound to the
prompt boundary, and end-of-decode KV rows are decode-kernel-produced, so they are not
bit-identical to the canonical hybrid-prefill stream. 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's blob-at-prompt-boundary is
the only L1-safe shape. See
notes/22§REJECTED and memorylane-live-warm-reuse-nogo.Distinct from this issue (that one shares across time, this one across lanes) but
killed by the same L1 constraint family.
Definition of done
Either:
the line removed from
HANDOFF.md's follow-up list — this is the expected andacceptable outcome; or
link it here. Do not design inside this issue.
Refs
swift/Sources/QwispCore/SeedlessLaneBatch.swift— per-lane cache ownershipswift/Sources/QwispCore/LaneServe.swift—LaneBatchSlots(sharedStore/convStore,kvBytesPerToken,canAdmit),LaneAdmitPlanswift/Sources/QwispCore/SeedlessMetalForward.swift—sdpa_rowssignaturenotes/22-ws-b-stage-b-ctx-adaptive-spec.md§4 and §REJECTED