PCP: in-kernel ring cache phase, fused with the current phase - #3367
Draft
wenxindongwork wants to merge 3 commits into
Draft
PCP: in-kernel ring cache phase, fused with the current phase#3367wenxindongwork wants to merge 3 commits into
wenxindongwork wants to merge 3 commits into
Conversation
Adds cache_phase="ring_fused": the in-kernel ring cache rounds and the causal current phase run in ONE kernel launch sharing the online-softmax state, eliminating the separate current-phase launch and the merge_attn_states LSE merge. 14-23% faster than the two-launch ring at 8k-64k contexts and the fastest cache-phase strategy outright at 8k-16k; neutral at 512k-1M where ICI link utilization dominates. Opt-in, never auto-selected. Region behavior inside the bkv loop is selected by value (zero-size DMAs, scalar-selected masks), not control flow. The all-gathered current KV is fetched piecewise per head/tail chunk so ring-sized blocks can cross chunk boundaries; the ring sync release keeps per-block signals with only the last ring block's release deferred past the current region; the (seq,bq)-crossing prefetch is issued post-attention to avoid overwriting the slot the last current block reads. Signed-off-by: wenxindongwork <wenxindong@google.com>
DescriptionStart with a short description of what the PR does and how this is a change from The rest of the description includes relevant details and context, examples:
If the change fixes a Github issue, please include a link, e.g.,: TestsPlease describe how you tested this change, and include any instructions and/or ChecklistBefore submitting this PR, please make sure:
|
The PR shipped two ring cache-phase variants: a cache-only ring launch merged with a separate current launch via merge_attn_states, and the fused single-launch version. Keep only the fused one and rename it cache_phase="ring": - kernel: ring_enabled now IS the fused ring; delete the pure-ring index remap, the round-0-only bkv wait, the pure-ring release schedule, and the cache-only validation arm (ring now requires use_causal_mask=True, skip_cache_attn=False, skip_current_attn=False). update_kv_cache is no longer needed by static_validate_inputs. - wrapper: cache_phase accepts gather_kv / gather_q / ring; the old "ring" (two-launch) branch is gone; unused logger removed; the gather-KV branch comment from main restored. - tests: kernel-level pure-ring test reverted (it targeted the deleted launch mode); wrapper tests exercise ring vs gathers agreement and the multi-block ring schedule. Signed-off-by: wenxindongwork <wenxindong@google.com>
… reuse main's paths Structural consolidation, no behavior change to non-ring paths (they are now byte-identical to main): - The ring launch takes the current KV in TOKEN order (wrapper uses to_token_order), so the in-kernel piecewise chunk-crossing fetch is gone and the rank-order remap stays exactly main's code; the block-size chunk clamp moves back to its main location. - fused_no_causal/fused_kv_lower collapse into ONE runtime in_ring flag: the current-region lower bound reuses the existing skip_cache_attn mask lines (the same bound made runtime), and causal-off is one OR. - get_cp_local_size and the cache-update path are restored to main; ring code uses a tiny get_cp_shard_len(x, rank) helper. - start/wait_fetch_bkv become **kw passthroughs; the virtual-index bookkeeping derives (block, round, slot) for v and v+1 from one ring_ids() helper; the ring-wait predicate simplifies to ~in_ring | round0; the two last-round release sites merge into one predicate. Kernel diff vs main: 519 -> 369 changed lines; PR total 537+ -> 410+. Signed-off-by: wenxindongwork <wenxindong@google.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
cache_phase="ring": an in-kernel ring cache phase forrpa_v3_cpthatruns the ring cache rounds AND the causal current phase in ONE kernel launch
with a shared online softmax — no separate current-phase launch and no
merge_attn_statesLSE merge. This is the only ring variant this PR ships:an earlier revision also carried a two-launch ring (cache-only ring launch +
LSE merge), which was strictly slower and has been removed to keep the change
minimal.
Design
The launch keeps the two-seq (head, tail) structure of the current phase, with
both seqs forced to a full
Cso every rank runs the identical ring schedule(pad rows are discarded by the caller, like the existing all-pad-tail case).
Per bq block, the bkv loop walks a virtual index
v:v in [0, ring_num_bkv * P): ring rounds over the rotating cache shards(each rank's striped shard travels the ring via in-kernel
make_async_remote_copy, double-buffered in the existing bkv slots);v in [ring_region, end): the current-KV blocks, causal, with theskip-cache lower bound applied at runtime.
Region behavior is selected by VALUE, not control flow: zero-size DMAs for the
non-active fetch kind,
causal | in_ringfor the mask, scalar-selectedeffective_kv_len/ lower bound — nolax.cond(which measurably regressesin this kernel). Slot parity stays uniform across the region boundary
(
slot = v mod 2; the ring region length is even because P is even).The region behavior is selected by VALUE inside the shared code paths: the
current-region cache-mask is the existing
skip_cache_attnbound maderuntime, fetch gating zeroes DMA sizes, and one
in_ringscalar drives allof it. Three synchronization/layout subtleties, the first handled in the
wrapper and the other two with dedicated regression tests:
KV (contiguous only within one head/tail chunk), safe because its block
size is capped at
C. The ring launch keeps ring-sized bkv (~2 MB hops)that cross chunks, so the wrapper hands it TOKEN-order KV instead and the
kernel's fetch stays a plain contiguous copy.
P-1 (the next writer is the rotation itself). Only the LAST ring block's
slot stays live through the current region, so only its release is
deferred to the end of the bq block loop — keeping signals per bq equal to
waits (a per-bq-only release deadlocks multi-block rings).
next bq's ring block 0 at slot 0 — potentially the same slot the last
current block is still computing from. The crossing prefetch is issued
after that block's attention instead of before.
Performance
v7x-8, per-request cumulative prefill TTFT (ms), CH=4k, pcp=8 tp=1, NQ=32
NKV=2 HD=256 bf16 ("two-launch ring" is the removed variant, kept as a
reference point):
Fusing removes the fixed per-step cost of the second launch + LSE merge and
hides the first ring hop: 14–23% faster than the two-launch ring at
8k–64k, and the fastest strategy outright at 8k–16k (below gather_kv's
range). At 512k–1M the ring's asymptote is ICI link utilization and per-bq
cache re-streaming, which launch structure does not touch; deeper pipelining
(HBM-spilled
(m,l,acc)) remains the follow-up for long-context parity.ringstays opt-in and is never chosen by the auto heuristic; if the 8k–16kwin holds up on more shapes, wiring it into the auto ladder
(
ring< ~16k <gather_kv< ~128k <gather_q) is a natural follow-up.Testing
tests/layers/common/test_pcp_attention_interface.py:test_cache_phase_strategies_agree: ring vs gather_kv vs gather_q mustagree (P=2, 4).
test_ring_multi_block: cache large enough that each rank's shard spansseveral ring bkv blocks — reaches the sync-release schedule and the
crossing prefetch, which small-cache configs cannot.
five configs incl. partial/all-pad tails and a boundary block with
cache_len not bkv-aligned): max |Δ| 3.0–4.9e-4 — the same magnitude as an
LSE-merge's own noise — negative control (different cache contents) 50–100x
larger, and the strided KV cache write is BIT-EXACT vs the gather_kv path.