Skip to content

Commit 0b73b90

Browse files
committed
uneven tp initial impl
Signed-off-by: Brenden Elgarten <belgarten@nvidia.com>
1 parent f7dd7ec commit 0b73b90

13 files changed

Lines changed: 2563 additions & 591 deletions

File tree

tensorrt_llm/_torch/modules/gated_mlp.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,25 @@ def __init__(
6262

6363
# Calculate local intermediate size after tensor parallel sharding
6464
tp_size = mapping.tp_size
65-
local_intermediate_size = self.intermediate_size // tp_size
65+
66+
local_intermediate_start = Linear._calc_shard(self.intermediate_size,
67+
mapping.tp_size,
68+
mapping.tp_rank)
69+
local_intermediate_end = Linear._calc_shard(self.intermediate_size,
70+
mapping.tp_size,
71+
mapping.tp_rank + 1)
72+
local_intermediate_size = local_intermediate_end - local_intermediate_start
6673

6774
gateup_shard_indices_mapping = {
6875
'gate': (0, local_intermediate_size),
6976
'up': (local_intermediate_size, local_intermediate_size),
7077
}
7178

79+
override_tp_sharding = {
80+
'gate': (local_intermediate_start, local_intermediate_end),
81+
'up': (local_intermediate_start, local_intermediate_end),
82+
}
83+
7284
self.gate_up_proj = Linear(
7385
self.hidden_size,
7486
self.intermediate_size * 2,
@@ -87,6 +99,7 @@ def __init__(
8799
disable_deep_gemm=disable_deep_gemm,
88100
fused_weight_shard_indices_mapping=gateup_shard_indices_mapping,
89101
use_custom_cublas_mm=use_custom_cublas_mm,
102+
override_tp_sharding=override_tp_sharding,
90103
)
91104

92105
if is_shared_expert:

tensorrt_llm/_torch/modules/linear.py

Lines changed: 492 additions & 299 deletions
Large diffs are not rendered by default.

tensorrt_llm/_torch/visual_gen/models/flux/attention.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ def __init__(
101101
mapping=config.mapping,
102102
tensor_parallel_mode=TensorParallelMode.COLUMN,
103103
reduce_output=False,
104+
override_tp_sharding={
105+
"q": (self.local_q_dim_start, self.local_q_dim_end),
106+
"k": (self.local_kv_dim_start, self.local_kv_dim_end),
107+
"v": (self.local_kv_dim_start, self.local_kv_dim_end),
108+
},
104109
)
105110

106111
# Need not pass any mapping info since this is intra-head normalization
@@ -130,6 +135,7 @@ def __init__(
130135
allreduce_strategy=config.allreduce_strategy,
131136
tensor_parallel_mode=TensorParallelMode.ROW,
132137
reduce_output=True,
138+
override_tp_sharding=(self.local_kv_dim_start, self.local_kv_dim_end),
133139
)
134140

135141
def apply_qk_norm(self, q: torch.Tensor, k: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
@@ -350,6 +356,7 @@ def __init__(
350356
skip_create_weights_in_init=self.skip_create_weights_in_init,
351357
force_dynamic_quantization=self.force_dynamic_quantization,
352358
config=config,
359+
attn_shard=(self.local_q_dim_start, self.local_q_dim_end),
353360
)
354361

355362
def _init_qkv_proj(self):
@@ -366,6 +373,11 @@ def _init_qkv_proj(self):
366373
skip_create_weights_in_init=self.skip_create_weights_in_init,
367374
force_dynamic_quantization=self.force_dynamic_quantization,
368375
mapping=self.mapping,
376+
override_qkv_sharding={
377+
"q": (self.local_q_dim_start, self.local_q_dim_end),
378+
"k": (self.local_kv_dim_start, self.local_kv_dim_end),
379+
"v": (self.local_kv_dim_start, self.local_kv_dim_end),
380+
},
369381
)
370382

371383
def _apply_norm_rope_unfused(

tensorrt_llm/_torch/visual_gen/models/flux/joint_proj.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,19 @@ def __init__(
5353
skip_create_weights_in_init: bool = False,
5454
force_dynamic_quantization: bool = False,
5555
config: Optional[DiffusionModelConfig] = None,
56+
attn_shard: Optional[tuple[int, int]] = None,
5657
):
5758
super().__init__()
5859
mapping = config.mapping if config else None
5960
self.tp_size = getattr(mapping, "tp_size", 1)
6061
self.tp_rank = getattr(mapping, "tp_rank", 0)
6162
self.attn_dim = attn_dim
6263
self.has_bias = bias
64+
self.attn_shard = attn_shard
65+
66+
assert attn_dim % self.tp_size == 0 or self.attn_shard, (
67+
"Explicit attention sharding required for uneven TP"
68+
)
6369

6470
if self.tp_size == 1:
6571
self.proj = Linear(
@@ -84,6 +90,7 @@ def __init__(
8490
mapping=config.mapping,
8591
tensor_parallel_mode=TensorParallelMode.ROW,
8692
reduce_output=False,
93+
override_tp_sharding=self.attn_shard,
8794
)
8895
self.mlp_proj = Linear(
8996
mlp_dim,
@@ -162,10 +169,12 @@ def __init__(
162169
skip_create_weights_in_init: bool = False,
163170
force_dynamic_quantization: bool = False,
164171
mapping: Optional[Mapping] = None,
172+
override_qkv_sharding=None,
165173
):
166174
super().__init__()
167175

168176
self.tp_size = mapping.tp_size if mapping else 1
177+
self.tp_rank = mapping.tp_rank if mapping else 0
169178

170179
# Store full (pre-TP) dims for weight loading (splitting checkpoint weight)
171180
self.full_q_dim = q_dim
@@ -188,9 +197,12 @@ def __init__(
188197
self.local_qkv_dim = q_dim + 2 * kv_dim
189198
self.local_mlp_dim = mlp_dim
190199
else:
191-
local_q_dim = q_dim // self.tp_size
192-
local_kv_dim = kv_dim // self.tp_size
193-
shard_mlp_hidden_dim = self.mlp_hidden_dim // self.tp_size
200+
201+
def range_size(r):
202+
return r[1] - r[0]
203+
204+
local_q_dim = range_size(override_qkv_sharding["q"])
205+
local_kv_dim = range_size(override_qkv_sharding["k"])
194206
# QKV: column-parallel with fused Q/K/V sharding
195207
self.qkv_proj = Linear(
196208
in_dim,
@@ -211,8 +223,17 @@ def __init__(
211223
mapping=mapping,
212224
tensor_parallel_mode=TensorParallelMode.COLUMN,
213225
reduce_output=False,
226+
override_tp_sharding=override_qkv_sharding,
227+
)
228+
229+
local_mlp_hidden_start = Linear._calc_shard(
230+
self.mlp_hidden_dim, self.tp_size, self.tp_rank
231+
)
232+
local_mlp_hidden_end = Linear._calc_shard(
233+
self.mlp_hidden_dim, self.tp_size, self.tp_rank + 1
214234
)
215-
# MLP gate+up: column-parallel with fused gate/up sharding
235+
local_mlp_hidden_size = local_mlp_hidden_end - local_mlp_hidden_start
236+
216237
self.mlp_proj = Linear(
217238
in_dim,
218239
mlp_dim,
@@ -225,15 +246,19 @@ def __init__(
225246
weight_mode=WeightMode.FUSED_GATE_UP_LINEAR,
226247
),
227248
fused_weight_shard_indices_mapping={
228-
"gate": (0, shard_mlp_hidden_dim),
229-
"up": (shard_mlp_hidden_dim, shard_mlp_hidden_dim),
249+
"gate": (0, local_mlp_hidden_size),
250+
"up": (local_mlp_hidden_size, local_mlp_hidden_size),
230251
},
231252
mapping=mapping,
232253
tensor_parallel_mode=TensorParallelMode.COLUMN,
233254
reduce_output=False,
255+
override_tp_sharding={
256+
"gate": (local_mlp_hidden_start, local_mlp_hidden_end),
257+
"up": (local_mlp_hidden_start, local_mlp_hidden_end),
258+
},
234259
)
235260
self.local_qkv_dim = (q_dim + 2 * kv_dim) // self.tp_size
236-
self.local_mlp_dim = mlp_dim // self.tp_size
261+
self.local_mlp_dim = local_mlp_hidden_size
237262

238263
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
239264
"""Returns (qkv, mlp_gate_up) with local (post-TP) sizes."""

tensorrt_llm/_torch/visual_gen/models/flux/transformer_flux.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -467,21 +467,6 @@ def __init__(
467467
)
468468
self.act_mlp = _gelu_tanh_eager
469469

470-
kv_dim = num_attention_heads * attention_head_dim
471-
472-
# MLP + Attn Output projection, requires special handling for TP
473-
self.proj_out = FluxJointAttnMLPProj(
474-
attn_dim=kv_dim,
475-
mlp_dim=self.mlp_hidden_dim,
476-
out_dim=dim,
477-
bias=True,
478-
dtype=dtype,
479-
quant_config=quant_config,
480-
skip_create_weights_in_init=skip_create_weights,
481-
force_dynamic_quantization=force_dynamic_quant,
482-
config=config,
483-
)
484-
485470
# Attention (no added_kv_proj_dim since tokens are already concatenated)
486471
self.attn = FluxJointAttention(
487472
hidden_size=dim,
@@ -495,6 +480,21 @@ def __init__(
495480
module_name=f"single_transformer_blocks.{layer_idx}.attn",
496481
)
497482

483+
# MLP + Attn Output projection, requires special handling for TP
484+
self.proj_out = FluxJointAttnMLPProj(
485+
attn_dim=self.attn.q_dim,
486+
mlp_dim=self.mlp_hidden_dim,
487+
out_dim=dim,
488+
bias=True,
489+
dtype=dtype,
490+
quant_config=quant_config,
491+
skip_create_weights_in_init=skip_create_weights,
492+
force_dynamic_quantization=force_dynamic_quant,
493+
config=config,
494+
# need explicit shard because we are aligned on head boundaries
495+
attn_shard=(self.attn.local_q_dim_start, self.attn.local_q_dim_end),
496+
)
497+
498498
def forward(
499499
self,
500500
hidden_states: torch.Tensor,

tensorrt_llm/_torch/visual_gen/models/wan/transformer_wan.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ def __init__(
371371
force_dynamic_quantization=force_dynamic_quant,
372372
tensor_parallel_mode=tp_mode,
373373
reduce_output=False,
374+
override_tp_sharding=(self.attn2.local_kv_dim_start, self.attn2.local_kv_dim_end),
374375
)
375376
self.add_v_proj = Linear(
376377
added_kv_proj_dim,
@@ -382,6 +383,7 @@ def __init__(
382383
force_dynamic_quantization=force_dynamic_quant,
383384
tensor_parallel_mode=tp_mode,
384385
reduce_output=False,
386+
override_tp_sharding=(self.attn2.local_kv_dim_start, self.attn2.local_kv_dim_end),
385387
)
386388
self.norm_added_k = RMSNormTPAware(
387389
hidden_size=hidden_size,
@@ -390,6 +392,7 @@ def __init__(
390392
has_weights=True,
391393
enable_tp=(tp_size > 1),
392394
mapping=model_config.mapping,
395+
override_tp_sharding=(self.attn2.local_kv_dim_start, self.attn2.local_kv_dim_end),
393396
)
394397

395398
# Use torch.empty().normal_(std=...) instead of torch.randn()/scale for MetaInitMode compatibility

tensorrt_llm/_torch/visual_gen/modules/attention.py

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,7 @@ def __init__(
7575
self.bias = bias
7676

7777
self.tp_size = self.mapping.tp_size if self.mapping else 1
78-
assert (
79-
self.num_attention_heads % self.tp_size == 0
80-
and self.num_key_value_heads % self.tp_size == 0
81-
), "TP size must divide the number of Query and KV Heads"
78+
self.tp_rank = self.mapping.tp_rank if self.mapping else 0
8279

8380
# Fused QK Norm + RoPE: each model class opts in via fuse_qk_norm_rope.
8481
# Backed by torch.ops.trtllm.fused_dit_qk_norm_rope which auto-dispatches:
@@ -112,11 +109,7 @@ def __init__(
112109
self.q_dim = self.num_attention_heads * self.head_dim
113110
self.kv_dim = self.num_key_value_heads * self.head_dim
114111

115-
self.local_num_attention_heads = self.num_attention_heads // self.tp_size
116-
self.local_num_key_value_heads = self.num_key_value_heads // self.tp_size
117-
self.local_q_dim = self.local_num_attention_heads * self.head_dim
118-
self.local_kv_dim = self.local_num_key_value_heads * self.head_dim
119-
112+
self._calculate_tp_parameters(ulysses_size if enable_ulysses else None)
120113
self._init_qkv_proj()
121114

122115
# Structural eligibility for SEPARATE_QKV self-attn quantize dedup.
@@ -142,13 +135,20 @@ def __init__(
142135
q_norm_dim = self.head_dim if qk_norm_mode == "per_head" else self.q_dim
143136
k_norm_dim = self.head_dim if qk_norm_mode == "per_head" else self.kv_dim
144137
enable_tp_rms = self.tp_size > 1 and qk_norm_mode == "full"
138+
139+
q_start = self.local_q_dim_start
140+
q_end = self.local_q_dim_end
141+
k_start = self.local_kv_dim_start
142+
k_end = self.local_kv_dim_end
143+
145144
self.norm_q = RMSNormTPAware(
146145
hidden_size=q_norm_dim,
147146
eps=self.eps,
148147
dtype=self.dtype,
149148
has_weights=True,
150149
enable_tp=enable_tp_rms,
151150
mapping=self.mapping,
151+
override_tp_sharding=(q_start, q_end) if qk_norm_mode == "full" else None,
152152
)
153153
self.norm_k = RMSNormTPAware(
154154
hidden_size=k_norm_dim,
@@ -157,6 +157,7 @@ def __init__(
157157
has_weights=True,
158158
enable_tp=enable_tp_rms,
159159
mapping=self.mapping,
160+
override_tp_sharding=(k_start, k_end) if qk_norm_mode == "full" else None,
160161
)
161162

162163
# TODO: Use weight mapper to create just a Linear module
@@ -174,6 +175,7 @@ def __init__(
174175
tensor_parallel_mode=TensorParallelMode.ROW if self.tp_size > 1 else None,
175176
reduce_output=(self.tp_size > 1),
176177
allreduce_strategy=self.allreduce_strategy,
178+
override_tp_sharding=(self.local_q_dim_start, self.local_q_dim_end),
177179
)
178180
]
179181
)
@@ -249,6 +251,46 @@ def _qualified_module_name(
249251
prefix = f"{component_name}."
250252
return module_name if module_name.startswith(prefix) else f"{prefix}{module_name}"
251253

254+
def _calculate_tp_parameters(self, ulysses_size: Optional[int]):
255+
assert self.num_attention_heads % self.num_key_value_heads == 0
256+
gqa_ratio = self.num_attention_heads // self.num_key_value_heads
257+
258+
if not ulysses_size:
259+
ulysses_size = 1
260+
261+
assert self.num_key_value_heads % ulysses_size == 0
262+
# Note: this is intentionally stronger than `num_kv_head >= ulysses_size * tp_size`
263+
assert self.num_key_value_heads // ulysses_size >= self.tp_size
264+
265+
def _calc_shard(full, size, rank):
266+
full //= ulysses_size
267+
shard = (full // size) * rank + min(full % size, rank)
268+
return shard * ulysses_size
269+
270+
self.local_key_value_head_start = _calc_shard(
271+
self.num_key_value_heads, self.tp_size, self.tp_rank
272+
)
273+
self.local_key_value_head_end = _calc_shard(
274+
self.num_key_value_heads, self.tp_size, self.tp_rank + 1
275+
)
276+
self.local_num_key_value_heads = (
277+
self.local_key_value_head_end - self.local_key_value_head_start
278+
)
279+
280+
self.local_attention_head_start = gqa_ratio * self.local_key_value_head_start
281+
self.local_attention_head_end = gqa_ratio * self.local_key_value_head_end
282+
self.local_num_attention_heads = (
283+
self.local_attention_head_end - self.local_attention_head_start
284+
)
285+
286+
self.local_q_dim_start = self.local_attention_head_start * self.head_dim
287+
self.local_q_dim_end = self.local_attention_head_end * self.head_dim
288+
self.local_q_dim = self.local_q_dim_end - self.local_q_dim_start
289+
290+
self.local_kv_dim_start = self.local_key_value_head_start * self.head_dim
291+
self.local_kv_dim_end = self.local_key_value_head_end * self.head_dim
292+
self.local_kv_dim = self.local_kv_dim_end - self.local_kv_dim_start
293+
252294
def _init_qkv_proj(self) -> None:
253295
tp_mode = TensorParallelMode.COLUMN if self.tp_size > 1 else None
254296

@@ -276,6 +318,11 @@ def _init_qkv_proj(self) -> None:
276318
},
277319
tensor_parallel_mode=tp_mode,
278320
reduce_output=False,
321+
override_tp_sharding={
322+
"q": (self.local_q_dim_start, self.local_q_dim_end),
323+
"k": (self.local_kv_dim_start, self.local_kv_dim_end),
324+
"v": (self.local_kv_dim_start, self.local_kv_dim_end),
325+
},
279326
)
280327
else:
281328
self.to_q = Linear(
@@ -289,6 +336,7 @@ def _init_qkv_proj(self) -> None:
289336
force_dynamic_quantization=self.force_dynamic_quantization,
290337
tensor_parallel_mode=tp_mode,
291338
reduce_output=False,
339+
override_tp_sharding=(self.local_q_dim_start, self.local_q_dim_end),
292340
)
293341
self.to_k = Linear(
294342
self.hidden_size,
@@ -301,6 +349,7 @@ def _init_qkv_proj(self) -> None:
301349
force_dynamic_quantization=self.force_dynamic_quantization,
302350
tensor_parallel_mode=tp_mode,
303351
reduce_output=False,
352+
override_tp_sharding=(self.local_kv_dim_start, self.local_kv_dim_end),
304353
)
305354
self.to_v = Linear(
306355
self.hidden_size,
@@ -313,6 +362,7 @@ def _init_qkv_proj(self) -> None:
313362
force_dynamic_quantization=self.force_dynamic_quantization,
314363
tensor_parallel_mode=tp_mode,
315364
reduce_output=False,
365+
override_tp_sharding=(self.local_kv_dim_start, self.local_kv_dim_end),
316366
)
317367

318368
def get_qkv(

0 commit comments

Comments
 (0)