perf(kda): preserve SM90 workspace byte layout with bulk copies - #115
perf(kda): preserve SM90 workspace byte layout with bulk copies#115fkuner wants to merge 3 commits into
Conversation
|
Thanks for working on this optimization. Could you explicitly credit flash-flash-kda as the source of the raw workspace transport idea in the PR description and/or code comments? |
icavan
left a comment
There was a problem hiding this comment.
The SM90 intracard path's pre_scan appears to still load ws_kd and ws_kr through TensorMap/TMA (make_ws_qkd_atom). Since this change stores the physical K_INTER byte images and updates K2 to restore them via raw bulk G2S copies, how is the pre_scan path handled?
Could you please make sure this workspace-format change is also applied correctly to the intracard path—for example, by migrating the corresponding pre_scan loads to raw bulk G2S or otherwise preserving format compatibility—and validate it with tests/test_kda_sm90_intracard_cp.py? As written, it looks possible for pre_scan to reinterpret the raw byte image as the previous logical workspace layout.
Thanks for pointing this out. I’ll take a closer look at the intracard |
|
Hi, I have one dependency question before updating the package metadata. This PR's raw workspace path calls For context, the previous Tensor TMA store path did manually elect one issuing lane: if warp_idx == 0:
with cute.arch.elect_one():
cute.copy(tma_atom_ws_qd, ...)
...The optimized code keeps that manual We therefore cannot simply wrap the raw copies in Would you prefer that we:
I have not changed |
Summary
K_INTERSMEM byte image for the SM90 KDAqd,kd, andkrworkspacescp.async.bulkS2G/G2S copiesinvandmqkon layout-aware Tensor TMApre_scanconsumerAcknowledgment
The raw workspace transport idea comes from Shikhar Mishra's Flash-Flash-KDA, which identified the same fragmented TensorMap workspace boundary and preserved the physical shared-memory byte image with non-tensor
cp.async.bulkcopies. This PR adapts that idea to cuLA's SM90 CuTeDSL implementation and itsCHUNK=16workspace ABI. The source is also credited next to the K1 raw workspace stores in code.Motivation
K1 and K2 use the same
K_INTERshared-memory layout forqd,kd, andkr. The previous path stored these tensors through TensorMap/TMA into a logical linear workspace and reconstructed the layout in K2. Flash-Flash-KDA identified this layout traversal as a source of fragmented TMA work for the same workspace boundary; cuLA's NCU results below show the corresponding reduction in observable store/load-class executions, XBAR traffic, and kernel duration. NCU does not expose the internal TensorMap segment count directly.Because both kernels agree on the physical layout, the workspace can instead be treated as an opaque byte image:
pre_scancopy the same image back with raw bulk G2S.The implementation deliberately leaves the other workspace tensors on Tensor TMA where layout-aware transport remains appropriate.
Figure 1. Raw bulk copies preserve three 4 KiB physical K_INTER byte images for qd/kd/kr; inv/mqk remain on layout-aware Tensor TMA.
Workspace transport
The optimization applies to
qd,kd, andkr, whose physicalK_INTERshared-memory layout is identical across their producers and consumers. Each cuLA tensor is a contiguous 4 KiB byte image (16 × 128 × sizeof(bf16)).The baseline uses layout-aware Tensor TMA to convert between the
K_INTERSMEM layout and a logical linear GMEM workspace. The optimized path preserves each tensor as an opaque byte image: K1 writes three contiguous payloads with raw bulk S2G copies, while K2 restores all three and intracardpre_scanrestoreskd/krwith raw bulk G2S copies.invandmqkare intentionally unchanged and continue to use layout-aware Tensor TMA. The diagram is conceptual and does not claim an exact internal TensorMap segment count; the observable NCU evidence is reported below.Performance
Commit A/B summary
use_intracard_cp=False)s_split=8)Common setup:
d78f0075c69135fe191358fe8daf10e7383009d433f8da18594af07b2e7ab52103b2c6f280e516e3(current PR head698738643134f80d6079360b441d269d58c4a279only restores the benchmark's original dispatch behavior)D=128,CHUNK=160)Non-intracard details
These runs explicitly pass
use_intracard_cp=False, so they contain no intracard planning,pre_scan, or merge work. Warmup: 25 iterations.Uneven 8Kdenotes[1300, 547, 2048, 963, 271, 3063].1 × 81928 × 10241 × 81928 × 1024Intracard-CP details
These runs force the same manual
s_split=8plan on both commits, so every row executes K1, intracardpre_scan, merge, and segmented K2 rather than relying on an auto heuristic that might fall back to serial. Warmup: 10 iterations.[8192][8192, 2048][16384][8192][8192, 2048][16384]The CP-on measurements show smaller end-to-end gains for these tested
H=4/8shapes. They are not a direct comparison with the CP-offH=64/96cases above. A likely reason is that complete CP latency also includespre_scanrecurrence, merge, and segmented recurrence work that is unchanged by the raw workspace transport. Confirming the phase-level contribution would require a dedicated CP-on NCU breakdown.Comparison with Flash-Flash-KDA
Flash-Flash-KDA's deep dive reports isolated workspace-transport latency reductions of 23%, 34%, and 37% on H100 for fixed, uneven packed, and uniform packed inputs with
CHUNK=16. cuLA also usesCHUNK=16; its complete CP-off prefill A/B on H200, where raw workspace transport is the only kernel implementation difference, shows 11.9–20.3% latency reductions. This is not claimed as an exact reproduction of Flash-Flash-KDA's isolated percentages.It is nevertheless comparable in direction and end-to-end scale: the vLLM FlashKDA integration PR reports 1.145–1.240x for complete pure-prefill calls on GB300, while the six CP-off cuLA cases above span 1.134–1.255x. Hardware, kernel scheduling, and the other optimizations included by Flash-Flash-KDA differ, so the raw transport contribution should not be compared as if these were identical kernels.
Broader cuLA shape sweep
The existing
benchmarks/bench_kda_sm90_prefill.pysweep also uses the non-intracard path. It callsflashkda_prefilldirectly; the defaultuse_intracard_cp=Noneresolves to the serial K1+K2 path, so the benchmark file does not need to override dispatch explicitly. With its normal 25 warmup / 100 measured iterations, baselined78f007versus the original optimized commit74af6d6produced:Nsight Compute evidence
NCU 2025.2.1,
--set full, B=1 / T=4096 / H=64:K1 accounts for about 88% of the combined NCU duration reduction, matching the hypothesis that workspace stores were the dominant opportunity.
NCU groups both
UTMASTGandUBLKCP.Gunder its TMA-store-class metric. The optimized K1 SASS adds threeUBLKCP.G.Sworkspace operations per CTA, but the aggregate dynamic count falls from 50 to 5 operations per CTA: three raw workspace copies plus the two retained Tensor TMA stores. Internal TensorMap layout-walk segments are not exposed as a standalone NCU counter, so the evidence is the observable instruction/request count, XBAR traffic, and kernel duration rather than an inferred segment count.Validation
pre-commit run --all-filespython -m py_compile cula/ops/kda/sm90/k1.py cula/ops/kda/sm90/k2.py cula/ops/kda/sm90/cp/pre_scan.py benchmarks/bench_kda_sm90_prefill.pypytest -q -m "not kda_slow" tests/test_kda_sm90_intracard_cp.pys_split=8) baseline/optimized A/B with deterministic output/final-state comparisonbenchmarks/bench_kda_sm90_prefill.pysweep