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 13, 10:45 UTC
[Frontend] Change CompilationMode to a proper Enum (#28165)
Signed-off-by: Yanan Cao <gmagogsfm@gmail.com>
Yanan Cao committed
Nov 11, 2025 at 16:46 UTC
48c879369f83ab1ab281a4bfe97f9a54790715d1
6 files changed
+108
-23
tests/compile/test_basic_correctness.py
+4
-2
@@ -127,7 +127,9 @@ def test_compile_correctness(
127
CompilationMode.VLLM_COMPILE,
128
]:
129
for mode in [CompilationMode.NONE, comp_mode]:
130
- all_args.append(final_args + [f"-O.mode={mode}", "-O.backend=inductor"])
130
+ all_args.append(
131
+ final_args + [f"-O.mode={mode.name}", "-O.backend=inductor"]
132
+ )
133
134
# inductor will change the output, so we only compare if the output
135
# is close, not exactly the same.
@@ -146,7 +148,7 @@ def test_compile_correctness(
148
CompilationMode.DYNAMO_TRACE_ONCE,
149
CompilationMode.VLLM_COMPILE,
150
]:
149
- all_args.append(final_args + [f"-O.mode={mode}", "-O.backend=eager"])
151
+ all_args.append(final_args + [f"-O.mode={mode.name}", "-O.backend=eager"])
152
all_envs.append({})
153
all_envs.append({})
154
tests/utils_/test_argparse_utils.py
+60
@@ -8,6 +8,7 @@ import os
8
import pytest
9
import yaml
10
from transformers import AutoTokenizer
11
+from pydantic import ValidationError
12
13
from vllm.transformers_utils.detokenizer_utils import convert_ids_list_to_tokens
14
@@ -376,6 +377,65 @@ def test_load_config_file(tmp_path):
377
os.remove(str(config_file_path))
378
379
380
+def test_compilation_mode_string_values(parser):
381
+ """Test that -O.mode accepts both integer and string mode values."""
382
+ args = parser.parse_args(["-O.mode", "0"])
383
+ assert args.compilation_config == {"mode": 0}
384
+
385
+ args = parser.parse_args(["-O3"])
386
+ assert args.compilation_config == {"mode": 3}
387
+
388
+ args = parser.parse_args(["-O.mode=NONE"])
389
+ assert args.compilation_config == {"mode": "NONE"}
390
+
391
+ args = parser.parse_args(["-O.mode", "STOCK_TORCH_COMPILE"])
392
+ assert args.compilation_config == {"mode": "STOCK_TORCH_COMPILE"}
393
+
394
+ args = parser.parse_args(["-O.mode=DYNAMO_TRACE_ONCE"])
395
+ assert args.compilation_config == {"mode": "DYNAMO_TRACE_ONCE"}
396
+
397
+ args = parser.parse_args(["-O.mode", "VLLM_COMPILE"])
398
+ assert args.compilation_config == {"mode": "VLLM_COMPILE"}
399
+
400
+ args = parser.parse_args(["-O.mode=none"])
401
+ assert args.compilation_config == {"mode": "none"}
402
+
403
+ args = parser.parse_args(["-O.mode=vllm_compile"])
404
+ assert args.compilation_config == {"mode": "vllm_compile"}
405
+
406
+
407
+def test_compilation_config_mode_validator():
408
+ """Test that CompilationConfig.mode field validator converts strings to integers."""
409
+ from vllm.config.compilation import CompilationConfig, CompilationMode
410
+
411
+ config = CompilationConfig(mode=0)
412
+ assert config.mode == CompilationMode.NONE
413
+
414
+ config = CompilationConfig(mode=3)
415
+ assert config.mode == CompilationMode.VLLM_COMPILE
416
+
417
+ config = CompilationConfig(mode="NONE")
418
+ assert config.mode == CompilationMode.NONE
419
+
420
+ config = CompilationConfig(mode="STOCK_TORCH_COMPILE")
421
+ assert config.mode == CompilationMode.STOCK_TORCH_COMPILE
422
+
423
+ config = CompilationConfig(mode="DYNAMO_TRACE_ONCE")
424
+ assert config.mode == CompilationMode.DYNAMO_TRACE_ONCE
425
+
426
+ config = CompilationConfig(mode="VLLM_COMPILE")
427
+ assert config.mode == CompilationMode.VLLM_COMPILE
428
+
429
+ config = CompilationConfig(mode="none")
430
+ assert config.mode == CompilationMode.NONE
431
+
432
+ config = CompilationConfig(mode="vllm_compile")
433
+ assert config.mode == CompilationMode.VLLM_COMPILE
434
+
435
+ with pytest.raises(ValidationError, match="Invalid compilation mode"):
436
+ CompilationConfig(mode="INVALID_MODE")
437
+
438
+
439
def test_flat_product():
440
# Check regular itertools.product behavior
441
result1 = list(flat_product([1, 2, 3], ["a", "b"]))
vllm/compilation/wrapper.py
+3
-1
@@ -31,7 +31,9 @@ class TorchCompileWrapperWithCustomDispatcher:
31
"""
32
33
def __init__(
34
- self, compiled_callable: Callable | None = None, compilation_mode: int = 0
34
+ self,
35
+ compiled_callable: Callable | None = None,
36
+ compilation_mode: CompilationMode = CompilationMode.NONE,
37
):
38
vllm_config = get_current_vllm_config()
39
self.vllm_config = vllm_config
vllm/config/compilation.py
+36
-15
@@ -28,7 +28,7 @@ else:
28
logger = init_logger(__name__)
29
30
31
-class CompilationMode:
31
+class CompilationMode(enum.IntEnum):
32
"""The compilation approach used for torch.compile-based compilation of the
33
model."""
34
@@ -115,7 +115,7 @@ class PassConfig:
115
"""The threshold of the communicated tensor sizes under which
116
vllm should use flashinfer fused allreduce. Specified as a
117
float in MB.
118
- Unspecified will fallback to default values
118
+ Unspecified will fallback to default values
119
which are compute capability and world size dependent.
120
FI_ALLREDUCE_FUSION_MAX_SIZE_MB = {
121
90: {
@@ -244,7 +244,7 @@ class CompilationConfig:
244
Please use mode. Currently all levels are mapped to mode.
245
"""
246
# Top-level Compilation control
247
- mode: int | None = None
247
+ mode: CompilationMode | None = None
248
"""The compilation approach used for torch.compile-based compilation of the
249
model.
250
@@ -377,23 +377,23 @@ class CompilationConfig:
377
FULL mode: Capture full cudagraph for all batches. Can be good for small
378
models or workloads with small prompts; not supported by many backends.
379
Generally for performance FULL_AND_PIECEWISE is better.
380
-
380
+
381
FULL_DECODE_ONLY mode: Capture full cudagraph for decode batches only.
382
Mixed prefill-decode batches are run without cudagraphs. Can be good for
383
decode instances in a P/D setup where prefill is not as important so we
384
can save some memory.
385
-
385
+
386
FULL_AND_PIECEWISE mode: Capture full cudagraph for decode batches and
387
piecewise cudagraph for prefill and mixed prefill-decode batches.
388
This is the most performant mode for most models and is the default.
389
390
Currently, the cudagraph mode is only used for the v1 engine.
391
- Note that the cudagraph logic is generally orthogonal to the
392
- compilation logic. While piecewise cudagraphs require piecewise
391
+ Note that the cudagraph logic is generally orthogonal to the
392
+ compilation logic. While piecewise cudagraphs require piecewise
393
compilation (mode=VLLM_COMPILE and non-empty splitting_ops), full
394
cudagraphs are supported with and without compilation.
395
-
396
- Warning: This flag is new and subject to change in addition
395
+
396
+ Warning: This flag is new and subject to change in addition
397
more modes may be added.
398
"""
399
use_cudagraph: bool = True
@@ -422,7 +422,7 @@ class CompilationConfig:
422
cudagraph. If the caller can guarantee that the same input buffers
423
are always used, it can set this to False. Otherwise, it should
424
set this to True, and the compiler will copy the input to an
425
- internally managed buffer. Default is False.
425
+ internally managed buffer. Default is False.
426
Note that this flag is only effective when cudagraph_mode is PIECEWISE.
427
"""
428
full_cuda_graph: bool | None = False
@@ -451,7 +451,7 @@ class CompilationConfig:
451
outside the partition functions. For a graph with N cudagraph-unsafe ops
452
(e.g., Attention), there would be N+1 partitions. To mark an op as
453
cudagraph unsafe, we can add `tags=(torch._C.Tag.cudagraph_unsafe)` when
454
- register the custom op.
454
+ register the custom op.
455
456
This config supports both full cudagraph and piecewise cudagraph without
457
compiling twice. For piecewise cudagraph, it applies vLLM CUDAGraph wrapper
@@ -468,8 +468,8 @@ class CompilationConfig:
468
469
max_cudagraph_capture_size: int | None = field(default=None)
470
"""The maximum cudagraph capture size.
471
-
472
- If cudagraph_capture_sizes is specified, this will be set to the largest
471
+
472
+ If cudagraph_capture_sizes is specified, this will be set to the largest
473
size in that list (or checked for consistency if specified). If
474
cudagraph_capture_sizes is not specified, the list of sizes is generated
475
automatically following the pattern:
@@ -478,7 +478,7 @@ class CompilationConfig:
478
range(256, max_cudagraph_capture_size + 1, 16))
479
480
If not specified, max_cudagraph_capture_size is set to min(max_num_seqs*2,
481
- 512) by default. This voids OOM in tight memory scenarios with small
481
+ 512) by default. This voids OOM in tight memory scenarios with small
482
max_num_seqs, and prevents capture of many large graphs (>512) that would
483
greatly increase startup time with limited performance benefit.
484
"""
@@ -579,6 +579,27 @@ class CompilationConfig:
579
580
__str__ = __repr__
581
582
+ @field_validator("mode", mode="before")
583
+ @classmethod
584
+ def validate_mode_before(cls, value: Any) -> Any:
585
+ """
586
+ Enable parsing the `mode` field from string mode names.
587
+ Accepts both integers (0-3) and string names, like NONE, STOCK_TORCH_COMPILE,
588
+ DYNAMO_TRACE_ONCE, VLLM_COMPILE.
589
+ """
590
+ if isinstance(value, str):
591
+ # Convert string mode name to integer value
592
+ mode_name = value.upper()
593
+
594
+ if mode_name not in CompilationMode.__members__:
595
+ raise ValueError(
596
+ f"Invalid compilation mode: {value}. "
597
+ f"Valid modes are: {', '.join(CompilationMode.__members__.keys())}"
598
+ )
599
+
600
+ return CompilationMode[mode_name]
601
+ return value
602
+
603
@field_validator("cudagraph_mode", mode="before")
604
@classmethod
605
def validate_cudagraph_mode_before(cls, value: Any) -> Any:
@@ -904,7 +925,7 @@ class CompilationConfig:
925
return self.mode == CompilationMode.VLLM_COMPILE
926
927
# Inductor partition case
907
- return self.backend == "inductor" and self.mode > CompilationMode.NONE
928
+ return self.backend == "inductor" and self.mode != CompilationMode.NONE
929
930
def custom_op_log_check(self):
931
"""
vllm/config/vllm.py
+1
-4
@@ -422,16 +422,13 @@ class VllmConfig:
422
self.compilation_config.mode = CompilationMode.VLLM_COMPILE
423
else:
424
self.compilation_config.mode = CompilationMode.NONE
425
- else:
426
- assert self.compilation_config.mode >= CompilationMode.NONE
427
- assert self.compilation_config.mode <= CompilationMode.VLLM_COMPILE
425
426
# If user does not set custom ops via none or all set it here based on
427
# compilation mode and backend.
428
if all(s not in self.compilation_config.custom_ops for s in ("all", "none")):
429
if (
430
self.compilation_config.backend == "inductor"
434
- and self.compilation_config.mode > CompilationMode.NONE
431
+ and self.compilation_config.mode != CompilationMode.NONE
432
):
433
self.compilation_config.custom_ops.append("none")
434
else:
vllm/entrypoints/llm.py
+4
-1
@@ -23,6 +23,7 @@ from vllm.config import (
23
StructuredOutputsConfig,
24
is_init_field,
25
)
26
+from vllm.config.compilation import CompilationMode
27
from vllm.config.model import (
28
ConvertOption,
29
HfOverrides,
@@ -259,7 +260,9 @@ class LLM:
260
261
if compilation_config is not None:
262
if isinstance(compilation_config, int):
262
- compilation_config_instance = CompilationConfig(mode=compilation_config)
263
+ compilation_config_instance = CompilationConfig(
264
+ mode=CompilationMode(compilation_config)
265
+ )
266
elif isinstance(compilation_config, dict):
267
compilation_config_instance = CompilationConfig(
268
**{