Skip to content

[PyTorch][torch.compile] DotProductAttention: declarative packed QKV/KV inputs#3200

Open
pggPL wants to merge 16 commits into
NVIDIA:mainfrom
pggPL:dpa_packed_qkv_api
Open

[PyTorch][torch.compile] DotProductAttention: declarative packed QKV/KV inputs#3200
pggPL wants to merge 16 commits into
NVIDIA:mainfrom
pggPL:dpa_packed_qkv_api

Conversation

@pggPL

@pggPL pggPL commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Description

A fused QKV projection produces one packed buffer, but callers must slice it into query_layer/key_layer/value_layer views, which DotProductAttention then reverse-engineers via pointer-based layout detection (get_qkv_layout inspecting data pointers, strides and offsets on every forward). That detection graph-breaks under torch.compile and re-derives information the caller already knows.

This PR lets callers declare the packing instead. The declared layout is truthful by construction, so no detection runs on this path — including for thd and FP8 DPA.

DotProductAttention.forward gains three optional arguments (existing call sites unchanged):

  • qkv_layer — fully packed QKV, e.g. [b,s,3,h,d] or [s,b,h,3,d] (thd: [t,3,h,d]).
  • kv_layer — packed KV (e.g. [b,s,2,hg,d]), used with query_layer; supports GQA.
  • qkv_interleave_dim (default -3) — where the 3/2 interleave sits (-3 or -2); explicit since shapes can be ambiguous.

Handling lives in a single helper (_unpack_packed_qkv): q/k/v come out as zero-copy select() views, the exact layout string (bs3hd, sbhd_sb2hd, th3d, ...) is built from the declaration, and inputs are strictly validated (mutual exclusion, rank/size at the interleave dim, stride(-1) == 1 so the declared layout cannot lie about memory, no inference_params).

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

  • DotProductAttention.forward: new optional qkv_layer / kv_layer / qkv_interleave_dim arguments; packed inputs are resolved by _unpack_packed_qkv into zero-copy q/k/v views plus a declared layout, skipping get_qkv_layout entirely.
  • get_qkv_layout emits a DeprecationWarning when it detects a packed layout purely from pointers, pointing callers at the new arguments. Separate q/k/v never warn.
  • FP8: the original packed buffer is threaded down as packed_qkv/packed_kv and combine_and_quantize(combined_qkv=..., combined_kv=...) quantizes it directly instead of rebuilding it from views via combine_tensors (raw set_ with a silent adjacency assumption). Backward combine paths untouched.
  • MultiheadAttention passes its fused projection output straight to DPA (self-attention: qkv_layer; cross-attention: kv_layer). The legacy sliced-views path is kept for RoPE, QK norm, KV caching, CPU offloading, GQA and FP8 projection outputs.
  • Tests: _run_dot_product_attention gains a declarative_packed mode (packed buffer passed via qkv_layer/kv_layer, input grads read off the packed buffer); test_dpa_qkv_layout_declarative covers all 8 packed dense layouts on self- and cross-attention configs, test_dpa_qkv_layout_thd_declarative the 4 packed thd layouts (Hopper+). Verified on sm89 (bf16, fused+flash+unfused) with the existing test_dpa_qkv_layout matrix unchanged and green; lint clean.

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

pggPL and others added 13 commits July 7, 2026 17:35
Fused QKV projections naturally produce one packed buffer, but
DotProductAttention forces callers to slice it into q/k/v views that TE
then reverse-engineers with pointer-based layout detection
(get_qkv_layout inspects data_ptr/storage_offset on every forward,
which graph-breaks under torch.compile and adds CPU overhead).

Let callers declare the packing instead (JAX-style):

* DotProductAttention.forward gains optional qkv_layer (fully packed
  QKV: [b,s,3,h,d]/[s,b,3,h,d]/[b,s,h,3,d]/[s,b,h,3,d] dense, [t,3,h,d]/
  [t,h,3,d] thd), kv_layer (packed KV used with query_layer), and
  qkv_interleave_dim (-3 or -2; explicit knob rather than shape
  inference since h==3 or hg==2 would be ambiguous).
* Q/K/V are derived as zero-copy select() views and the exact layout
  enum (bs3hd, bsh3d, sb3hd, bshd_bs2hd, t3hd, ...) is constructed
  declaratively -- it is truthful by construction, so get_qkv_layout is
  never called on this path, including for thd and FP8 DPA.
* combine_and_quantize no longer re-combines what is already combined:
  a new optional combined= argument carries the caller's original
  packed buffer, which is quantized directly instead of rebuilding the
  packed buffer from q/k/v views via combine_tensors (a raw set_ with
  a silent adjacency/interleave assumption). The packed original is
  threaded from DPA.forward through FusedAttention to
  FusedAttnFunc.forward; all legacy call sites are untouched
  (combined=None preserves exact behavior), and backward combine calls
  are unchanged (gradients have no pre-packed original).

Tests: dense fwd+grad bit-exactness vs separate contiguous q/k/v for
bs3hd/bsh3d/sb3hd/kv-packed/GQA (fused + flash), validation errors,
torch.compile (no data_ptr/UntypedStorage graph breaks), FP8
combined-vs-views bit equivalence, and detection-free declared t3hd.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…claratively

Adopt the new DotProductAttention packed API inside MultiheadAttention:

* self-attention (np == ng): the fused QKV projection output, already
  viewed as [.., h, 3, d] (qkv_weight_interleaved) or [.., 3, h, d], is
  handed to DPA directly as qkv_layer with the matching
  qkv_interleave_dim (-2 / -3) -- no SplitAlongDim slicing in MHA and
  no pointer-based layout detection in DPA.
* cross-attention: the packed KV projection output is exposed as
  [.., hg, 2, d] / [.., 2, hg, d] and passed as kv_layer.
* The pass-through only engages when no per-tensor operation needs the
  individual q/k/v slices: it is skipped for RoPE, QK normalization,
  KV caching (inference_params), CPU offloading, GQA (np != ng, not a
  uniform 3-interleave) and quantized (FP8) projection outputs; those
  keep the legacy sliced-views path unchanged.

Tests: MHA self (interleaved + non-interleaved) and cross (both
interleaves) are bit-exact vs the same MHA with packed inputs converted
back to separate contiguous q/k/v (output, input grad, weight grads);
spy asserts the packed argument and interleave dim actually reach DPA;
GQA and RoPE fall back to the views path. TransformerLayer regression
suite unchanged; test_kv_cache failures on this device are pre-existing
on origin/main (verified).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…ack_packed_qkv

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
… arguments

Rename the single layout-dependent packed_qkv plumbing argument (which held
the full QKV buffer for *3* layouts but the KV buffer for *_2* layouts) into
explicit packed_qkv/packed_kv, mirroring the public qkv_layer/kv_layer API.
combine_and_quantize's combined= is split into combined_qkv=/combined_kv=
accordingly, and _unpack_packed_qkv no longer returns the packed tensor since
the callers already hold qkv_layer/kv_layer.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Fold the tests from the new test_dpa_packed_inputs.py file into the existing
attention test suite as a dedicated section, reusing its imports. No new test
file; test logic unchanged apart from renaming the module-level constants
(_B/_S/_H/_D/_DTYPE -> _PACKED_*) to avoid collisions.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
get_qkv_layout now emits a DeprecationWarning when it recognizes q/k/v as
views of a packed buffer purely from data pointers/strides/offsets (detected
*3*/*_2* layouts), pointing callers at the declarative qkv_layer/kv_layer
API. Separate q/k/v tensors (hd_hd_hd layouts) never warn since there is
nothing to declare.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Drop the API-validation, torch.compile graph-break, combine_and_quantize
equivalence and thd no-detection spy tests; keep the dense/flash bit-exact
equivalence tests and the MHA packed pass-through/fallback tests.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…arative param

Parametrize test_dpa_qkv_layout and test_dpa_qkv_layout_thd with
declarative={views,declarative}: the declarative mode passes the packed buffer
to DotProductAttention via qkv_layer/kv_layer (declared layout, gradients read
off the packed buffer) instead of slicing it into q/k/v views for pointer-based
detection. This reuses the whole existing config matrix (masks, bias, SWA,
cross-attention, thd, all backends) for the declarative API, replacing the
dedicated packed-input test section.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…matrix

Revert the declarative parametrization of test_dpa_qkv_layout(_thd) (which
doubled their whole config x layout product) and instead add
test_dpa_qkv_layout(_thd)_declarative covering all packed layouts on a trimmed
config dimension: one self-attention and one cross-attention config (kv_layer
path) for dense, one config for thd. Past the input handling the backend code
is identical to the views mode, so the full config matrix added no coverage.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Replace the .grad-holder objects substituted for q/k/v in declarative packed
mode with q_grad/k_grad/v_grad variables computed right after backward, used
uniformly by all return paths.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL pggPL requested a review from cyanguwa as a code owner July 10, 2026 11:03
pggPL and others added 2 commits July 10, 2026 13:08
…to dpa_packed_qkv_api

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

# Conflicts:
#	tests/pytorch/attention/test_attention.py
@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a declarative packed QKV/KV input API to DotProductAttention, allowing callers to pass a fused projection buffer directly via new qkv_layer/kv_layer/qkv_interleave_dim arguments instead of slicing Q/K/V views that require pointer-based layout detection on every forward pass.

  • _unpack_packed_qkv extracts zero-copy select() views and constructs the layout string (e.g. \"bs3hd\", \"thd_t2hd\") from the declaration, covering dense, thd, and FP8 DPA.
  • MultiheadAttention passes its fused projection output straight to DPA when no per-tensor operation (RoPE, QK norm, KV caching, CPU offload) requires individual slices; GQA and FP8 QKV outputs fall back to the legacy sliced-views path.
  • get_qkv_layout emits a DeprecationWarning (with stacklevel=2) when packed layouts are detected via pointer inspection, suppressed during CPU offload where migration is not possible.

Confidence Score: 5/5

The change is safe to merge: all layout construction paths are verified correct across dense, thd, and cross-attention formats, backward gradient routing through select-view autograd is sound, and existing callers are unaffected by the purely additive API.

The core _unpack_packed_qkv helper correctly handles every format/interleave combination. MHA packed paths gate correctly on QuantizedTensorStorage and num_queries_per_key_value. Gradient flow through select() views is handled properly. The only findings are two defensive assert statements in combine_and_quantize that should be raise ValueError.

No files require special attention; the two assert-vs-raise sites in utils.py are the only items worth a follow-up cleanup.

Important Files Changed

Filename Overview
transformer_engine/pytorch/attention/dot_product_attention/dot_product_attention.py Adds _unpack_packed_qkv helper and three new optional forward args; layout construction logic and rank/stride validations are correct across all format/interleave combinations.
transformer_engine/pytorch/attention/multi_head_attention.py Adds packed_dpa_eligible gate and wires packed buffers to DPA for self/cross-attention; FP8 QKV output and GQA self-attention correctly fall back to sliced-views path via QuantizedTensorStorage check and num_queries_per_key_value guard.
transformer_engine/pytorch/attention/dot_product_attention/utils.py Adds deprecation warning in get_qkv_layout and combined_qkv/combined_kv fast-path in combine_and_quantize; dim-index derivation via string find is consistent with the declared layout construction.
transformer_engine/pytorch/attention/dot_product_attention/backends.py Threads packed_qkv/packed_kv through FusedAttnFunc and FusedAttention; backward correctly returns None for both since gradients flow via the q/k/v select-view autograd path.
tests/pytorch/attention/test_attention.py Adds declarative_packed mode to _run_dot_product_attention and two new test functions covering 8 dense layouts and 4 thd layouts; gradient extraction from packed buffer and backend comparison are correctly implemented.

Reviews (2): Last reviewed commit: "[PyTorch] Address review: warning stackl..." | Re-trigger Greptile

Comment thread transformer_engine/pytorch/attention/dot_product_attention/utils.py Outdated
Comment thread transformer_engine/pytorch/attention/dot_product_attention/utils.py Outdated
Comment thread transformer_engine/pytorch/attention/multi_head_attention.py
…plicit k_norm

- get_qkv_layout deprecation warning: add stacklevel=2 and skip it while CPU
  offloading is enabled (offloading forces MultiheadAttention onto the
  sliced-views fallback, so the caller has no migration option there).
- MultiheadAttention: gate the packed pass-through on k_norm explicitly
  instead of relying on q_norm/k_norm being created together.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL pggPL changed the title [PyTorch] DotProductAttention: declarative packed QKV/KV inputs [PyTorch][torch.compile] DotProductAttention: declarative packed QKV/KV inputs Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant