Skip to content

lanes: WS-B step 3 — shared-prefix KV across lanes: value + mechanism feasibility (Step 0, expected NO-GO) #154

Description

@penta2himajin

Why this issue exists

"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 ...)"
    .
  • 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 prefix cache (sharedStore / convStore in LaneBatchSlots) is already
    shared 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.
  • 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?

Existing evidence says no. notes/22 §4 (post-fix bench, 3 simultaneous 30.8K admits):

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:

  1. Fan-out workload with a real shared prefix across lanes (the 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 recurrence shape —
    tools/lane_stage_b_probe.mjs / scripts/bench_lane_stage_b.sh already generate it;
    QWISP_LANE_PREFIX_MB controls the store).
  2. 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.
  3. 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)

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.

Refs

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 request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions