Skip to content

Commit 0cc00aa

Browse files
cherry pick 4131 from main to release 2.11 (#4149)
Co-authored-by: Evan Li <zewenl@nvidia.com>
1 parent e424466 commit 0cc00aa

11 files changed

Lines changed: 647 additions & 41 deletions

File tree

py/torch_tensorrt/dynamo/_compiler.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def cross_compile_for_windows(
112112
cpu_memory_budget: Optional[int] = _defaults.CPU_MEMORY_BUDGET,
113113
dynamically_allocate_resources: bool = _defaults.DYNAMICALLY_ALLOCATE_RESOURCES,
114114
decompose_attention: bool = _defaults.DECOMPOSE_ATTENTION,
115+
attn_bias_is_causal: bool = _defaults.ATTN_BIAS_IS_CAUSAL,
115116
**kwargs: Any,
116117
) -> torch.fx.GraphModule:
117118
"""Compile an ExportedProgram module using TensorRT in Linux for Inference in Windows
@@ -190,6 +191,7 @@ def cross_compile_for_windows(
190191
cpu_memory_budget (Optional[int]): The maximum amount of CPU memory to use for the compilation. If the compilation requires more memory than this budget, the compilation will fail.
191192
dynamically_allocate_resources (bool): Dynamically allocate resources during engine execution.
192193
decompose_attention (bool): Whether to decompose attention layers. We have converters for handling attention ops, but if you want to decompose them into smaller ops, you can set this to True.
194+
attn_bias_is_causal (bool): Whether the attn_bias in efficient SDPA is causal. Default is True. This can accelerate models from HF because attn_bias is always a causal mask in HF. If you want to use non-causal attn_bias, you can set this to False.
193195
**kwargs: Any,
194196
Returns:
195197
torch.fx.GraphModule: Compiled FX Module, when run it will execute via TensorRT
@@ -349,6 +351,7 @@ def cross_compile_for_windows(
349351
"cpu_memory_budget": cpu_memory_budget,
350352
"dynamically_allocate_resources": dynamically_allocate_resources,
351353
"decompose_attention": decompose_attention,
354+
"attn_bias_is_causal": attn_bias_is_causal,
352355
}
353356

354357
# disable the following settings is not supported for cross compilation for windows feature
@@ -471,6 +474,7 @@ def compile(
471474
enable_resource_partitioning: bool = _defaults.ENABLE_RESOURCE_PARTITIONING,
472475
dynamically_allocate_resources: bool = _defaults.DYNAMICALLY_ALLOCATE_RESOURCES,
473476
decompose_attention: bool = _defaults.DECOMPOSE_ATTENTION,
477+
attn_bias_is_causal: bool = _defaults.ATTN_BIAS_IS_CAUSAL,
474478
**kwargs: Any,
475479
) -> torch.fx.GraphModule:
476480
"""Compile an ExportedProgram module for NVIDIA GPUs using TensorRT
@@ -559,6 +563,7 @@ def compile(
559563
cpu_memory_budget (Optional[int]): The maximum amount of CPU memory to use for the compilation. If the compilation requires more memory than this budget, the compilation will fail.
560564
dynamically_allocate_resources (bool): Dynamically allocate resources during engine execution.
561565
decompose_attention (bool): Whether to decompose attention layers. We have converters for handling attention ops, but if you want to decompose them into smaller ops, you can set this to True.
566+
attn_bias_is_causal (bool): Whether the attn_bias in efficient SDPA is causal. Default is True. This can accelerate models from HF because attn_bias is always a causal mask in HF. If you want to use non-causal attn_bias, you can set this to False.
562567
**kwargs: Any,
563568
Returns:
564569
torch.fx.GraphModule: Compiled FX Module, when run it will execute via TensorRT
@@ -763,6 +768,7 @@ def compile(
763768
"cpu_memory_budget": cpu_memory_budget,
764769
"dynamically_allocate_resources": dynamically_allocate_resources,
765770
"decompose_attention": decompose_attention,
771+
"attn_bias_is_causal": attn_bias_is_causal,
766772
}
767773
logger.debug(f"CPU memory usage before lowering: {get_cpu_memory_usage()} MB")
768774
settings = CompilationSettings(**compilation_options)
@@ -1167,6 +1173,7 @@ def convert_exported_program_to_serialized_trt_engine(
11671173
offload_module_to_cpu: bool = _defaults.OFFLOAD_MODULE_TO_CPU,
11681174
use_distributed_mode_trace: bool = _defaults.USE_DISTRIBUTED_MODE_TRACE,
11691175
decompose_attention: bool = _defaults.DECOMPOSE_ATTENTION,
1176+
attn_bias_is_causal: bool = _defaults.ATTN_BIAS_IS_CAUSAL,
11701177
**kwargs: Any,
11711178
) -> bytes:
11721179
"""Convert an ExportedProgram to a serialized TensorRT engine
@@ -1242,6 +1249,7 @@ def convert_exported_program_to_serialized_trt_engine(
12421249
offload_module_to_cpu (bool): Offload the module to CPU. This is useful when we need to minimize GPU memory usage.
12431250
use_distributed_mode_trace (bool): Using aot_autograd to trace the graph. This is enabled when DTensors or distributed tensors are present in distributed model.
12441251
decompose_attention (bool): Whether to decompose attention layers. We have converters for handling attention ops, but if you want to decompose them into smaller ops, you can set this to True.
1252+
attn_bias_is_causal (bool): Whether the attn_bias in efficient SDPA is causal. Default is True. This can accelerate models from HF because attn_bias is always a causal mask in HF. If you want to use non-causal attn_bias, you can set this to False.
12451253
**kwargs: Any,
12461254
Returns:
12471255
bytes: Serialized TensorRT engine, can either be saved to a file or deserialized via TensorRT APIs
@@ -1412,6 +1420,7 @@ def convert_exported_program_to_serialized_trt_engine(
14121420
"offload_module_to_cpu": offload_module_to_cpu,
14131421
"use_distributed_mode_trace": use_distributed_mode_trace,
14141422
"decompose_attention": decompose_attention,
1423+
"attn_bias_is_causal": attn_bias_is_causal,
14151424
}
14161425

14171426
settings = CompilationSettings(**compilation_options)

py/torch_tensorrt/dynamo/_defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
CPU_MEMORY_BUDGET = None
6969
DYNAMICALLY_ALLOCATE_RESOURCES = False
7070
DECOMPOSE_ATTENTION = False
71+
ATTN_BIAS_IS_CAUSAL = True
7172

7273
if platform.system() == "Linux":
7374
import pwd

py/torch_tensorrt/dynamo/_settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from torch_tensorrt._enums import EngineCapability, dtype
99
from torch_tensorrt.dynamo._defaults import (
1010
ASSUME_DYNAMIC_SHAPE_SUPPORT,
11+
ATTN_BIAS_IS_CAUSAL,
1112
AUTOCAST_CALIBRATION_DATALOADER,
1213
AUTOCAST_EXCLUDED_NODES,
1314
AUTOCAST_EXCLUDED_OPS,
@@ -120,6 +121,7 @@ class CompilationSettings:
120121
offload_module_to_cpu (bool): Offload the model to CPU to reduce memory footprint during compilation
121122
dynamically_allocate_resources (bool): Dynamically allocate resources for TensorRT engines
122123
decompose_attention (bool): Whether to decompose attention layers. We have converters for handling attention ops, but if you want to decompose them into smaller ops, you can set this to True.
124+
attn_bias_is_causal (bool): Whether the attn_bias in efficient SDPA is causal. Default is True. This can accelerate models from HF because attn_bias is always a causal mask in HF. If you want to use non-causal attn_bias, you can set this to False.
123125
"""
124126

125127
enabled_precisions: Set[dtype] = field(default_factory=lambda: ENABLED_PRECISIONS)
@@ -180,6 +182,7 @@ class CompilationSettings:
180182
cpu_memory_budget: Optional[int] = CPU_MEMORY_BUDGET
181183
dynamically_allocate_resources: bool = DYNAMICALLY_ALLOCATE_RESOURCES
182184
decompose_attention: bool = DECOMPOSE_ATTENTION
185+
attn_bias_is_causal: bool = ATTN_BIAS_IS_CAUSAL
183186

184187
def __getstate__(self) -> dict[str, Any]:
185188
from torch_tensorrt.dynamo.conversion._ConverterRegistry import (
@@ -220,6 +223,7 @@ def __setstate__(self, state: dict[str, Any]) -> None:
220223
"autocast_max_depth_of_reduction",
221224
"autocast_calibration_dataloader",
222225
"decompose_attention",
226+
"attn_bias_is_causal",
223227
}
224228

225229

py/torch_tensorrt/dynamo/conversion/impl/attention.py

Lines changed: 99 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from torch_tensorrt.dynamo.conversion.converter_utils import (
1111
cast_trt_tensor,
1212
get_trt_tensor,
13+
prepend_ones,
1314
)
1415

1516
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -193,18 +194,17 @@ def scaled_dot_product_attention(
193194
if attn_mask is not None:
194195
if attn_mask.dtype == trt.DataType.BOOL:
195196
mask_tensor = attn_mask
197+
elif attn_mask.dtype != query.dtype:
198+
mask_tensor = cast_trt_tensor(
199+
ctx,
200+
attn_mask,
201+
query.dtype,
202+
name + "_cast_attn_mask",
203+
target,
204+
source_ir,
205+
)
196206
else:
197-
if attn_mask.dtype != query.dtype:
198-
mask_tensor = cast_trt_tensor(
199-
ctx,
200-
attn_mask,
201-
query.dtype,
202-
name + "_cast_attn_mask",
203-
target,
204-
source_ir,
205-
)
206-
else:
207-
mask_tensor = attn_mask
207+
mask_tensor = attn_mask
208208

209209
scaled_query = impl.elementwise.mul(
210210
ctx, target, source_ir, f"{name}_scaled_query", query, scale_factor
@@ -329,41 +329,101 @@ def scaled_dot_product_efficient_attention(
329329
source_ir,
330330
)
331331

332+
if (
333+
isinstance(scaled_query.shape[1], int)
334+
and scaled_query.shape[1] < 0
335+
and isinstance(key.shape[1], int)
336+
and key.shape[1] > 0
337+
):
338+
shape_layer = ctx.net.add_shape(key)
339+
shape_layer.name = name + "_key_shape"
340+
shuffle = ctx.net.add_shuffle(scaled_query)
341+
shuffle.set_input(1, shape_layer.get_output(0))
342+
shuffle.name = name + "_fix_head_dim"
343+
scaled_query = shuffle.get_output(0)
344+
345+
mask_tensor = None
332346
if attn_bias is not None:
333-
attn_weight = impl.matmul.matrix_multiply(
334-
ctx,
335-
target,
336-
source_ir,
337-
name + "_mm",
338-
scaled_query,
339-
key,
340-
other_matrix_op=trt.MatrixOperation.TRANSPOSE,
341-
)
342-
attn_weight = impl.elementwise.add(
343-
ctx, target, source_ir, name + "_attn_bias_add", attn_weight, attn_bias
344-
)
345-
attn_weight = impl.normalization.softmax(
346-
ctx, target, source_ir, name + "_softmax", attn_weight, -1, False
347-
)
348-
out = impl.matmul.matrix_multiply(
347+
if attn_bias.dtype == trt.DataType.BOOL:
348+
mask_tensor = attn_bias
349+
elif attn_bias.dtype != query.dtype:
350+
mask_tensor = cast_trt_tensor(
351+
ctx,
352+
attn_bias,
353+
query.dtype,
354+
name + "_cast_attn_bias",
355+
target,
356+
source_ir,
357+
)
358+
else:
359+
mask_tensor = attn_bias
360+
361+
# TensorRT IAttention does not allow setting both causal=True and mask.
362+
# If both are requested, fold causal into mask and disable causal flag.
363+
use_causal = is_causal
364+
if mask_tensor is not None and is_causal:
365+
L = impl.shape.shape(ctx, target, source_ir, name + "_L", query, -2)
366+
S = impl.shape.shape(ctx, target, source_ir, name + "_S", key, -2)
367+
causal_mask = tril(
349368
ctx,
350369
target,
351370
source_ir,
352-
name + "_out",
353-
attn_weight,
354-
value,
371+
name + "_tril",
372+
L,
373+
S,
355374
)
356-
return out, None, None, None
357-
else:
358-
attention_layer = ctx.net.add_attention(
359-
scaled_query, key, value, trt.AttentionNormalizationOp.SOFTMAX, is_causal
360-
)
361-
assert attention_layer is not None, "attention layer is None"
375+
diff = len(query.shape) - len(causal_mask.shape)
376+
causal_mask = prepend_ones(ctx, causal_mask, name + "_prepend_ones", diff)
377+
378+
if mask_tensor.dtype == trt.DataType.BOOL:
379+
mask_tensor = impl.elementwise.logical_and(
380+
ctx,
381+
target,
382+
source_ir,
383+
name + "_causal_attn_bias_and",
384+
causal_mask,
385+
mask_tensor,
386+
)
387+
else:
388+
# Convert causal bool mask to additive bias mask:
389+
# True -> 0.0 (keep), False -> -inf (block)
390+
zero_bias = get_trt_tensor(
391+
ctx, 0.0, name + "_causal_additive_bias_zero", query.dtype
392+
)
393+
neg_inf_bias = get_trt_tensor(
394+
ctx, float("-inf"), name + "_causal_additive_bias_neg_inf", query.dtype
395+
)
396+
causal_additive_bias = impl.condition.where(
397+
ctx,
398+
target,
399+
source_ir,
400+
name + "_causal_additive_bias",
401+
zero_bias,
402+
neg_inf_bias,
403+
causal_mask,
404+
)
405+
mask_tensor = impl.elementwise.add(
406+
ctx,
407+
target,
408+
source_ir,
409+
name + "_attn_bias_add_causal",
410+
mask_tensor,
411+
causal_additive_bias,
412+
)
413+
use_causal = False
362414

363-
attention_layer.decomposable = True
415+
attention_layer = ctx.net.add_attention(
416+
scaled_query, key, value, trt.AttentionNormalizationOp.SOFTMAX, use_causal
417+
)
418+
assert attention_layer is not None, "attention layer is None"
364419

365-
attention_output = attention_layer.get_output(0)
366-
return attention_output, None, None, None
420+
if mask_tensor is not None:
421+
attention_layer.mask = mask_tensor
422+
423+
attention_layer.decomposable = True
424+
425+
attention_output = attention_layer.get_output(0)
426+
return attention_output, None, None, None
367427

368428

369429
def scaled_dot_product_cudnn_attention(

py/torch_tensorrt/dynamo/lowering/passes/_aten_lowering_pass.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from .complex_graph_rewrite import complex_graph_detection
1414
from .constant_folding import constant_fold
15+
from .force_causal_efficient_attention import force_causal_efficient_attention
1516
from .fuse_prims_broadcast import fuse_prims_broadcast
1617
from .pass_manager import DynamoPassManager
1718
from .remove_assert_nodes import remove_assert_nodes
@@ -39,6 +40,7 @@
3940
remove_assert_nodes,
4041
remove_num_users_is_0_nodes,
4142
complex_graph_detection,
43+
force_causal_efficient_attention,
4244
]
4345

4446
if not is_tegra_platform():
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import logging
2+
3+
import torch
4+
from torch_tensorrt.dynamo._settings import CompilationSettings
5+
from torch_tensorrt.dynamo.lowering.passes.pass_utils import (
6+
clean_up_graph_after_modifications,
7+
)
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def force_causal_efficient_attention(
13+
gm: torch.fx.GraphModule, settings: CompilationSettings
14+
) -> torch.fx.GraphModule:
15+
"""Force efficient-attention calls to causal mode when enabled in settings."""
16+
if not settings.attn_bias_is_causal:
17+
return gm
18+
19+
changed = False
20+
for node in gm.graph.nodes:
21+
if (
22+
node.target
23+
== torch.ops.aten._scaled_dot_product_efficient_attention.default
24+
):
25+
attn_bias = node.args[3] if len(node.args) > 3 else None
26+
if attn_bias is None:
27+
continue
28+
node.args = (
29+
node.args[0],
30+
node.args[1],
31+
node.args[2],
32+
None,
33+
False,
34+
0.0,
35+
True,
36+
)
37+
changed = True
38+
logger.debug(
39+
f"The args of node {node} was changed to causal mode. Now the node's arguments are: {node.args}"
40+
)
41+
42+
if changed:
43+
gm = clean_up_graph_after_modifications(gm)
44+
45+
logger.debug(f"After forcing causal efficient attention pass:\n{gm.graph}")
46+
return gm

0 commit comments

Comments
 (0)