@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 5efd6905bc
Fork 0

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

[CLI][Doc] Formalize `--mm-encoder-tp-mode` (#23190)

Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>

Cyrus Leung committed Aug 20, 2025 at 23:42 UTC 5efd6905bc8469a30664de83bdafaad56aa92903
7 files changed +104 -24
docs/configuration/optimization.md
+45
@@ -129,6 +129,51 @@ Data parallelism replicates the entire model across multiple GPU sets and proces
129 Data parallelism can be combined with the other parallelism strategies and is set by `data_parallel_size=N`.
130 Note that MoE layers will be sharded according to the product of the tensor parallel size and data parallel size.
131
132 +### Batch-level DP for Multi-Modal Encoders
133 +
134 +By default, TP is used to shard the weights of multi-modal encoders just like for language decoders,
135 +in order to reduce the memory and compute load on each GPU.
136 +
137 +However, since the size of multi-modal encoders is very small compared to language decoders,
138 +there is relatively little gain from TP. On the other hand, TP incurs significant communication
139 +overhead because of all-reduce being performed after every layer.
140 +
141 +Given this, it may be advantageous to instead shard the batched input data using TP, essentially
142 +performing batch-level DP. This has been shown to improve the throughput by around 10% for
143 +`tensor_parallel_size=8`. For vision encoders that use hardware-unoptimized Conv3D operations,
144 +batch-level DP can provide another 40% increase to throughput compared to regular TP.
145 +
146 +Nevertheless, since the weights of the multi-modal encoder are replicated across each TP rank,
147 +there will be a minor increase in memory consumption and may cause OOM if you can barely fit the model already.
148 +
149 +You can enable batch-level DP by setting `mm_encoder_tp_mode="data"`, for example:
150 +
151 +```python
152 +from vllm import LLM
153 +
154 +llm = LLM(
155 + model="Qwen/Qwen2.5-VL-72B-Instruct",
156 + # Create two EngineCore instances, one per DP rank
157 + data_parallel_size=2,
158 + # Within each EngineCore instance:
159 + # The vision encoder uses TP=4 (not DP=2) to shard the input data
160 + # The language decoder uses TP=4 to shard the weights as usual
161 + tensor_parallel_size=4,
162 + mm_encoder_tp_mode="data",
163 +)
164 +```
165 +
166 +!! important
167 + Batch-level DP is not to be confused with API request-level DP
168 + (which is instead controlled by `data_parallel_size`).
169 +
170 +The availablilty of batch-level DP is based on model implementation.
171 +Currently, the following models support `mm_encoder_tp_mode="data"`:
172 +
173 +- Llama4 (<gh-pr:18368>)
174 +- Qwen2.5-VL (<gh-pr:22742>)
175 +- Step3 (<gh-pr:22697>)
176 +
177 ## Input Processing
178
179 ### Parallel Processing
vllm/config/__init__.py
+33 -1
@@ -258,6 +258,7 @@ TokenizerMode = Literal["auto", "slow", "mistral", "custom"]
258 ModelDType = Literal["auto", "half", "float16", "bfloat16", "float", "float32"]
259 LogprobsMode = Literal["raw_logprobs", "raw_logits", "processed_logprobs",
260 "processed_logits"]
261 +MMEncoderTPMode = Literal["weights", "data"]
262
263
264 @config
@@ -438,6 +439,19 @@ class ModelConfig:
439 `mm_processor_cache_gb * (api_server_count + data_parallel_size)`.
440
441 Set to `0` to disable this cache completely (not recommended)."""
442 + mm_encoder_tp_mode: MMEncoderTPMode = "weights"
443 + """Indicates how to optimize multi-modal encoder inference using
444 + tensor parallelism (TP).
445 +
446 + - `"weights"`: Within the same vLLM engine, split the weights of
447 + each layer across TP ranks. (default TP behavior)
448 + - `"data"`: Within the same vLLM engine, split the batched input data
449 + across TP ranks to process the data in parallel, while hosting
450 + the full weights on each TP rank.
451 + This batch-level DP is not to be confused with API request-level
452 + DP (which is controlled by `--data-parallel-size`).
453 + This is only supported on a per-model basis and falls back to
454 + `"weights"` if the encoder does not support DP."""
455 override_neuron_config: dict[str, Any] = field(default_factory=dict)
456 """Initialize non-default neuron config or override default neuron config
457 that are specific to Neuron devices, this argument will be used to
@@ -856,8 +870,10 @@ class ModelConfig:
870 media_io_kwargs=self.media_io_kwargs,
871 mm_processor_kwargs=self.mm_processor_kwargs,
872 mm_processor_cache_gb=self.mm_processor_cache_gb,
873 + mm_encoder_tp_mode=self.mm_encoder_tp_mode,
874 interleave_mm_strings=self.interleave_mm_strings,
860 - skip_mm_profiling=self.skip_mm_profiling)
875 + skip_mm_profiling=self.skip_mm_profiling,
876 + )
877
878 return None
879
@@ -2547,6 +2563,22 @@ class MultiModalConfig:
2563 Set to `0` to disable this cache completely (not recommended).
2564 """
2565
2566 + mm_encoder_tp_mode: MMEncoderTPMode = "weights"
2567 + """
2568 + Indicates how to optimize multi-modal encoder inference using
2569 + tensor parallelism (TP).
2570 +
2571 + - `"weights"`: Within the same vLLM engine, split the weights of
2572 + each layer across TP ranks. (default TP behavior)
2573 + - `"data"`: Within the same vLLM engine, split the batched input data
2574 + across TP ranks to process the data in parallel, while hosting
2575 + the full weights on each TP rank.
2576 + This batch-level DP is not to be confused with API request-level
2577 + DP (which is controlled by `--data-parallel-size`).
2578 + This is only supported on a per-model basis and falls back to
2579 + `"weights"` if the encoder does not support DP.
2580 + """
2581 +
2582 interleave_mm_strings: bool = False
2583 """
2584 Enable fully interleaved support for multimodal prompts.
vllm/config/parallel.py
-4
@@ -137,10 +137,6 @@ class ParallelConfig:
137 rank: int = 0
138 """Global rank in distributed setup."""
139
140 - enable_multimodal_encoder_data_parallel: bool = False
141 - """ Use data parallelism instead of tensor parallelism for vision encoder.
142 - Only support LLama4 for now"""
143 -
140 @property
141 def world_size_across_dp(self) -> int:
142 """world_size_across_dp is TPxPPxDP, it is the size of the world
vllm/engine/arg_utils.py
+22 -13
@@ -28,12 +28,12 @@ from vllm.config import (BlockSize, CacheConfig, CacheDType, CompilationConfig,
28 DeviceConfig, DistributedExecutorBackend,
29 GuidedDecodingBackend, HfOverrides, KVEventsConfig,
30 KVTransferConfig, LoadConfig, LogprobsMode,
31 - LoRAConfig, MambaDType, ModelConfig, ModelDType,
32 - ModelImpl, MultiModalConfig, ObservabilityConfig,
33 - ParallelConfig, PoolerConfig, PrefixCachingHashAlgo,
34 - RunnerOption, SchedulerConfig, SchedulerPolicy,
35 - SpeculativeConfig, TaskOption, TokenizerMode,
36 - VllmConfig, get_attr_docs, get_field)
31 + LoRAConfig, MambaDType, MMEncoderTPMode, ModelConfig,
32 + ModelDType, ModelImpl, MultiModalConfig,
33 + ObservabilityConfig, ParallelConfig, PoolerConfig,
34 + PrefixCachingHashAlgo, RunnerOption, SchedulerConfig,
35 + SchedulerPolicy, SpeculativeConfig, TaskOption,
36 + TokenizerMode, VllmConfig, get_attr_docs, get_field)
37 from vllm.logger import init_logger
38 from vllm.platforms import CpuArchEnum, current_platform
39 from vllm.plugins import load_general_plugins
@@ -352,6 +352,7 @@ class EngineArgs:
352 MultiModalConfig.mm_processor_kwargs
353 disable_mm_preprocessor_cache: bool = False # DEPRECATED
354 mm_processor_cache_gb: int = MultiModalConfig.mm_processor_cache_gb
355 + mm_encoder_tp_mode: MMEncoderTPMode = MultiModalConfig.mm_encoder_tp_mode
356 skip_mm_profiling: bool = MultiModalConfig.skip_mm_profiling
357 # LoRA fields
358 enable_lora: bool = False
@@ -434,16 +435,14 @@ class EngineArgs:
435 use_tqdm_on_load: bool = LoadConfig.use_tqdm_on_load
436 pt_load_map_location: str = LoadConfig.pt_load_map_location
437
437 - enable_multimodal_encoder_data_parallel: bool = \
438 - ParallelConfig.enable_multimodal_encoder_data_parallel
438 + # DEPRECATED
439 + enable_multimodal_encoder_data_parallel: bool = False
440
441 logits_processors: Optional[list[Union[
442 str, type[LogitsProcessor]]]] = ModelConfig.logits_processors
443 """Custom logitproc types"""
444
445 async_scheduling: bool = SchedulerConfig.async_scheduling
445 - # DEPRECATED
446 - enable_prompt_adapter: bool = False
446
447 kv_sharing_fast_prefill: bool = \
448 CacheConfig.kv_sharing_fast_prefill
@@ -685,7 +684,8 @@ class EngineArgs:
684 **parallel_kwargs["worker_extension_cls"])
685 parallel_group.add_argument(
686 "--enable-multimodal-encoder-data-parallel",
688 - **parallel_kwargs["enable_multimodal_encoder_data_parallel"])
687 + action="store_true",
688 + deprecated=True)
689
690 # KV cache arguments
691 cache_kwargs = get_kwargs(CacheConfig)
@@ -735,6 +735,8 @@ class EngineArgs:
735 multimodal_group.add_argument("--disable-mm-preprocessor-cache",
736 action="store_true",
737 deprecated=True)
738 + multimodal_group.add_argument(
739 + "--mm-encoder-tp-mode", **multimodal_kwargs["mm_encoder_tp_mode"])
740 multimodal_group.add_argument(
741 "--interleave-mm-strings",
742 **multimodal_kwargs["interleave_mm_strings"])
@@ -909,6 +911,14 @@ class EngineArgs:
911
912 self.mm_processor_cache_gb = envs.VLLM_MM_INPUT_CACHE_GIB
913
914 + if self.enable_multimodal_encoder_data_parallel:
915 + logger.warning(
916 + "--enable-multimodal-encoder-data-parallel` is deprecated "
917 + "and will be removed in v0.13. "
918 + "Please use `--mm-encoder-tp-mode data` instead.")
919 +
920 + self.mm_encoder_tp_mode = "data"
921 +
922 return ModelConfig(
923 model=self.model,
924 hf_config_path=self.hf_config_path,
@@ -947,6 +957,7 @@ class EngineArgs:
957 config_format=self.config_format,
958 mm_processor_kwargs=self.mm_processor_kwargs,
959 mm_processor_cache_gb=self.mm_processor_cache_gb,
960 + mm_encoder_tp_mode=self.mm_encoder_tp_mode,
961 override_neuron_config=self.override_neuron_config,
962 override_pooler_config=self.override_pooler_config,
963 logits_processor_pattern=self.logits_processor_pattern,
@@ -1258,8 +1269,6 @@ class EngineArgs:
1269 distributed_executor_backend=self.distributed_executor_backend,
1270 worker_cls=self.worker_cls,
1271 worker_extension_cls=self.worker_extension_cls,
1261 - enable_multimodal_encoder_data_parallel=self.
1262 - enable_multimodal_encoder_data_parallel,
1272 )
1273
1274 if model_config.is_multimodal_model:
vllm/model_executor/models/mllama4.py
+2 -2
@@ -728,8 +728,8 @@ class Llama4ForConditionalGeneration(nn.Module, SupportsMultiModal,
728 config = vllm_config.model_config.hf_config
729 quant_config = vllm_config.quant_config
730 multimodal_config = vllm_config.model_config.multimodal_config
731 - self.use_data_parallel = (vllm_config.parallel_config.
732 - enable_multimodal_encoder_data_parallel)
731 + self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
732 +
733 self.config = config
734 self.quant_config = quant_config
735 self.multimodal_config = multimodal_config
vllm/model_executor/models/qwen2_5_vl.py
+1 -2
@@ -877,8 +877,7 @@ class Qwen2_5_VLForConditionalGeneration(nn.Module, SupportsMultiModal,
877 config: Qwen2_5_VLConfig = vllm_config.model_config.hf_config
878 multimodal_config = vllm_config.model_config.multimodal_config
879
880 - self.use_data_parallel = (vllm_config.parallel_config.
881 - enable_multimodal_encoder_data_parallel)
880 + self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
881 self.config = config
882 self.multimodal_config = multimodal_config
883
vllm/model_executor/models/step3_vl.py
+1 -2
@@ -882,8 +882,7 @@ class Step3VLForConditionalGeneration(nn.Module, SupportsMultiModal,
882
883 self.config = config
884 self.multimodal_config = multimodal_config
885 - self.use_data_parallel = (vllm_config.parallel_config.
886 - enable_multimodal_encoder_data_parallel)
885 + self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
886
887 if multimodal_config.get_limit_per_prompt("image"):
888 self.vision_model = Step3VisionTransformer(