[diffusion] chore: auto-enable best parallel setting if unspecified (#22763)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mick
2026-04-15 00:02:05 +08:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 3c0a6c6987
commit d2f479e544
4 changed files with 62 additions and 7 deletions
@@ -130,8 +130,8 @@ class ServerArgs:
dp_size: int = 1
# number of gpu in a dp group
dp_degree: int = 1
# cfg parallel
enable_cfg_parallel: bool = False
# cfg parallel (None = auto-decide based on num_gpus)
enable_cfg_parallel: Optional[bool] = None
hsdp_replicate_dim: int = 1
hsdp_shard_dim: Optional[int] = None
@@ -428,6 +428,7 @@ class ServerArgs:
sp_unspecified = self.sp_degree is None
ulysses_unspecified = self.ulysses_degree is None
ring_unspecified = self.ring_degree is None
cfg_unspecified = self.enable_cfg_parallel is None
if self.hsdp_shard_dim is None:
self.hsdp_shard_dim = self.num_gpus
@@ -435,6 +436,30 @@ class ServerArgs:
if self.tp_size is None:
self.tp_size = 1
# Auto-enable CFG parallel when user hasn't set any parallelism flags
# and there are enough GPUs. Only auto-enable for models whose default
# SamplingParams use classifier-free guidance (negative_prompt is not None),
# because non-CFG models (e.g. FLUX) crash when CFG parallel splits ranks.
if cfg_unspecified:
cfg_group_size = self.dp_size * self.tp_size * 2
if (
self.num_gpus >= 2
and self.num_gpus % cfg_group_size == 0
and sp_unspecified
and ulysses_unspecified
and ring_unspecified
and self._model_default_uses_cfg()
):
self.enable_cfg_parallel = True
logger.info(
"Automatically enabled CFG parallel for %d GPUs. "
"Use --sp-degree / --ulysses-degree to use sequence "
"parallelism instead.",
self.num_gpus,
)
else:
self.enable_cfg_parallel = False
# adjust sp_degree: allocate all remaining GPUs after TP and DP
if self.sp_degree is None:
num_gpus_per_group = self.dp_size * self.tp_size
@@ -466,6 +491,28 @@ class ServerArgs:
self.ring_degree = 1
logger.debug(f"Ring degree not set, using default value {self.ring_degree}")
def _model_default_uses_cfg(self) -> bool:
"""
Check whether the model uses classifier-free guidance by default.
CFG is active when *both* ``negative_prompt is not None`` and ``guidance_scale > 1``.
"""
from sglang.multimodal_gen.registry import get_model_info
model_info = get_model_info(self.model_path, self.backend, self.model_id)
if model_info is None:
return False
default_params = model_info.sampling_param_cls()
# for ltx2.3, cfg-parallel performs worse than ulysses-sp
is_ltx = "ltx" in type(default_params).__name__.lower()
if is_ltx:
return False
return (
getattr(default_params, "negative_prompt", None) is not None
and getattr(default_params, "guidance_scale", 0) > 1.0
)
@staticmethod
def _is_ltx23_model_path(model_path: str | None) -> bool:
if not model_path:
@@ -679,8 +726,8 @@ class ServerArgs:
parser.add_argument(
"--enable-cfg-parallel",
action="store_true",
default=ServerArgs.enable_cfg_parallel,
help="Enable cfg parallel.",
default=None,
help="Enable cfg parallel. Auto-enabled when num_gpus >= 2 and no SP flags are set.",
)
parser.add_argument(
"--data-parallel-size",
@@ -1655,7 +1655,8 @@
},
"expected_e2e_ms": 149864.99,
"expected_avg_denoise_ms": 3608.89,
"expected_median_denoise_ms": 2400.38
"expected_median_denoise_ms": 2400.38,
"estimated_full_test_time_s": 253.9
},
"wan2_1_t2v_14b_2gpu": {
"stages_ms": {
@@ -154,7 +154,9 @@ def diffusion_server(case: DiffusionTestCase) -> ServerContext:
# pipeline class. This avoids hard failures when a model needs a
# newer diffusers release than what is currently installed in CI.
msg = str(exc)
if "not found in diffusers" in msg or "has no attribute" in msg:
if "not found in diffusers" in msg or (
"has no attribute" in msg and "diffusers" in msg.lower()
):
pytest.skip(
f"Skipping {case.id}: required diffusers pipeline class "
f"is not available in the installed version. "
@@ -808,6 +808,7 @@ TWO_GPU_CASES_A = [
modality="video",
custom_validator="video",
num_gpus=2,
extras=["--ulysses-degree=2"],
),
T2V_sampling_params,
),
@@ -821,6 +822,7 @@ TWO_GPU_CASES_A = [
modality="video",
custom_validator="video",
num_gpus=2,
extras=["--ulysses-degree=2"],
),
DiffusionSamplingParams(
prompt=T2V_PROMPT,
@@ -912,7 +914,7 @@ TWO_GPU_CASES_A = [
model_path="Lightricks/LTX-2",
modality="video",
num_gpus=2,
extras=["--pipeline-class-name LTX2TwoStagePipeline"],
extras=["--pipeline-class-name LTX2TwoStagePipeline", "--ulysses-degree=2"],
),
T2V_sampling_params,
),
@@ -936,6 +938,7 @@ TWO_GPU_CASES_B = [
modality="video",
custom_validator="video",
num_gpus=2,
extras=["--ulysses-degree=2"],
),
TI2V_sampling_params,
),
@@ -958,6 +961,7 @@ TWO_GPU_CASES_B = [
custom_validator="video",
num_gpus=2,
lora_path="starsfriday/Wan2.1-Divine-Power-LoRA",
extras=["--ulysses-degree=2"],
),
TI2V_sampling_params,
run_lora_basic_api_check=True,
@@ -969,6 +973,7 @@ TWO_GPU_CASES_B = [
modality="video",
custom_validator="video",
num_gpus=2,
extras=["--ulysses-degree=2"],
),
TI2V_sampling_params,
),