A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[Model] enable data parallel for InternVL vision encoder (#23909)
Signed-off-by: Yiwen Chen <yiwen66@berkeley.edu> Signed-off-by: YiwenC <54658925+666even666@users.noreply.github.com> Co-authored-by: Roger Wang <hey@rogerw.io>
YiwenC committed
Sep 17, 2025 at 21:11 UTC
52bc9d5b3edbf8804758d46cde28024d6c362e42
3 files changed
+80
-33
docs/configuration/optimization.md
+1
@@ -175,6 +175,7 @@ Regardless, you need to set `mm_encoder_tp_mode="data"` in engine arguments to u
175
Known supported models:
176
177
- GLM-4.5V GLM-4.1V (<gh-pr:23168>)
178
+- InternVL (<gh-pr:23909>)
179
- Kimi-VL (<gh-pr:23817>)
180
- Llama4 (<gh-pr:18368>)
181
- MiniCPM-V-2.5 or above (<gh-pr:23327>, <gh-pr:23948>)
vllm/model_executor/models/intern_vit.py
+75
-32
@@ -25,9 +25,11 @@ from vllm.model_executor.layers.activation import get_act_fn
25
from vllm.model_executor.layers.layernorm import RMSNorm
26
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
27
QKVParallelLinear,
28
+ ReplicatedLinear,
29
RowParallelLinear)
30
from vllm.model_executor.layers.quantization import QuantizationConfig
31
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
32
+from vllm.multimodal.utils import run_dp_sharded_vision_model
33
34
NORM2FN = {
35
'rms_norm': RMSNorm,
@@ -137,6 +139,7 @@ class InternParallelAttention(nn.Module):
139
*,
140
num_dummy_heads: int = 0,
141
prefix: str = "",
142
+ use_data_parallel: bool = False,
143
) -> None:
144
super().__init__()
145
@@ -150,8 +153,10 @@ class InternParallelAttention(nn.Module):
153
f'(got `embed_dim`: {self.embed_dim} and `num_heads`:'
154
f' {self.num_heads}).')
155
153
- self.tp_size = get_tensor_model_parallel_world_size()
154
- self.tp_rank = get_tensor_model_parallel_rank()
156
+ self.tp_size = (1 if use_data_parallel else
157
+ get_tensor_model_parallel_world_size())
158
+ self.tp_rank = (0 if use_data_parallel else
159
+ get_tensor_model_parallel_rank())
160
161
# Additional dummy heads are used to enable TP for common GPU counts.
162
self.dummy_dim = (num_dummy_heads + self.num_heads) * self.head_dim
@@ -159,14 +164,23 @@ class InternParallelAttention(nn.Module):
164
self.tp_size)
165
166
self.scale = self.head_dim**-0.5
162
- self.qkv = QKVParallelLinear(
163
- self.embed_dim,
164
- self.head_dim,
165
- num_dummy_heads + self.num_heads,
166
- bias=config.qkv_bias,
167
- quant_config=quant_config,
168
- prefix=f"{prefix}.qkv",
169
- )
167
+ if use_data_parallel:
168
+ self.qkv = ReplicatedLinear(
169
+ self.embed_dim,
170
+ 3 * self.head_dim * self.num_heads,
171
+ bias=config.qkv_bias,
172
+ quant_config=quant_config,
173
+ prefix=f"{prefix}.qkv",
174
+ )
175
+ else:
176
+ self.qkv = QKVParallelLinear(
177
+ self.embed_dim,
178
+ self.head_dim,
179
+ num_dummy_heads + self.num_heads,
180
+ bias=config.qkv_bias,
181
+ quant_config=quant_config,
182
+ prefix=f"{prefix}.qkv",
183
+ )
184
185
self.qk_normalization = config.qk_normalization
186
@@ -178,12 +192,20 @@ class InternParallelAttention(nn.Module):
192
eps=config.layer_norm_eps,
193
var_hidden_size=self.embed_dim)
194
181
- self.proj = RowParallelLinear(
182
- self.dummy_dim,
183
- self.embed_dim,
184
- quant_config=quant_config,
185
- prefix=f"{prefix}.proj",
186
- )
195
+ if use_data_parallel:
196
+ self.proj = ReplicatedLinear(
197
+ self.dummy_dim,
198
+ self.embed_dim,
199
+ quant_config=quant_config,
200
+ prefix=f"{prefix}.proj",
201
+ )
202
+ else:
203
+ self.proj = RowParallelLinear(
204
+ self.dummy_dim,
205
+ self.embed_dim,
206
+ quant_config=quant_config,
207
+ prefix=f"{prefix}.proj",
208
+ )
209
210
self.attn = MultiHeadAttention(self.num_heads_per_partition,
211
self.head_dim, self.scale)
@@ -287,21 +309,26 @@ class InternMLP(nn.Module):
309
config: PretrainedConfig,
310
quant_config: Optional[QuantizationConfig] = None,
311
prefix: str = "",
312
+ use_data_parallel: bool = False,
313
) -> None:
314
super().__init__()
315
316
self.config = config
317
self.activation_fn = get_act_fn(config.hidden_act)
295
- self.fc1 = ColumnParallelLinear(config.hidden_size,
296
- config.intermediate_size,
297
- bias=True,
298
- quant_config=quant_config,
299
- prefix=f"{prefix}.fc1")
300
- self.fc2 = RowParallelLinear(config.intermediate_size,
301
- config.hidden_size,
302
- bias=True,
303
- quant_config=quant_config,
304
- prefix=f"{prefix}.fc2")
318
+ cls_fc1 = (ReplicatedLinear
319
+ if use_data_parallel else ColumnParallelLinear)
320
+ self.fc1 = cls_fc1(config.hidden_size,
321
+ config.intermediate_size,
322
+ bias=True,
323
+ quant_config=quant_config,
324
+ prefix=f"{prefix}.fc1")
325
+ cls_fc2 = (ReplicatedLinear
326
+ if use_data_parallel else RowParallelLinear)
327
+ self.fc2 = cls_fc2(config.intermediate_size,
328
+ config.hidden_size,
329
+ bias=True,
330
+ quant_config=quant_config,
331
+ prefix=f"{prefix}.fc2")
332
333
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
334
hidden_states, _ = self.fc1(hidden_states)
@@ -320,6 +347,7 @@ class InternVisionEncoderLayer(nn.Module):
347
*,
348
num_dummy_heads: int = 0,
349
prefix: str = "",
350
+ use_data_parallel: bool = False,
351
) -> None:
352
super().__init__()
353
@@ -330,11 +358,13 @@ class InternVisionEncoderLayer(nn.Module):
358
self.attn = self._init_attn(config,
359
quant_config,
360
num_dummy_heads=num_dummy_heads,
333
- prefix=f"{prefix}.attn")
361
+ prefix=f"{prefix}.attn",
362
+ use_data_parallel=use_data_parallel)
363
364
self.mlp = InternMLP(config,
365
quant_config=quant_config,
337
- prefix=f"{prefix}.mlp")
366
+ prefix=f"{prefix}.mlp",
367
+ use_data_parallel=use_data_parallel)
368
self.norm1 = NORM2FN[self.norm_type](self.embed_dim,
369
eps=config.layer_norm_eps)
370
self.norm2 = NORM2FN[self.norm_type](self.embed_dim,
@@ -352,16 +382,20 @@ class InternVisionEncoderLayer(nn.Module):
382
*,
383
num_dummy_heads: int,
384
prefix: str = "",
385
+ use_data_parallel: bool = False,
386
):
387
# fallback to sdpa attention if tp unavailable
357
- tp_size = get_tensor_model_parallel_world_size()
388
+ # tp_size = get_tensor_model_parallel_world_size()
389
+ tp_size = (1 if use_data_parallel else
390
+ get_tensor_model_parallel_world_size())
391
num_heads = config.num_attention_heads
392
393
if (num_heads + num_dummy_heads) % tp_size == 0:
394
return InternParallelAttention(config,
395
quant_config=quant_config,
396
num_dummy_heads=num_dummy_heads,
364
- prefix=prefix)
397
+ prefix=prefix,
398
+ use_data_parallel=use_data_parallel)
399
400
return InternSdpaAttention(config, num_dummy_heads=num_dummy_heads)
401
@@ -388,6 +422,7 @@ class InternVisionEncoder(nn.Module):
422
num_hidden_layers_override: Optional[int] = None,
423
num_dummy_heads: int = 0,
424
prefix: str = "",
425
+ use_data_parallel: bool = False,
426
):
427
super().__init__()
428
@@ -402,7 +437,8 @@ class InternVisionEncoder(nn.Module):
437
InternVisionEncoderLayer(config,
438
quant_config,
439
num_dummy_heads=num_dummy_heads,
405
- prefix=f"{prefix}.layers.{layer_idx}")
440
+ prefix=f"{prefix}.layers.{layer_idx}",
441
+ use_data_parallel=use_data_parallel)
442
for layer_idx in range(num_hidden_layers)
443
])
444
@@ -429,10 +465,12 @@ class InternVisionModel(nn.Module):
465
num_hidden_layers_override: Optional[int] = None,
466
num_dummy_heads: int = 0,
467
prefix: str = "",
468
+ use_data_parallel: bool = False,
469
) -> None:
470
super().__init__()
471
472
self.config = config
473
+ self.use_data_parallel = use_data_parallel
474
475
self.embeddings = InternVisionEmbeddings(config)
476
self.encoder = InternVisionEncoder(
@@ -441,6 +479,7 @@ class InternVisionModel(nn.Module):
479
num_hidden_layers_override=num_hidden_layers_override,
480
num_dummy_heads=num_dummy_heads,
481
prefix=f"{prefix}.encoder",
482
+ use_data_parallel=use_data_parallel,
483
)
484
485
def get_input_embeddings(self):
@@ -464,7 +503,11 @@ class InternVisionModel(nn.Module):
503
raise ValueError(
504
f'wrong pixel_values size: {pixel_values.shape}')
505
467
- encoder_outputs = self.encoder(inputs_embeds=hidden_states)
506
+ if self.use_data_parallel:
507
+ encoder_outputs = run_dp_sharded_vision_model(
508
+ hidden_states, self.encoder)
509
+ else:
510
+ encoder_outputs = self.encoder(inputs_embeds=hidden_states)
511
512
return encoder_outputs
513
vllm/model_executor/models/internvl.py
+4
-1
@@ -1035,6 +1035,8 @@ class InternVLMultiModalProcessor(
1035
class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
1036
SupportsLoRA):
1037
1038
+ supports_encoder_tp_data = True
1039
+
1040
@classmethod
1041
def get_placeholder_str(cls, modality: str, i: int) -> Optional[str]:
1042
if modality.startswith("image"):
@@ -1053,6 +1055,7 @@ class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
1055
1056
self.config = config
1057
self.multimodal_config = multimodal_config
1058
+ self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
1059
self._patch_quant_config(config, quant_config)
1060
1061
image_size = config.force_image_size or config.vision_config.image_size
@@ -1120,7 +1123,7 @@ class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
1123
quant_config=quant_config,
1124
num_hidden_layers_override=num_hidden_layers,
1125
prefix=prefix,
1123
- )
1126
+ use_data_parallel=self.use_data_parallel)
1127
else:
1128
return InternVisionPatchModel(config.vision_config)
1129