Skip to content

Commit f907b64

Browse files
committed
adding examples for shared dims in both index.rst and a short section in dynamic_shapes.rst
1 parent 6b3801b commit f907b64

3 files changed

Lines changed: 225 additions & 0 deletions

File tree

docsrc/user_guide/compilation/dynamic_shapes.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,49 @@ Here's a simple example that exports a matmul layer with some restrictions on dy
7878
# Run inference
7979
trt_gm(*inputs)
8080
81+
Sharing a Dynamic Dimension Across Multiple Inputs
82+
---------------------------------------------------
83+
84+
HuggingFace-style encoders and similar models take multiple inputs (e.g.
85+
``input_ids`` and ``attention_mask``) whose dynamic axes **must be equal at
86+
runtime**. If you assign an independent dynamic dimension to each input,
87+
``torch.export`` detects that the two independent symbols are forced equal by
88+
the model's forward pass (e.g. a broadcast) and raises a
89+
``ConstraintViolationError``.
90+
91+
``torch_tensorrt.Input(shared_dims={axis: name})`` solves this without any
92+
manual ``torch.export`` work. Axes that share the same name across inputs are
93+
exported as a single ``torch.export.Dim``, so the equality constraint is
94+
satisfied automatically.
95+
96+
.. code-block:: python
97+
98+
import torch
99+
import torch_tensorrt
100+
101+
model = MyHFEncoder().eval().cuda()
102+
103+
inputs = [
104+
torch_tensorrt.Input(
105+
min_shape=(1, 16), opt_shape=(4, 16), max_shape=(8, 16),
106+
dtype=torch.int64, name="input_ids",
107+
shared_dims={0: "B"}, # axis 0 named "B"
108+
),
109+
torch_tensorrt.Input(
110+
min_shape=(1, 16), opt_shape=(4, 16), max_shape=(8, 16),
111+
dtype=torch.int64, name="attention_mask",
112+
shared_dims={0: "B"}, # same name → same Dim
113+
),
114+
]
115+
116+
trt_model = torch_tensorrt.compile(model, ir="dynamo", inputs=inputs)
117+
118+
The same name on the same axis index across different inputs produces one
119+
shared ``Dim``; different names produce independent ``Dim``\s. Multiple axes
120+
can be shared simultaneously with ``shared_dims={0: "B", 1: "S"}``.
121+
122+
See the full runnable example: :ref:`shared_dynamic_dims`.
123+
81124
Dynamic shapes using torch.compile (JIT)
82125
------------------------------------
83126

docsrc/user_guide/compilation/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ How Torch-TensorRT compiles models: the JIT ``torch.compile`` path, the AOT
1313
compilation_settings
1414
dynamic_shapes
1515
Example: Compiling Models with Dynamic Input Shapes <../../tutorials/_rendered_examples/dynamo/compile_with_dynamic_inputs>
16+
Example: Sharing Dynamic Dimensions Across Inputs <../../tutorials/_rendered_examples/dynamo/shared_dynamic_dims_example>
1617
unsupported_ops
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
.. _shared_dynamic_dims:
3+
4+
Sharing Dynamic Dimensions Across Inputs
5+
==========================================================
6+
7+
When a model takes multiple inputs whose dynamic axes must be **equal at
8+
runtime** — for example, HuggingFace-style encoders where ``input_ids`` and
9+
``attention_mask`` are both shaped ``[batch, seq_len]`` — naively assigning an
10+
independent dynamic dimension to each input causes ``torch.export`` to raise a
11+
``ConstraintViolationError``. The exporter detects that the two independent
12+
symbols are forced equal by the model's forward pass (e.g. a broadcast) and
13+
rejects the export.
14+
15+
``torch_tensorrt.Input(shared_dims={axis: name})`` solves this: axes that share
16+
the same name across inputs are exported as a single ``torch.export.Dim``, so
17+
the equality constraint is satisfied automatically. All dynamic-shape intent
18+
lives on the ``Input`` objects — no separate ``dynamic_shapes`` argument or
19+
``torch.export`` knowledge is required at the call site.
20+
"""
21+
22+
# %%
23+
# Imports and Model Definition
24+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
26+
import torch
27+
import torch.nn as nn
28+
import torch_tensorrt
29+
from torch_tensorrt.dynamo.utils import COSINE_THRESHOLD, cosine_similarity
30+
31+
# %%
32+
# Define a HuggingFace-style encoder whose two inputs share the batch axis.
33+
# The ``embed * mask`` broadcast forces ``input_ids.shape[0] ==
34+
# attention_mask.shape[0]`` at every forward call — exactly the pattern that
35+
# triggers ``ConstraintViolationError`` when the batch axis is exported as two
36+
# independent ``Dim`` objects.
37+
38+
39+
class SharedDimEncoder(nn.Module):
40+
def __init__(self, vocab: int = 1024, hidden: int = 64):
41+
super().__init__()
42+
self.embed = nn.Embedding(vocab, hidden)
43+
self.proj = nn.Linear(hidden, hidden)
44+
45+
def forward(self, input_ids: torch.Tensor, attention_mask: torch.Tensor):
46+
x = self.embed(input_ids) # [B, S, hidden]
47+
mask = attention_mask.unsqueeze(-1).to(x.dtype) # [B, S, 1]
48+
return self.proj(x * mask) # [B, S, hidden]
49+
50+
51+
model = SharedDimEncoder().cuda().eval()
52+
53+
# %%
54+
# Without ``shared_dims`` — raises ``ConstraintViolationError``
55+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
#
57+
# Using independent ``Input`` objects like this would fail at export time:
58+
#
59+
# .. code-block:: python
60+
#
61+
# inputs = [
62+
# torch_tensorrt.Input(min_shape=(1,16), opt_shape=(4,16), max_shape=(8,16), dtype=torch.int64),
63+
# torch_tensorrt.Input(min_shape=(1,16), opt_shape=(4,16), max_shape=(8,16), dtype=torch.int64),
64+
# ]
65+
# # torch.export mints independent symbols s0, s1 for the batch axis of
66+
# # each input. The broadcast forces Eq(s0, s1), which the exporter rejects.
67+
#
68+
# %%
69+
# With ``shared_dims`` — correct approach (positional inputs)
70+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
71+
#
72+
# Annotate the batch axis (axis 0) with the same name ``"B"`` on both inputs.
73+
# Torch-TensorRT creates a single shared ``torch.export.Dim("B")`` for that
74+
# axis so the equality constraint is satisfied up front.
75+
76+
inputs = [
77+
torch_tensorrt.Input(
78+
min_shape=(1, 16),
79+
opt_shape=(4, 16),
80+
max_shape=(8, 16),
81+
dtype=torch.int64,
82+
name="input_ids",
83+
shared_dims={0: "B"},
84+
),
85+
torch_tensorrt.Input(
86+
min_shape=(1, 16),
87+
opt_shape=(4, 16),
88+
max_shape=(8, 16),
89+
dtype=torch.int64,
90+
name="attention_mask",
91+
shared_dims={0: "B"},
92+
),
93+
]
94+
95+
trt_model = torch_tensorrt.compile(
96+
model,
97+
ir="dynamo",
98+
inputs=inputs,
99+
min_block_size=1,
100+
cache_built_engines=False,
101+
reuse_cached_engines=False,
102+
)
103+
104+
# %%
105+
# Verify correctness at multiple batch sizes within the declared range.
106+
107+
for batch_size in (4, 2, 1):
108+
ids = torch.randint(0, 1024, (batch_size, 16), dtype=torch.int64, device="cuda")
109+
mask = torch.ones((batch_size, 16), dtype=torch.int64, device="cuda")
110+
111+
with torch.no_grad():
112+
ref = model(ids, mask)
113+
out = trt_model(ids, mask)
114+
115+
cos_sim = cosine_similarity(ref, out)
116+
assert (
117+
cos_sim > COSINE_THRESHOLD
118+
), f"Numerical mismatch at batch_size={batch_size}: cos_sim={cos_sim:.4f}"
119+
print(f"batch_size={batch_size} cos_sim={cos_sim:.6f} ✓")
120+
121+
# %%
122+
# With ``shared_dims`` — kwarg inputs
123+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124+
#
125+
# The same feature works with ``kwarg_inputs``, which is the natural form for
126+
# HuggingFace models whose ``forward`` signature uses keyword arguments.
127+
128+
kwarg_inputs = {
129+
"input_ids": torch_tensorrt.Input(
130+
min_shape=(1, 16),
131+
opt_shape=(4, 16),
132+
max_shape=(8, 16),
133+
dtype=torch.int64,
134+
name="input_ids",
135+
shared_dims={0: "B"},
136+
),
137+
"attention_mask": torch_tensorrt.Input(
138+
min_shape=(1, 16),
139+
opt_shape=(4, 16),
140+
max_shape=(8, 16),
141+
dtype=torch.int64,
142+
name="attention_mask",
143+
shared_dims={0: "B"},
144+
),
145+
}
146+
147+
trt_model_kwargs = torch_tensorrt.compile(
148+
model,
149+
ir="dynamo",
150+
kwarg_inputs=kwarg_inputs,
151+
min_block_size=1,
152+
cache_built_engines=False,
153+
reuse_cached_engines=False,
154+
)
155+
156+
ids = torch.randint(0, 1024, (4, 16), dtype=torch.int64, device="cuda")
157+
mask = torch.ones((4, 16), dtype=torch.int64, device="cuda")
158+
159+
with torch.no_grad():
160+
ref = model(input_ids=ids, attention_mask=mask)
161+
out = trt_model_kwargs(input_ids=ids, attention_mask=mask)
162+
163+
cos_sim = cosine_similarity(ref, out)
164+
assert cos_sim > COSINE_THRESHOLD, f"kwarg path mismatch: cos_sim={cos_sim:.4f}"
165+
print(f"kwarg_inputs path cos_sim={cos_sim:.6f} ✓")
166+
167+
# %%
168+
# Sharing multiple axes
169+
# ^^^^^^^^^^^^^^^^^^^^^^
170+
#
171+
# If both batch and sequence length are dynamic and must be shared, annotate
172+
# both axes on each input:
173+
#
174+
# .. code-block:: python
175+
#
176+
# shared_dims={0: "B", 1: "S"}
177+
#
178+
# The same name on the same axis across different inputs produces one shared
179+
# ``Dim``; different names on different axes produce independent ``Dim``\s.
180+
181+
print("\nAll checks passed.")

0 commit comments

Comments
 (0)