Skip to content

Commit bbbfed0

Browse files
committed
address Shreyas' comments, add lpips test
Signed-off-by: Brenden Elgarten <belgarten@nvidia.com>
1 parent d7f3f89 commit bbbfed0

6 files changed

Lines changed: 64 additions & 29 deletions

File tree

tensorrt_llm/_torch/modules/gated_mlp.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ def __init__(
7171
mapping.tp_rank + 1)
7272
local_intermediate_size = local_intermediate_end - local_intermediate_start
7373

74+
self._uneven_tp_blocks_lora = (mapping.tp_size > 1
75+
and self.intermediate_size %
76+
mapping.tp_size != 0)
77+
78+
# gateup_shard_indices_mapping is the local offset and size for each sub-weight
79+
# in this rank's concatenated (gate || up) buffer.
80+
# override_tp_sharding is the absolute range of the global weight from which
81+
# this rank pulls each sub-weight.
7482
gateup_shard_indices_mapping = {
7583
'gate': (0, local_intermediate_size),
7684
'up': (local_intermediate_size, local_intermediate_size),
@@ -297,6 +305,10 @@ def forward_lora(
297305
) -> torch.Tensor:
298306
assert lora_params is not None
299307
assert self.layer_idx is not None, "layer_idx is required for lora"
308+
if self._uneven_tp_blocks_lora:
309+
raise NotImplementedError(
310+
"LoRA is not supported with uneven TP for GatedMLP "
311+
"(intermediate_size not divisible by tp_size).")
300312

301313
h1 = self.gate_up_proj(x)
302314

tensorrt_llm/_torch/modules/linear.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ def load_weight_shard(
106106
device: torch.device = torch.device('cpu'),
107107
return_slice_indices: bool = False,
108108
) -> torch.Tensor:
109+
"""Legacy weight shard helper using ceil-divide sharding.
110+
111+
`Linear.load_shard` is preferred — it respects uneven-TP overrides, fused
112+
QKV/gate-up sharding dicts, and quant-specific scale/packing semantics that
113+
this function does not.
114+
"""
109115
# Skip device transfers on integrated GPUs to conserve shared memory
110116
if weight.device.type != device.type and is_device_integrated():
111117
# For integrated GPU systems (e.g., DGX Spark), CPU and GPU share limited physical memory.
@@ -2923,8 +2929,11 @@ def __init__(
29232929
'cutlass', 'cublaslt', 'cuda_core'
29242930
]
29252931

2926-
assert self.tp_mode in (TensorParallelMode.ROW,
2927-
TensorParallelMode.COLUMN, None)
2932+
if self.tp_mode not in (TensorParallelMode.ROW,
2933+
TensorParallelMode.COLUMN, None):
2934+
raise ValueError(
2935+
f"Invalid tp_mode {self.tp_mode!r}; expected ROW, COLUMN, or None."
2936+
)
29282937

29292938
# Init TP sharding either from override or auto generated
29302939
_uneven_tp_unsupported = {QuantAlgo.NVFP4_ARC}
@@ -3006,8 +3015,10 @@ def _calc_shard(total, tp_size, rank):
30063015
def _auto_tp_sharding(self, features, quant_config):
30073016
"""Auto-generate tp_sharding tuple based on quant alignment requirements.
30083017
3009-
For VANILLA mode only. Fused modes with non-divisible dims require
3010-
explicit override_tp_sharding from the model layer.
3018+
VANILLA mode only. Fused modes (FUSED_QKV, FUSED_GATE_UP) require explicit
3019+
override_tp_sharding because individual sub-weight sizes (Q vs K vs V; gate
3020+
vs up) are not knowable here — they aren't always equal (e.g. GQA), and
3021+
cross-rank consistency must be decided by the caller.
30113022
"""
30123023
assert self.weights_loading_config.weight_mode == WeightMode.VANILLA, (
30133024
f"_auto_tp_sharding only supports VANILLA mode, got "
@@ -3049,20 +3060,22 @@ def _calculate_local_features_helper(self, features):
30493060
return end - start
30503061

30513062
def calculate_local_in_features(self, in_features):
3063+
"""Local input feature count after TP sharding (full size if not row-parallel)."""
30523064
if self.tp_mode != TensorParallelMode.ROW:
30533065
return in_features
30543066

30553067
return self._calculate_local_features_helper(in_features)
30563068

30573069
def calculate_local_out_features(self, out_features):
3070+
"""Local output feature count after TP sharding (full size if not column-parallel)."""
30583071
if self.tp_mode != TensorParallelMode.COLUMN:
30593072
return out_features
30603073

30613074
return self._calculate_local_features_helper(out_features)
30623075

30633076
def load_shard(
30643077
self,
3065-
weights: Dict,
3078+
weights: Union[Dict, torch.Tensor],
30663079
label: Optional[str] = None,
30673080
device: torch.device = torch.device('cpu'),
30683081
name: Optional[str] = None,
@@ -3072,6 +3085,14 @@ def load_shard(
30723085
# 2 are packed in each 8 bit element of the tensor
30733086
elm_packing: int = 1,
30743087
) -> torch.Tensor:
3088+
"""Slice a weight tensor for this rank's TP shard.
3089+
3090+
Unified entry point for module-aware weight loading: respects
3091+
`self.tp_sharding` (uneven TP overrides, fused QKV/gate-up dicts) and
3092+
quant-specific knobs (`scale_span`, `elm_packing`). Pass `weights` as a
3093+
dict with `label` to pick the entry, or as a bare tensor when there's
3094+
only one. Supersedes the free function `load_weight_shard`.
3095+
"""
30753096
if label:
30763097
if label not in weights:
30773098
return None

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def __init__(
6363
self.has_bias = bias
6464
self.attn_shard = attn_shard
6565

66-
assert attn_dim % self.tp_size == 0 or self.attn_shard, (
66+
assert attn_dim % self.tp_size == 0 or self.attn_shard is not None, (
6767
"Explicit attention sharding required for uneven TP"
6868
)
6969

@@ -197,6 +197,9 @@ def __init__(
197197
self.local_qkv_dim = q_dim + 2 * kv_dim
198198
self.local_mlp_dim = mlp_dim
199199
else:
200+
assert override_qkv_sharding is not None, (
201+
"override_qkv_sharding required when tp_size > 1"
202+
)
200203

201204
def range_size(r):
202205
return r[1] - r[0]

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,21 @@ def __init__(
495495
attn_shard=(self.attn.local_q_dim_start, self.attn.local_q_dim_end),
496496
)
497497

498+
# MLP + Attn Output projection, requires special handling for TP
499+
self.proj_out = FluxJointAttnMLPProj(
500+
attn_dim=self.attn.q_dim,
501+
mlp_dim=self.mlp_hidden_dim,
502+
out_dim=dim,
503+
bias=True,
504+
dtype=dtype,
505+
quant_config=quant_config,
506+
skip_create_weights_in_init=skip_create_weights,
507+
force_dynamic_quantization=force_dynamic_quant,
508+
config=config,
509+
# need explicit shard because we are aligned on head boundaries
510+
attn_shard=(self.attn.local_q_dim_start, self.attn.local_q_dim_end),
511+
)
512+
498513
def forward(
499514
self,
500515
hidden_states: torch.Tensor,

tensorrt_llm/_torch/visual_gen/modules/attention.py

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def __init__(
109109
self.q_dim = self.num_attention_heads * self.head_dim
110110
self.kv_dim = self.num_key_value_heads * self.head_dim
111111

112-
self._calculate_tp_parameters(ulysses_size if enable_ulysses else None)
112+
self._calculate_tp_parameters(ulysses_size if enable_sequence_parallel else None)
113113
self._init_qkv_proj()
114114

115115
# Structural eligibility for SEPARATE_QKV self-attn quantize dedup.
@@ -239,18 +239,6 @@ def __init__(
239239
async_ulysses=use_ulysses and async_ulysses,
240240
)
241241

242-
@staticmethod
243-
def _qualified_module_name(
244-
component_name: Optional[str],
245-
module_name: Optional[str],
246-
) -> Optional[str]:
247-
if module_name is None:
248-
return None
249-
if component_name is None:
250-
return module_name
251-
prefix = f"{component_name}."
252-
return module_name if module_name.startswith(prefix) else f"{prefix}{module_name}"
253-
254242
def _calculate_tp_parameters(self, ulysses_size: Optional[int]):
255243
assert self.num_attention_heads % self.num_key_value_heads == 0
256244
gqa_ratio = self.num_attention_heads // self.num_key_value_heads
@@ -259,19 +247,14 @@ def _calculate_tp_parameters(self, ulysses_size: Optional[int]):
259247
ulysses_size = 1
260248

261249
assert self.num_key_value_heads % ulysses_size == 0
262-
# Note: this is intentionally stronger than `num_kv_head >= ulysses_size * tp_size`
263250
assert self.num_key_value_heads // ulysses_size >= self.tp_size
264251

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
252+
kv_heads_per_ulysses = self.num_key_value_heads // ulysses_size
253+
self.local_key_value_head_start = (
254+
Linear._calc_shard(kv_heads_per_ulysses, self.tp_size, self.tp_rank) * ulysses_size
272255
)
273-
self.local_key_value_head_end = _calc_shard(
274-
self.num_key_value_heads, self.tp_size, self.tp_rank + 1
256+
self.local_key_value_head_end = (
257+
Linear._calc_shard(kv_heads_per_ulysses, self.tp_size, self.tp_rank + 1) * ulysses_size
275258
)
276259
self.local_num_key_value_heads = (
277260
self.local_key_value_head_end - self.local_key_value_head_start

tests/integration/defs/examples/visual_gen/test_visual_gen_multi_gpu.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959

6060
WAN22_LPIPS_TP_VARIANTS = [
6161
("tp2", {"tp_size": 2}),
62+
("tp3", {"tp_size": 3}),
6263
("cfg2_tp2", {"cfg_size": 2, "tp_size": 2}),
6364
("tp2_ulysses2", {"tp_size": 2, "ulysses_size": 2}),
6465
]

0 commit comments

Comments
 (0)