Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f108426
Refine the support for D=256 on Blackwell server type GPUs
KshitijLakhani May 28, 2026
8ca68e2
Add deterministic tests for D=256 for sm10.x
KshitijLakhani May 28, 2026
a093127
Test clean up
KshitijLakhani May 28, 2026
b7dc18c
Add PyT side tests for D=256 cuDNN fused attn on SM100-110
KshitijLakhani May 28, 2026
28fe7ba
Refine the PyT D=256 tests
KshitijLakhani May 28, 2026
dd6cbf3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 28, 2026
9920cbe
Fix the filtering condition for bias type for D=256 on sm10x for cudn…
KshitijLakhani May 29, 2026
3f06289
Code clean up
KshitijLakhani May 29, 2026
38de70a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 29, 2026
021c450
Switching test check logic around
KshitijLakhani Jun 3, 2026
b597eda
Add THD xfail marked tests for testing
KshitijLakhani Jun 3, 2026
4882cda
Fix the jax test bias condition fo skipping for D=256
KshitijLakhani Jun 5, 2026
4d5ad94
nit: Fix comment
KshitijLakhani Jun 5, 2026
6184e85
Move the D=256 changes so as to follow an order of increasing cuDNN v…
KshitijLakhani Jun 5, 2026
424b2c4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 5, 2026
fe7c6b2
Add support for D=256 THD in TE
KshitijLakhani Jul 1, 2026
cfed53f
Add TE JAX CP test support to verify D=256 for AG and Ring
KshitijLakhani Jul 8, 2026
5767db0
Add PyTorch D256 CP tests
KshitijLakhani Jul 8, 2026
3035301
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 8, 2026
06eee35
nit: clean up comments
KshitijLakhani Jul 8, 2026
ee79662
Correct the atgs passed to test dpa
KshitijLakhani Jul 21, 2026
cbab884
Simplify THD fused attention layout check
KshitijLakhani Jul 22, 2026
00d14cd
Rename PyTorch D256 fused attention tests
KshitijLakhani Jul 22, 2026
ed4b745
Run JAX fused attention before softmax
KshitijLakhani Jul 22, 2026
e3032d3
Relabel THD D256 cuDNN guard to 9.25
KshitijLakhani Jul 22, 2026
6f2b748
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions qa/L1_jax_distributed_unittest/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -v --junitxml=$XML_LOG_DIR/py

python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -v --junitxml=$XML_LOG_DIR/pytest_dist_mlp.xml $TE_PATH/tests/jax/test_distributed_layernorm_mlp.py || test_fail "test_distributed_layernorm_mlp.py"

python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -v --junitxml=$XML_LOG_DIR/pytest_dist_fused_attn.xml $TE_PATH/tests/jax/test_distributed_fused_attn.py || test_fail "test_distributed_fused_attn.py"

# XLA_FLAGS to WAR for test_distributed_softmax issue with NCCL
# TODO(Kshitij): remove when NCCL issue is fixed
# TODO(KshitijLakhani): remove when NCCL issue is fixed
XLA_FLAGS="$XLA_FLAGS --xla_gpu_enable_nccl_comm_splitting=false" python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -v --junitxml=$XML_LOG_DIR/pytest_dist_softmax.xml $TE_PATH/tests/jax/test_distributed_softmax.py || test_fail "test_distributed_softmax.py"

python3 -m pytest -c $TE_PATH/tests/jax/pytest.ini -v --junitxml=$XML_LOG_DIR/pytest_dist_fused_attn.xml $TE_PATH/tests/jax/test_distributed_fused_attn.py || test_fail "test_distributed_fused_attn.py"

# NCCL EP multi-process suite. Self-skips on <4 GPUs.
TE_PATH=$TE_PATH bash $TE_PATH/tests/jax/multi_process_launch_ep.sh || test_fail "test_multi_process_ep.py"

Expand Down
167 changes: 167 additions & 0 deletions tests/jax/test_distributed_fused_attn.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
_has_cudnn_frontend_python,
)
from utils import pytest_parametrize_wrapper
from transformer_engine_jax import get_cudnn_version, get_device_compute_capability
from transformer_engine.jax.attention import (
is_fused_attn_kernel_available,
AttnBiasType,
Expand Down Expand Up @@ -345,6 +346,59 @@ def test_softcap_score_mod_with_aux_params_backward(
pytest.param([4, 256, 16, 64], id="4-256xCPx2-16-64"),
]

DISTRIBUTED_CONTEXT_SELF_ATTN_D256_DATA_SHAPES = {
"L0": [],
"L1": [[2, 128, 16, 256]],
"L2": [],
}

# Keep these as explicit tuples instead of independent layout/mask/window as:
# BSHD CP uses CAUSAL_MASK, THD CP uses PADDING_CAUSAL_MASK, SWA is
# only valid for THD, and stripe_size behavior is different for
# BSHD vs THD in these tests. Splitting the axes would mostly add
# invalid BSHD+SWA and THD+CAUSAL combinations that fail or skip.
DISTRIBUTED_CONTEXT_SELF_ATTN_D256_LAYOUTS_MASKS_WINDOWS = [
# BSHD with different layouts, but same causal mask and no sliding window
pytest.param(
QKVLayout.BSHD_BS2HD,
AttnMaskType.CAUSAL_MASK,
(-1, -1),
id="BSHD_KVPACKED-CAUSAL-NO_SWA",
),
pytest.param(
QKVLayout.BSHD_BSHD_BSHD,
AttnMaskType.CAUSAL_MASK,
(-1, -1),
id="BSHD_SEPARATE-CAUSAL-NO_SWA",
),
# THD with different sliding window sizes, but same packed layout and padding causal mask
pytest.param(
QKVLayout.THD_T2HD,
AttnMaskType.PADDING_CAUSAL_MASK,
(-1, -1),
id="THD_KVPACKED-PADDING_CAUSAL-NO_SWA",
),
pytest.param(
QKVLayout.THD_T2HD,
AttnMaskType.PADDING_CAUSAL_MASK,
(20, 0),
id="THD_KVPACKED-PADDING_CAUSAL-SWA",
),
# THD with different sliding window sizes, but same separate layout and padding causal mask
pytest.param(
QKVLayout.THD_THD_THD,
AttnMaskType.PADDING_CAUSAL_MASK,
(-1, -1),
id="THD_SEPARATE-PADDING_CAUSAL-NO_SWA",
),
pytest.param(
QKVLayout.THD_THD_THD,
AttnMaskType.PADDING_CAUSAL_MASK,
(20, 0),
id="THD_SEPARATE-PADDING_CAUSAL-SWA",
),
]


class TestDistributedContextParallelSelfAttn:
# TODO(KshitijLakhani): parametrize num_segments_per_seq for all CP tests
Expand Down Expand Up @@ -638,6 +692,119 @@ def test_context_parallel_ring_attn(
stripe_size=stripe_size,
)

# CP ring and all-gather tests for D=256
# TODO(KshitijLakhani): Replace this with common-provided fused-attn disable reasons once
# they can be surfaced to framework tests.
@staticmethod
def skip_if_d256_cp_unsupported(qkv_layout):
compute_capability = get_device_compute_capability(0)
if not 100 <= compute_capability < 110:
pytest.skip("D=256 CP fused attention is only enabled on Blackwell server GPUs.")

required_cudnn_version = 92500 if qkv_layout.is_thd() else 92300
required_cudnn_version_label = "9.25" if qkv_layout.is_thd() else "9.23"
if get_cudnn_version() < required_cudnn_version:
pytest.skip(
f"D=256 CP fused attention with {qkv_layout} requires cuDNN"
f" {required_cudnn_version_label} or newer."
)

@pytest_parametrize_wrapper(
"device_count,mesh_shape,mesh_axes,mesh_resource",
generate_context_parallel_configs_for_attn(),
)
@pytest_parametrize_wrapper(
"data_shape",
DISTRIBUTED_CONTEXT_SELF_ATTN_D256_DATA_SHAPES,
)
@pytest.mark.parametrize(
"dtype",
[pytest.param(jnp.float16, id="FP16"), pytest.param(jnp.bfloat16, id="BF16")],
)
@pytest.mark.parametrize(
"qkv_layout, attn_mask_type, window_size",
DISTRIBUTED_CONTEXT_SELF_ATTN_D256_LAYOUTS_MASKS_WINDOWS,
)
def test_context_parallel_ring_attn_d256(
self,
device_count,
mesh_shape,
mesh_axes,
mesh_resource,
data_shape,
dtype,
qkv_layout,
attn_mask_type,
window_size,
):
"""D=256 CP ring coverage."""
self.skip_if_d256_cp_unsupported(qkv_layout)

self.impl_test_context_parallel_attn(
device_count,
mesh_shape,
mesh_axes,
mesh_resource,
data_shape,
1,
attn_mask_type,
dtype,
qkv_layout,
True,
CPStrategy.RING,
use_scan_ring=False,
window_size=window_size,
stripe_size=1 if qkv_layout.is_thd() else None,
)

@pytest_parametrize_wrapper(
"device_count,mesh_shape,mesh_axes,mesh_resource",
generate_context_parallel_configs_for_attn(),
)
@pytest_parametrize_wrapper(
"data_shape",
DISTRIBUTED_CONTEXT_SELF_ATTN_D256_DATA_SHAPES,
)
@pytest.mark.parametrize(
"dtype",
[pytest.param(jnp.float16, id="FP16"), pytest.param(jnp.bfloat16, id="BF16")],
)
@pytest.mark.parametrize(
"qkv_layout, attn_mask_type, window_size",
DISTRIBUTED_CONTEXT_SELF_ATTN_D256_LAYOUTS_MASKS_WINDOWS,
)
def test_context_parallel_allgather_attn_d256(
self,
device_count,
mesh_shape,
mesh_axes,
mesh_resource,
data_shape,
dtype,
qkv_layout,
attn_mask_type,
window_size,
):
"""D=256 CP all-gather coverage."""
self.skip_if_d256_cp_unsupported(qkv_layout)

self.impl_test_context_parallel_attn(
device_count,
mesh_shape,
mesh_axes,
mesh_resource,
data_shape,
1,
attn_mask_type,
dtype,
qkv_layout,
True,
CPStrategy.ALL_GATHER,
window_size=window_size,
stripe_size=128 if qkv_layout.is_thd() else None,
num_segments_per_seq=5 if qkv_layout.is_thd() else None,
)


REORDER_CAUSAL_LOAD_BALANCING_DATA_SHAPES = {
"L0": [[]],
Expand Down
85 changes: 81 additions & 4 deletions tests/jax/test_fused_attn.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ def _get_max_segments_per_sequence(self):
return 1

def _check_configs(self):
# TODO(rewang): probably adds this in is_fused_attn_available
# TODO(KshitijLakhani): probably add/move this to is_fused_attn_available
if self.qkv_layout.is_thd() and not self.attn_mask_type.is_padding():
pytest.skip("THD format requires padding masks.")

Expand All @@ -493,11 +493,62 @@ def _check_configs(self):
pytest.skip(
"seqlen_q > seqlen_kv is not supported with sliding window attention in cuDNN"
)
compute_capability = get_device_compute_capability(0)
cudnn_version = get_cudnn_version()
# D=256 bprop on SM10x uses the deterministic algorithm path only. BSHD support
# starts with cuDNN FE 1.24 / BE 9.23; THD execution-plan support starts with
# cuDNN FE 1.26 / BE 9.25. The kernel rejects dBias, dropout, and ALiBi, supports vanilla
# softmax only, and allows SWA together with a causal mask only.
is_sm10x = 100 <= compute_capability < 110
if self.is_training and is_sm10x and (self.head_dim_qk == 256 or self.head_dim_v == 256):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these checks already be covered on the C side? Are these by any chance duplicate?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, is your concern the same as you had in your earlier review: #3056 (comment). ? I think my comment goes into more details. Happy to answer any follow ups on that.

I will get rid of these extraneous checks in the tests (if #2964 does not already) once #2964 is merged in. For now, I think we should let them be in. The clean up for these test messages can follow up once we've confirmed that a more descriptive string is plumbed through to the tests

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good - are there any CP specific skips/conditions we should add?

#2964 will surface the reject reason to Jax FusedAttnHelper.get_fused_attn_backend() so it'd be nice to use that as a single source of rejection/pass reasons. The d256 feature reject reason does come from cudnn-frontend so we should be able to remove some of the code (and reduce some maintenance cost).

if self.head_dim_qk != 256 or self.head_dim_v != 256:
pytest.skip(
"D=256 BWD on Blackwell only supports d_qk == d_v == 256;"
f" got d_qk={self.head_dim_qk}, d_v={self.head_dim_v}."
)
required_cudnn_version = 92500 if self.qkv_layout.is_thd() else 92300
required_cudnn_version_label = "9.25" if self.qkv_layout.is_thd() else "9.23"
if cudnn_version < required_cudnn_version:
pytest.skip(
f"D=256 BWD on Blackwell with {self.qkv_layout} requires cuDNN"
f" {required_cudnn_version_label} or newer; got cuDNN {cudnn_version}."
)
# TODO(KshitijLakhani): cuDNN FE can model bias input separately from dBias,
# but TE does not yet plumb whether dBias is requested into the common backend selector.
# Until that distinction is available, the D=256 SM10x gate requires no bias.
Comment on lines +516 to +518

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've opened a GH issue for this: #3188

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2964 will plumb bias/dbias shapes/requirements to the C side, so hopefully when that's merged, this wouldn't be an issue.

unsupported = None
if self.attn_bias_type == AttnBiasType.PRE_SCALE_BIAS:
unsupported = "pre-scale bias"
elif self.attn_bias_type != AttnBiasType.NO_BIAS:
unsupported = (
"post-scale bias in TE's D=256 backend gate; bias-input-only"
" support needs TE to distinguish between bias input and dBias"
)
elif self.dropout_prob != 0.0:
unsupported = "dropout"
elif self.softmax_type != AttnSoftmaxType.VANILLA_SOFTMAX:
unsupported = "non-vanilla softmax"
if unsupported is not None:
pytest.skip(
"D=256 BWD on Blackwell uses the deterministic SM100 D=256 SDPA BWD"
f" kernel which does not support {unsupported}."
)
if self.window_size is not None and self.window_size != (-1, -1):
if not self.attn_mask_type.is_causal():
pytest.skip(
"D=256 BWD on Blackwell uses the SM100 D=256 SDPA BWD kernel"
" which requires window_size=(-1, -1) for non-causal masks."
)
if self.window_size[1] not in (-1, 0):
pytest.skip(
"D=256 BWD on Blackwell only supports right window -1 or 0"
" for causal masks."
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't these checks duplicate to the checks we added on the C++ side? Would the call FusedAttnHelper().get_fused_attn_backend() give you the same gating effect?

@KshitijLakhani KshitijLakhani Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if we are just interested in the gating effect, you are right. The get_fused_attn_backend() will return NVTE_No_Backend and then there's a catch-all at the end which basically skip the tests as there is no fused attn backend avalable.

However, the reason for this to be here is to give a meaningful reason as to why a test is being skipped as compared to a generic "Unsupported inputs combination or device compute capability." message which does not qualify the reason for the skip. Unfortunately, on the JAX attn side we do not log the reason for disabling fused attn in the feature code like we have on the Pytorch side in d_p_a/utils.py. So there is no way for the user to know why the test was skipped. Hence, we need to rely on test code to log this on the JAX side.

I'd suggest we leave this in here for now. And when your PR for generating log messages in the C++ level when selecting the attn backend is ready, I can plumb it through onto the JAX side and then as part of that clean up, get rid of all the skip messages in check_configs()

if get_device_compute_capability(0) >= 100 and self.is_training:
if compute_capability >= 100 and self.is_training:
if FusedAttnHelper.is_non_deterministic_allowed() and (
(self.dropout_prob != 0.0 and self.attn_bias_type != AttnBiasType.NO_BIAS)
or get_cudnn_version() < 90700
or cudnn_version < 90700
):
pytest.skip(
"For sm100+, non-deterministic bprop (cuDNN 9.7+) does not support bias with"
Expand All @@ -506,7 +557,7 @@ def _check_configs(self):
if not FusedAttnHelper.is_non_deterministic_allowed() and (
self.dropout_prob != 0.0
or self.attn_bias_type != AttnBiasType.NO_BIAS
or get_cudnn_version() < 91801
or cudnn_version < 91801
):
pytest.skip(
"For sm100+, deterministic bprop (cuDNN 9.18.1+) does not support bias or"
Expand Down Expand Up @@ -1642,6 +1693,32 @@ def test_backward(
QKVLayout.THD_THD_THD,
id="2-1024-2048-12-6-128-64-BF16-CROSS-GQA-RAGGED_SEPARATE",
),
# D=256 deterministic backward on the SM100 dedicated SDPA bprop kernel.
# BSHD requires cuDNN FE 1.24 / BE 9.23+; THD requires cuDNN FE 1.26 / BE 9.25+.
pytest.param(
4,
128,
128,
16,
16,
256,
256,
jnp.float16,
QKVLayout.BSHD_BS2HD,
id="4-128-128-16-16-256-256-FP16-SELF-KV_PACKED",
),
pytest.param(
4,
128,
128,
16,
16,
256,
256,
jnp.float16,
QKVLayout.THD_T2HD,
id="4-128-128-16-16-256-256-FP16-SELF-RAGGED_KV_PACKED",
),
],
)
@pytest.mark.parametrize(
Expand Down
13 changes: 11 additions & 2 deletions tests/pytorch/attention/run_attention_with_cp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
from transformer_engine.pytorch.attention.dot_product_attention.utils import combine_and_quantize
import transformer_engine_torch as tex
from transformer_engine.pytorch import DType
from test_attention_with_cp import model_configs_flash_attn, model_configs_fused_attn
from test_attention_with_cp import (
model_configs_flash_attn,
model_configs_fused_attn,
model_configs_fused_attn_d256,
)
from transformer_engine.pytorch import (
autocast,
DotProductAttention,
Expand Down Expand Up @@ -231,7 +235,12 @@ def run_dpa_with_cp(
config = copy.deepcopy(model_configs_flash_attn[model])
if kernel_backend == "FusedAttention":
os.environ["NVTE_FUSED_ATTN"] = "1"
config = copy.deepcopy(model_configs_fused_attn[model])
if model in model_configs_fused_attn:
config = copy.deepcopy(model_configs_fused_attn[model])
elif model in model_configs_fused_attn_d256:
config = copy.deepcopy(model_configs_fused_attn_d256[model])
else:
assert False, f"{model=} is not a known FusedAttention CP config!"
assert config.attn_mask_type in [
"causal",
"no_mask",
Expand Down
Loading
Loading