Skip to content

Commit 479a660

Browse files
Revert "cherry pick 4203 from main to release 2.12" (#4275)
1 parent e196f98 commit 479a660

2 files changed

Lines changed: 170 additions & 35 deletions

File tree

py/torch_tensorrt/dynamo/conversion/aten_ops_converters.py

Lines changed: 142 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3961,7 +3961,11 @@ def aten_ops_linear(
39613961
)
39623962

39633963

3964-
def _attention_qkv_shapes_supported(node: Node) -> bool:
3964+
def scaled_dot_product_attention_validator(
3965+
node: Node, settings: Optional[CompilationSettings] = None
3966+
) -> bool:
3967+
enable_gqa = node.kwargs.get("enable_gqa", False)
3968+
39653969
query_shape, key_shape, value_shape = None, None, None
39663970
if "val" in node.args[0].meta:
39673971
query_shape = node.args[0].meta["val"].size()
@@ -3980,38 +3984,46 @@ def _attention_qkv_shapes_supported(node: Node) -> bool:
39803984
)
39813985
return False
39823986

3983-
# TensorRT IAttention layer supports different sequence lengths for query and key/value
3984-
# ([B, Nq, Sq, H] vs [B, Nkv, Skv, H]), but K and V must still agree on all dims.
3985-
seq_dim = len(query_shape) - 2
3986-
for dim, (query_dim, key_dim, value_dim) in enumerate(
3987-
zip(query_shape, key_shape, value_shape)
3988-
):
3989-
if dim == seq_dim:
3990-
if key_dim != value_dim:
3991-
_LOGGER.debug(
3992-
"key and value must have the same sequence length. Please try setting decompose_attention=True in the compilation settings."
3993-
)
3994-
return False
3995-
else:
3996-
if query_dim != key_dim or query_dim != value_dim or key_dim != value_dim:
3987+
if key_shape != value_shape:
3988+
_LOGGER.debug(
3989+
"key and value have different shapes, which is not supported. Please try setting decompose_attention=True in the compilation settings."
3990+
)
3991+
return False
3992+
3993+
ndim = len(query_shape)
3994+
num_heads_dim = 1
3995+
seq_len_dim = ndim - 2
3996+
if enable_gqa:
3997+
# IAttentionLayer natively supports GQA: Q and K/V may differ on the
3998+
# head dim (dim 1) as long as Hq is divisible by Hkv.
3999+
# Check batch (dim 0) and head_dim (last dim) match;
4000+
# skip seq_len (dim -2) for decode phase and num_heads (dim 1).
4001+
for i in range(ndim):
4002+
if i in (num_heads_dim, seq_len_dim):
4003+
continue
4004+
if query_shape[i] != key_shape[i]:
39974005
_LOGGER.debug(
3998-
"query, key, and value differ on a non-sequence dimension. Please try setting decompose_attention=True in the compilation settings."
4006+
f"GQA: query and key mismatch on dim {i} when enable_gqa=True."
39994007
)
40004008
return False
4009+
num_q_heads = query_shape[num_heads_dim]
4010+
num_kv_heads = key_shape[num_heads_dim]
4011+
if num_q_heads % num_kv_heads != 0:
4012+
_LOGGER.debug(
4013+
f"GQA: num_q_heads={num_q_heads} is not divisible by num_kv_heads={num_kv_heads} when enable_gqa=True."
4014+
)
4015+
return False
4016+
else:
4017+
# IAttentionLayer supports decode-phase (seq_q != seq_k).
4018+
# Check all dims except the seq_len dim.
4019+
if any(query_shape[i] != key_shape[i] for i in range(ndim) if i != seq_len_dim):
4020+
_LOGGER.debug(
4021+
"query and key have incompatible shapes (batch, num_heads, or head_dim mismatch). Please try setting decompose_attention=True in the compilation settings."
4022+
)
4023+
return False
40014024
return True
40024025

40034026

4004-
def scaled_dot_product_attention_validator(
4005-
node: Node, settings: Optional[CompilationSettings] = None
4006-
) -> bool:
4007-
if node.kwargs.get("enable_gqa", False):
4008-
_LOGGER.debug(
4009-
"enable_gqa is not yet supported by the converter. Please try setting decompose_attention=True in the compilation settings."
4010-
)
4011-
return False
4012-
return _attention_qkv_shapes_supported(node)
4013-
4014-
40154027
@dynamo_tensorrt_converter(
40164028
torch.ops.aten.scaled_dot_product_attention.default,
40174029
supports_dynamic_shapes=True,
@@ -4047,7 +4059,64 @@ def scaled_dot_product_flash_attention_validator(
40474059
if args_bounds_check(node.args, 5, False):
40484060
_LOGGER.debug("return_debug_mask is not yet supported.")
40494061
return False
4050-
return _attention_qkv_shapes_supported(node)
4062+
4063+
query_shape, key_shape, value_shape = None, None, None
4064+
if "val" in node.args[0].meta:
4065+
query_shape = node.args[0].meta["val"].size()
4066+
if "val" in node.args[1].meta:
4067+
key_shape = node.args[1].meta["val"].size()
4068+
if "val" in node.args[2].meta:
4069+
value_shape = node.args[2].meta["val"].size()
4070+
4071+
# If shape metadata is unavailable, defer to runtime/converter checks.
4072+
if query_shape is None or key_shape is None or value_shape is None:
4073+
return True
4074+
4075+
if len(query_shape) != len(key_shape) or len(query_shape) != len(value_shape):
4076+
_LOGGER.debug(
4077+
"query, key, and value must have the same rank. Please try setting decompose_attention=True in the compilation settings."
4078+
)
4079+
return False
4080+
4081+
if key_shape != value_shape:
4082+
_LOGGER.debug(
4083+
"key and value have different shapes, which is not supported. Please try setting decompose_attention=True in the compilation settings."
4084+
)
4085+
return False
4086+
4087+
ndim = len(query_shape)
4088+
num_heads_dim = 1
4089+
seq_len_dim = ndim - 2
4090+
num_q_heads = query_shape[num_heads_dim]
4091+
num_kv_heads = key_shape[num_heads_dim]
4092+
is_gqa = num_q_heads != num_kv_heads
4093+
if is_gqa:
4094+
# IAttentionLayer natively supports GQA: Q and K/V may differ on the
4095+
# head dim (dim 1) as long as Hq is divisible by Hkv.
4096+
# Check batch (dim 0) and head_dim (last dim) match;
4097+
# skip seq_len (dim -2) for decode phase and num_heads (dim 1).
4098+
for i in range(ndim):
4099+
if i in (num_heads_dim, seq_len_dim):
4100+
continue
4101+
if query_shape[i] != key_shape[i]:
4102+
_LOGGER.debug(
4103+
f"GQA: query and key mismatch on dim {i} when enable_gqa=True."
4104+
)
4105+
return False
4106+
if num_q_heads % num_kv_heads != 0:
4107+
_LOGGER.debug(
4108+
f"GQA: num_q_heads={num_q_heads} is not divisible by num_kv_heads={num_kv_heads} when enable_gqa=True."
4109+
)
4110+
return False
4111+
else:
4112+
# IAttentionLayer supports decode-phase (seq_q != seq_k).
4113+
# Check all dims except the seq_len dim.
4114+
if any(query_shape[i] != key_shape[i] for i in range(ndim) if i != seq_len_dim):
4115+
_LOGGER.debug(
4116+
"query and key have incompatible shapes (batch, num_heads, or head_dim mismatch). Please try setting decompose_attention=True in the compilation settings."
4117+
)
4118+
return False
4119+
return True
40514120

40524121

40534122
@dynamo_tensorrt_converter(
@@ -4084,7 +4153,51 @@ def scaled_dot_product_efficient_attention_validator(
40844153
if args_bounds_check(node.args, 4, False):
40854154
_LOGGER.debug("compute_log_sumexp is not yet supported.")
40864155
return False
4087-
return _attention_qkv_shapes_supported(node)
4156+
4157+
query_shape, key_shape, value_shape = None, None, None
4158+
if "val" in node.args[0].meta:
4159+
query_shape = node.args[0].meta["val"].size()
4160+
if "val" in node.args[1].meta:
4161+
key_shape = node.args[1].meta["val"].size()
4162+
if "val" in node.args[2].meta:
4163+
value_shape = node.args[2].meta["val"].size()
4164+
4165+
# If shape metadata is unavailable, defer to runtime/converter checks.
4166+
if query_shape is None or key_shape is None or value_shape is None:
4167+
return True
4168+
4169+
if len(query_shape) != len(key_shape) or len(query_shape) != len(value_shape):
4170+
_LOGGER.debug(
4171+
"query, key, and value must have the same rank. Please try setting decompose_attention=True in the compilation settings."
4172+
)
4173+
return False
4174+
4175+
if key_shape != value_shape:
4176+
_LOGGER.debug(
4177+
"key and value have different shapes, which is not supported. Please try setting decompose_attention=True in the compilation settings."
4178+
)
4179+
return False
4180+
4181+
# Note1: GQA (Hq != Hkv) is intentionally not supported here.
4182+
# PyTorch's eager _scaled_dot_product_efficient_attention kernel rejects
4183+
# non-equal head counts at runtime, so no valid reference output exists for comparison.
4184+
# In practice, GQA models on CUDA dispatch to _scaled_dot_product_flash_attention (FP16/BF16)
4185+
# or decompose into matmul+_safe_softmax (FP32) — this op never appears with GQA shapes in
4186+
# a real FX graph. GQA is handled by the flash attention validator instead.
4187+
#
4188+
# Note2: IAttentionLayer does support decode-phase (seq_q != seq_k), so only the
4189+
# sequence dimension is skipped in the shape check below.
4190+
seq_len_dim = len(query_shape) - 2
4191+
if any(
4192+
query_shape[i] != key_shape[i]
4193+
for i in range(len(query_shape))
4194+
if i != seq_len_dim # skip the seq_len dim
4195+
):
4196+
_LOGGER.debug(
4197+
"query and key have incompatible shapes (batch, num_heads, or head_dim mismatch). Please try setting decompose_attention=True in the compilation settings."
4198+
)
4199+
return False
4200+
return True
40884201

40894202

40904203
@dynamo_tensorrt_converter(

tests/py/dynamo/conversion/test_attention_aten.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,14 +451,25 @@ class TestScaledDotProductEfficientAttention(DispatchTestCase):
451451
),
452452
(
453453
(4, 8, 1, 64),
454-
(4, 8, 4, 64),
455-
(4, 8, 4, 64),
456-
(1, 1, 4),
454+
(4, 8, 8, 64),
455+
(4, 8, 8, 64),
456+
# attn_bias needs to match query @ key.transpose(-2, -1), and seq_k>=8 for kernel layout constraint to the FP16
457+
(4, 8, 1, 8),
457458
False,
458459
None,
459460
torch.float16,
460461
0.0,
461462
), # decoder-style single-token attention
463+
(
464+
(4, 8, 1, 64),
465+
(4, 8, 4, 64),
466+
(4, 8, 4, 64),
467+
(4, 8, 1, 4), # need to match query @ key.transpose(-2, -1)
468+
False,
469+
2.0,
470+
torch.float32,
471+
0.0,
472+
), # decoder-style single-token attention
462473
]
463474
)
464475
def test_efficient_sdpa(
@@ -563,14 +574,25 @@ def forward(self, query, key, value, attn_bias=None):
563574
),
564575
(
565576
(4, 8, 1, 64),
566-
(4, 8, 4, 64),
567-
(4, 8, 4, 64),
568-
(1, 1, 4),
577+
(4, 8, 8, 64),
578+
(4, 8, 8, 64),
579+
# attn_bias needs to match query @ key.transpose(-2, -1), and seq_k>=8 for kernel layout constraint to the FP16
580+
(4, 8, 1, 8),
569581
False,
570582
None,
571583
torch.float16,
572584
0.0,
573585
), # decoder-style single-token attention
586+
(
587+
(4, 8, 1, 64),
588+
(4, 8, 4, 64),
589+
(4, 8, 4, 64),
590+
(4, 8, 1, 4), # need to match query @ key.transpose(-2, -1)
591+
False,
592+
2.0,
593+
torch.float32,
594+
0.0,
595+
), # decoder-style single-token attention
574596
]
575597
)
576598
def test_efficient_sdpa_random_attn_bias(

0 commit comments

Comments
 (0)