[Feature] Coordinate FullCG prefill across DP-attention ranks (#35640)
Co-authored-by: Yuwei An <ayw.sirius19@gmail.com>
This commit is contained in:
@@ -736,8 +736,8 @@ class TboForwardBatchPreparer:
|
|||||||
"forward_mode",
|
"forward_mode",
|
||||||
"is_extend_in_batch",
|
"is_extend_in_batch",
|
||||||
"return_logprob",
|
"return_logprob",
|
||||||
"can_run_dp_cuda_graph",
|
"can_run_decode_cuda_graph",
|
||||||
"can_run_dp_breakable_cuda_graph",
|
"can_run_dp_prefill_cuda_graph",
|
||||||
"dp_padding_mode",
|
"dp_padding_mode",
|
||||||
"global_forward_mode",
|
"global_forward_mode",
|
||||||
"is_prefill_only",
|
"is_prefill_only",
|
||||||
|
|||||||
@@ -2161,8 +2161,8 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
|
|||||||
|
|
||||||
# For DP attention
|
# For DP attention
|
||||||
is_extend_in_batch: bool = False
|
is_extend_in_batch: bool = False
|
||||||
can_run_dp_cuda_graph: bool = False
|
can_run_decode_cuda_graph: bool = False
|
||||||
can_run_dp_breakable_cuda_graph: bool = False
|
can_run_dp_prefill_cuda_graph: bool = False
|
||||||
tbo_split_seq_index: Optional[int] = None
|
tbo_split_seq_index: Optional[int] = None
|
||||||
# Rank-consistent forward mode for the recv skipper, derived from the MLP
|
# Rank-consistent forward mode for the recv skipper, derived from the MLP
|
||||||
# sync all-gather (the TBO-only `global_forward_mode` is None without TBO).
|
# sync all-gather (the TBO-only `global_forward_mode` is None without TBO).
|
||||||
@@ -3355,8 +3355,8 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
|
|||||||
spec_info=self.spec_info,
|
spec_info=self.spec_info,
|
||||||
global_num_tokens=self.global_num_tokens,
|
global_num_tokens=self.global_num_tokens,
|
||||||
global_num_tokens_for_logprob=self.global_num_tokens_for_logprob,
|
global_num_tokens_for_logprob=self.global_num_tokens_for_logprob,
|
||||||
can_run_dp_cuda_graph=self.can_run_dp_cuda_graph,
|
can_run_decode_cuda_graph=self.can_run_decode_cuda_graph,
|
||||||
can_run_dp_breakable_cuda_graph=self.can_run_dp_breakable_cuda_graph,
|
can_run_dp_prefill_cuda_graph=self.can_run_dp_prefill_cuda_graph,
|
||||||
is_extend_in_batch=self.is_extend_in_batch,
|
is_extend_in_batch=self.is_extend_in_batch,
|
||||||
is_prefill_only=self.is_prefill_only,
|
is_prefill_only=self.is_prefill_only,
|
||||||
seq_lens_cpu=self.seq_lens_cpu,
|
seq_lens_cpu=self.seq_lens_cpu,
|
||||||
|
|||||||
@@ -221,8 +221,8 @@ def _update_gather_batch(
|
|||||||
batch.global_forward_mode = mlp_sync_info.global_forward_mode
|
batch.global_forward_mode = mlp_sync_info.global_forward_mode
|
||||||
|
|
||||||
# Check forward mode for cuda graph
|
# Check forward mode for cuda graph
|
||||||
batch.can_run_dp_cuda_graph = mlp_sync_info.can_run_decode_cuda_graph
|
batch.can_run_decode_cuda_graph = mlp_sync_info.can_run_decode_cuda_graph
|
||||||
batch.can_run_dp_breakable_cuda_graph = mlp_sync_info.can_run_prefill_cuda_graph
|
batch.can_run_dp_prefill_cuda_graph = mlp_sync_info.can_run_prefill_cuda_graph
|
||||||
|
|
||||||
|
|
||||||
def prepare_mlp_sync_batch_raw(
|
def prepare_mlp_sync_batch_raw(
|
||||||
@@ -271,14 +271,15 @@ def prepare_mlp_sync_batch_raw(
|
|||||||
or local_batch.forward_mode.is_decode_or_idle()
|
or local_batch.forward_mode.is_decode_or_idle()
|
||||||
or local_batch.forward_mode.is_prebuilt()
|
or local_batch.forward_mode.is_prebuilt()
|
||||||
) and not disable_cuda_graph
|
) and not disable_cuda_graph
|
||||||
breakable_prefill = check_cuda_graph_backend(Phase.PREFILL, Backend.BREAKABLE)
|
coordinated_prefill = check_cuda_graph_backend(
|
||||||
|
Phase.PREFILL, Backend.BREAKABLE
|
||||||
|
) or check_cuda_graph_backend(Phase.PREFILL, Backend.FULL)
|
||||||
prefill_graph_runner = (
|
prefill_graph_runner = (
|
||||||
model_runner.prefill_cuda_graph_runner if breakable_prefill else None
|
model_runner.prefill_cuda_graph_runner if coordinated_prefill else None
|
||||||
)
|
)
|
||||||
can_run_prefill_cuda_graph = (
|
can_run_prefill_cuda_graph = (
|
||||||
local_batch is None
|
local_batch is None
|
||||||
or local_batch.forward_mode.is_idle()
|
or local_batch.forward_mode.is_idle()
|
||||||
# Breakable Cuda Graph Backend Check.
|
|
||||||
or (
|
or (
|
||||||
local_batch.forward_mode in (ForwardMode.EXTEND, ForwardMode.MIXED)
|
local_batch.forward_mode in (ForwardMode.EXTEND, ForwardMode.MIXED)
|
||||||
and (
|
and (
|
||||||
@@ -287,7 +288,7 @@ def prepare_mlp_sync_batch_raw(
|
|||||||
batch_size=local_batch.batch_size(),
|
batch_size=local_batch.batch_size(),
|
||||||
num_tokens=local_batch.extend_num_tokens,
|
num_tokens=local_batch.extend_num_tokens,
|
||||||
input_embeds=local_batch.input_embeds,
|
input_embeds=local_batch.input_embeds,
|
||||||
replace_embeds=None,
|
replace_embeds=local_batch.replace_embeds,
|
||||||
prefix_lens=local_batch.prefix_lens,
|
prefix_lens=local_batch.prefix_lens,
|
||||||
is_target_verify=local_batch.forward_mode.is_target_verify(),
|
is_target_verify=local_batch.forward_mode.is_target_verify(),
|
||||||
capture_hidden_mode=None,
|
capture_hidden_mode=None,
|
||||||
@@ -295,7 +296,7 @@ def prepare_mlp_sync_batch_raw(
|
|||||||
lora_ineligible=prefill_graph_runner.enable_lora,
|
lora_ineligible=prefill_graph_runner.enable_lora,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
and breakable_prefill
|
and coordinated_prefill
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -446,8 +446,8 @@ class ForwardBatch(ForwardBatchDeepSeekMHAMixin):
|
|||||||
|
|
||||||
# For DP attention
|
# For DP attention
|
||||||
is_extend_in_batch: bool = False
|
is_extend_in_batch: bool = False
|
||||||
can_run_dp_cuda_graph: bool = False
|
can_run_decode_cuda_graph: bool = False
|
||||||
can_run_dp_breakable_cuda_graph: bool = False
|
can_run_dp_prefill_cuda_graph: bool = False
|
||||||
global_forward_mode: Optional[ForwardMode] = None
|
global_forward_mode: Optional[ForwardMode] = None
|
||||||
|
|
||||||
# For two-batch overlap
|
# For two-batch overlap
|
||||||
@@ -703,7 +703,7 @@ class ForwardBatch(ForwardBatchDeepSeekMHAMixin):
|
|||||||
self.global_num_tokens_for_logprob_gpu = torch.tensor(
|
self.global_num_tokens_for_logprob_gpu = torch.tensor(
|
||||||
global_num_tokens_for_logprob, dtype=torch.int64
|
global_num_tokens_for_logprob, dtype=torch.int64
|
||||||
).to(device, non_blocking=True)
|
).to(device, non_blocking=True)
|
||||||
self.can_run_dp_cuda_graph = batch.can_run_dp_cuda_graph
|
self.can_run_decode_cuda_graph = batch.can_run_decode_cuda_graph
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init_new(
|
def init_new(
|
||||||
@@ -788,8 +788,8 @@ class ForwardBatch(ForwardBatchDeepSeekMHAMixin):
|
|||||||
# Scalar config / flags
|
# Scalar config / flags
|
||||||
return_logprob=batch.return_logprob,
|
return_logprob=batch.return_logprob,
|
||||||
is_extend_in_batch=batch.is_extend_in_batch,
|
is_extend_in_batch=batch.is_extend_in_batch,
|
||||||
can_run_dp_cuda_graph=batch.can_run_dp_cuda_graph,
|
can_run_decode_cuda_graph=batch.can_run_decode_cuda_graph,
|
||||||
can_run_dp_breakable_cuda_graph=batch.can_run_dp_breakable_cuda_graph,
|
can_run_dp_prefill_cuda_graph=batch.can_run_dp_prefill_cuda_graph,
|
||||||
global_forward_mode=batch.global_forward_mode,
|
global_forward_mode=batch.global_forward_mode,
|
||||||
is_prefill_only=batch.is_prefill_only,
|
is_prefill_only=batch.is_prefill_only,
|
||||||
spec_algorithm=batch.spec_algorithm,
|
spec_algorithm=batch.spec_algorithm,
|
||||||
@@ -1312,21 +1312,20 @@ class ForwardBatch(ForwardBatchDeepSeekMHAMixin):
|
|||||||
):
|
):
|
||||||
# Joined ranks require real token counts instead of MAX_LEN padding.
|
# Joined ranks require real token counts instead of MAX_LEN padding.
|
||||||
dp_padding_mode = DpPaddingMode.SUM_LEN
|
dp_padding_mode = DpPaddingMode.SUM_LEN
|
||||||
# Prefill breakable CUDA graph requires every DP rank to run the SAME
|
# Prefill CUDA graphs require every DP rank to run the same captured
|
||||||
# captured shape. Under SUM_LEN each rank pads to its own local token
|
# shape. Under SUM_LEN each rank pads to its own local token
|
||||||
# count and can select a different capture bucket. This mismatches the
|
# count and can select a different capture bucket. This mismatches the
|
||||||
# rank-coupled communication geometry: DP gather/combine uses
|
# rank-coupled communication geometry: DP gather/combine uses
|
||||||
# all_gather_into_tensor / reduce_scatter_tensor, while MoE backends may
|
# all_gather_into_tensor / reduce_scatter_tensor, while MoE backends may
|
||||||
# use A2A dispatch/combine. Force MAX_LEN so every rank pads to the global
|
# use A2A dispatch/combine. Force MAX_LEN so every rank pads to the global
|
||||||
# max and picks the same bucket.
|
# max and picks the same bucket.
|
||||||
#
|
#
|
||||||
# Only force MAX_LEN when the batch fits a captured breakable prefill
|
# Larger prefills fall back to eager and keep the memory-efficient
|
||||||
# graph; larger prefills fall back to eager and keep the
|
# SUM_LEN. global_num_tokens is identical across ranks (all-gathered),
|
||||||
# memory-efficient SUM_LEN. global_num_tokens is identical across ranks
|
# so the decision is consistent cluster-wide.
|
||||||
# (all-gathered), so the decision is consistent cluster-wide.
|
|
||||||
prefill_cg = get_exec().graph.cuda_graph_config.prefill
|
prefill_cg = get_exec().graph.cuda_graph_config.prefill
|
||||||
if (
|
if (
|
||||||
self.can_run_dp_breakable_cuda_graph
|
self.can_run_dp_prefill_cuda_graph
|
||||||
and self.is_extend_in_batch
|
and self.is_extend_in_batch
|
||||||
and prefill_cg.bs
|
and prefill_cg.bs
|
||||||
and max(global_num_tokens) <= max(prefill_cg.bs)
|
and max(global_num_tokens) <= max(prefill_cg.bs)
|
||||||
|
|||||||
@@ -694,7 +694,9 @@ class DecodeCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.require_mlp_sync:
|
if self.require_mlp_sync:
|
||||||
is_bs_supported = is_bs_supported and forward_batch.can_run_dp_cuda_graph
|
is_bs_supported = (
|
||||||
|
is_bs_supported and forward_batch.can_run_decode_cuda_graph
|
||||||
|
)
|
||||||
|
|
||||||
# NOTE: cuda graph cannot handle mixed batch (encoder_len = 0)
|
# NOTE: cuda graph cannot handle mixed batch (encoder_len = 0)
|
||||||
# If mixed batch cannot be supported, then encoder_lens can be removed in cuda graph
|
# If mixed batch cannot be supported, then encoder_lens can be removed in cuda graph
|
||||||
@@ -735,7 +737,7 @@ class DecodeCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
] and forward_batch.batch_size <= self._ragged_capture_slots(admission_tokens)
|
] and forward_batch.batch_size <= self._ragged_capture_slots(admission_tokens)
|
||||||
|
|
||||||
is_dp_supported = (
|
is_dp_supported = (
|
||||||
forward_batch.can_run_dp_cuda_graph if self.require_mlp_sync else True
|
forward_batch.can_run_decode_cuda_graph if self.require_mlp_sync else True
|
||||||
)
|
)
|
||||||
|
|
||||||
is_encoder_lens_supported = (
|
is_encoder_lens_supported = (
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ from __future__ import annotations
|
|||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
from collections.abc import Sequence
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||||
@@ -826,19 +827,29 @@ class PrefillCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
return prefix_chunk_len, prefix_chunk_len * capture_req_slots
|
return prefix_chunk_len, prefix_chunk_len * capture_req_slots
|
||||||
|
|
||||||
def _select_prefix_capture_chunks(
|
def _select_prefix_capture_chunks(
|
||||||
self, forward_batch: ForwardBatch
|
self, prefix_lens: Sequence[int]
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
"""Smallest captured variant covering the batch's max prefix, or None."""
|
"""Smallest captured variant covering the batch's max prefix, or None."""
|
||||||
max_prefix_len = max(
|
max_prefix_len = max(int(length) for length in prefix_lens)
|
||||||
int(length) for length in forward_batch.extend_prefix_lens_cpu
|
|
||||||
)
|
|
||||||
real_n = _ceil_div(max_prefix_len, self._prefix_chunk_len)
|
real_n = _ceil_div(max_prefix_len, self._prefix_chunk_len)
|
||||||
return next((n for n in self._prefix_capture_variants if n >= real_n), None)
|
return next((n for n in self._prefix_capture_variants if n >= real_n), None)
|
||||||
|
|
||||||
|
def _has_uncapturable_chunked_prefix(
|
||||||
|
self, prefix_lens: Sequence[int] | None
|
||||||
|
) -> bool:
|
||||||
|
return (
|
||||||
|
self._capture_chunked_prefix
|
||||||
|
and prefix_lens is not None
|
||||||
|
and any(int(length) > 0 for length in prefix_lens)
|
||||||
|
and self._select_prefix_capture_chunks(prefix_lens) is None
|
||||||
|
)
|
||||||
|
|
||||||
def _shape_key(self, num_tokens: int, forward_batch: ForwardBatch) -> ShapeKey:
|
def _shape_key(self, num_tokens: int, forward_batch: ForwardBatch) -> ShapeKey:
|
||||||
variant = None
|
variant = None
|
||||||
if self._capture_chunked_prefix and self._has_prefix_hit(forward_batch):
|
if self._capture_chunked_prefix and self._has_prefix_hit(forward_batch):
|
||||||
captured_n = self._select_prefix_capture_chunks(forward_batch)
|
captured_n = self._select_prefix_capture_chunks(
|
||||||
|
forward_batch.extend_prefix_lens_cpu
|
||||||
|
)
|
||||||
assert captured_n is not None, "prefix batch has no captured FullCG variant"
|
assert captured_n is not None, "prefix batch has no captured FullCG variant"
|
||||||
variant = _chunked_prefix_variant(captured_n)
|
variant = _chunked_prefix_variant(captured_n)
|
||||||
return ShapeKey(size=num_tokens, variant_label=variant)
|
return ShapeKey(size=num_tokens, variant_label=variant)
|
||||||
@@ -1055,7 +1066,6 @@ class PrefillCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
capture_hidden_mode,
|
capture_hidden_mode,
|
||||||
return_logprob: bool,
|
return_logprob: bool,
|
||||||
lora_ineligible: bool = False,
|
lora_ineligible: bool = False,
|
||||||
chunked_prefix_uncapturable: bool = False,
|
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Rank-local replay eligibility: the single source of truth for
|
"""Rank-local replay eligibility: the single source of truth for
|
||||||
``can_run_graph`` (ForwardBatch, forward time) and the dp mlp-sync
|
``can_run_graph`` (ForwardBatch, forward time) and the dp mlp-sync
|
||||||
@@ -1084,10 +1094,9 @@ class PrefillCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
and any(prefix_lens)
|
and any(prefix_lens)
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
# FullCG's chunked-prefix topology covers a bounded prefix. The flag
|
# FullCG's chunked-prefix topology covers a bounded prefix. Its capture
|
||||||
# gating it is FULL-backend-only, so this is inert for the breakable
|
# flag is FullCG-only, so this is inert for the BreakableCG vote path.
|
||||||
# vote path.
|
if self._has_uncapturable_chunked_prefix(prefix_lens):
|
||||||
if chunked_prefix_uncapturable:
|
|
||||||
return False
|
return False
|
||||||
# tc_piecewise captures with ForwardMode.EXTEND and spec_info=None.
|
# tc_piecewise captures with ForwardMode.EXTEND and spec_info=None.
|
||||||
if is_target_verify:
|
if is_target_verify:
|
||||||
@@ -1115,7 +1124,7 @@ class PrefillCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
# (min-reduced votes; also requires every rank to hold tokens).
|
# (min-reduced votes; also requires every rank to hold tokens).
|
||||||
if (
|
if (
|
||||||
forward_batch.global_num_tokens_cpu is not None
|
forward_batch.global_num_tokens_cpu is not None
|
||||||
and not forward_batch.can_run_dp_breakable_cuda_graph
|
and not forward_batch.can_run_dp_prefill_cuda_graph
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1141,11 +1150,6 @@ class PrefillCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
forward_batch
|
forward_batch
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
chunked_prefix_uncapturable=(
|
|
||||||
self._capture_chunked_prefix
|
|
||||||
and self._has_prefix_hit(forward_batch)
|
|
||||||
and self._select_prefix_capture_chunks(forward_batch) is None
|
|
||||||
),
|
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
if getattr(self, "enable_cp_v2_bcg_capture", False) and is_cp_v2_active(
|
if getattr(self, "enable_cp_v2_bcg_capture", False) and is_cp_v2_active(
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ class DraftBlockProposer:
|
|||||||
) -> None:
|
) -> None:
|
||||||
# The dense DSpark draft still reuses the target batch's graph tier.
|
# The dense DSpark draft still reuses the target batch's graph tier.
|
||||||
# Set graph eligibility before the DP-MoE-only metadata early return.
|
# Set graph eligibility before the DP-MoE-only metadata early return.
|
||||||
forward_batch.can_run_dp_cuda_graph = batch.can_run_dp_cuda_graph
|
forward_batch.can_run_decode_cuda_graph = batch.can_run_decode_cuda_graph
|
||||||
if not self._dp_moe_sync or batch.global_num_tokens is None:
|
if not self._dp_moe_sync or batch.global_num_tokens is None:
|
||||||
return
|
return
|
||||||
# Graph bucket selection uses the raw per-rank request counts. Keep
|
# Graph bucket selection uses the raw per-rank request counts. Keep
|
||||||
|
|||||||
@@ -318,7 +318,9 @@ class EAGLEDraftCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.require_mlp_sync:
|
if self.require_mlp_sync:
|
||||||
is_bs_supported = is_bs_supported and forward_batch.can_run_dp_cuda_graph
|
is_bs_supported = (
|
||||||
|
is_bs_supported and forward_batch.can_run_decode_cuda_graph
|
||||||
|
)
|
||||||
|
|
||||||
return is_bs_supported
|
return is_bs_supported
|
||||||
|
|
||||||
|
|||||||
@@ -309,7 +309,9 @@ class EAGLEDraftExtendCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.require_mlp_sync:
|
if self.require_mlp_sync:
|
||||||
is_bs_supported = is_bs_supported and forward_batch.can_run_dp_cuda_graph
|
is_bs_supported = (
|
||||||
|
is_bs_supported and forward_batch.can_run_decode_cuda_graph
|
||||||
|
)
|
||||||
|
|
||||||
return is_bs_supported
|
return is_bs_supported
|
||||||
|
|
||||||
|
|||||||
@@ -237,7 +237,9 @@ class FrozenKVMTPCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
else cuda_graph_bs <= self.max_bs
|
else cuda_graph_bs <= self.max_bs
|
||||||
)
|
)
|
||||||
if self.require_mlp_sync:
|
if self.require_mlp_sync:
|
||||||
is_bs_supported = is_bs_supported and forward_batch.can_run_dp_cuda_graph
|
is_bs_supported = (
|
||||||
|
is_bs_supported and forward_batch.can_run_decode_cuda_graph
|
||||||
|
)
|
||||||
return is_bs_supported
|
return is_bs_supported
|
||||||
|
|
||||||
def capture_one_shape(
|
def capture_one_shape(
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ class FrozenKVMTPDraftWorker(EagleDraftWorkerBase, TpModelWorker):
|
|||||||
self.cuda_graph_runner.execute(forward_batch)
|
self.cuda_graph_runner.execute(forward_batch)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
forward_batch.can_run_dp_cuda_graph = False
|
forward_batch.can_run_decode_cuda_graph = False
|
||||||
parent_list, top_scores_index, draft_tokens = self.draft_forward(
|
parent_list, top_scores_index, draft_tokens = self.draft_forward(
|
||||||
forward_batch
|
forward_batch
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -255,7 +255,9 @@ class MultiLayerEagleDraftExtendCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.require_mlp_sync:
|
if self.require_mlp_sync:
|
||||||
is_bs_supported = is_bs_supported and forward_batch.can_run_dp_cuda_graph
|
is_bs_supported = (
|
||||||
|
is_bs_supported and forward_batch.can_run_decode_cuda_graph
|
||||||
|
)
|
||||||
|
|
||||||
return is_bs_supported
|
return is_bs_supported
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -23,7 +28,12 @@ from sglang.test.test_utils import (
|
|||||||
popen_launch_server,
|
popen_launch_server,
|
||||||
)
|
)
|
||||||
|
|
||||||
register_cuda_ci(est_time=160, stage="base-b", runner_config="2-gpu-large")
|
register_cuda_ci(est_time=320, stage="base-b", runner_config="2-gpu-large")
|
||||||
|
|
||||||
|
PREFILL_GRAPH_REPLAY_PATTERN = re.compile(r"Prefill batch.*cuda graph: True")
|
||||||
|
CACHED_PREFIX_GRAPH_REPLAY_PATTERN = re.compile(
|
||||||
|
r"Prefill batch.*#cached-token: [1-9][0-9]*.*cuda graph: True"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -185,10 +195,11 @@ def _select_attention_backend():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestDPAttentionBreakablePrefillCudaGraphKL(CustomTestCase):
|
class _DPAttentionPrefillCudaGraphKLMixin:
|
||||||
num_samples = 48
|
num_samples = 48
|
||||||
max_prompt_tokens = 1024
|
max_prompt_tokens = 1024
|
||||||
max_new_tokens = 256
|
max_new_tokens = 256
|
||||||
|
prefill_backend: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -196,6 +207,11 @@ class TestDPAttentionBreakablePrefillCudaGraphKL(CustomTestCase):
|
|||||||
cls.model = DEFAULT_TARGET_MODEL_EAGLE_DP_ATTN
|
cls.model = DEFAULT_TARGET_MODEL_EAGLE_DP_ATTN
|
||||||
cls.base_url = DEFAULT_URL_FOR_TEST
|
cls.base_url = DEFAULT_URL_FOR_TEST
|
||||||
cls.attention_backend = _select_attention_backend()
|
cls.attention_backend = _select_attention_backend()
|
||||||
|
cls.log_dir = tempfile.mkdtemp(prefix=f"dp_attn_{cls.prefill_backend}_")
|
||||||
|
cls.stdout_path = os.path.join(cls.log_dir, "server.out")
|
||||||
|
cls.stderr_path = os.path.join(cls.log_dir, "server.err")
|
||||||
|
cls.stdout = open(cls.stdout_path, "w")
|
||||||
|
cls.stderr = open(cls.stderr_path, "w")
|
||||||
cls.process = popen_launch_server(
|
cls.process = popen_launch_server(
|
||||||
cls.model,
|
cls.model,
|
||||||
cls.base_url,
|
cls.base_url,
|
||||||
@@ -212,12 +228,15 @@ class TestDPAttentionBreakablePrefillCudaGraphKL(CustomTestCase):
|
|||||||
cls.attention_backend,
|
cls.attention_backend,
|
||||||
"--moe-runner-backend",
|
"--moe-runner-backend",
|
||||||
"triton",
|
"triton",
|
||||||
"--cuda-graph-backend-prefill=breakable",
|
f"--cuda-graph-backend-prefill={cls.prefill_backend}",
|
||||||
"--chunked-prefill-size",
|
"--chunked-prefill-size",
|
||||||
"2048",
|
"2048",
|
||||||
|
"--prefill-max-requests",
|
||||||
|
"2",
|
||||||
"--mem-fraction-static",
|
"--mem-fraction-static",
|
||||||
"0.70",
|
"0.70",
|
||||||
],
|
],
|
||||||
|
return_stdout_stderr=(cls.stdout, cls.stderr),
|
||||||
)
|
)
|
||||||
|
|
||||||
server_info = requests.get(f"{cls.base_url}/server_info", timeout=30).json()
|
server_info = requests.get(f"{cls.base_url}/server_info", timeout=30).json()
|
||||||
@@ -235,6 +254,36 @@ class TestDPAttentionBreakablePrefillCudaGraphKL(CustomTestCase):
|
|||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
if hasattr(cls, "process") and cls.process:
|
if hasattr(cls, "process") and cls.process:
|
||||||
kill_process_tree(cls.process.pid)
|
kill_process_tree(cls.process.pid)
|
||||||
|
for attr in ("stdout", "stderr"):
|
||||||
|
output = getattr(cls, attr, None)
|
||||||
|
if output is not None and not output.closed:
|
||||||
|
output.close()
|
||||||
|
if hasattr(cls, "log_dir"):
|
||||||
|
shutil.rmtree(cls.log_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
def _wait_for_prefill_graph_replay(
|
||||||
|
self,
|
||||||
|
offsets,
|
||||||
|
pattern=PREFILL_GRAPH_REPLAY_PATTERN,
|
||||||
|
case="a lone request",
|
||||||
|
):
|
||||||
|
deadline = time.monotonic() + 30
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
chunks = []
|
||||||
|
for path, offset in zip(
|
||||||
|
(self.stdout_path, self.stderr_path), offsets, strict=True
|
||||||
|
):
|
||||||
|
with open(path, "rb") as log:
|
||||||
|
log.seek(offset)
|
||||||
|
chunks.append(log.read().decode(errors="replace"))
|
||||||
|
logs = "\n".join(chunks)
|
||||||
|
if pattern.search(logs):
|
||||||
|
return
|
||||||
|
time.sleep(0.5)
|
||||||
|
self.fail(
|
||||||
|
f"No {self.prefill_backend} prefill CUDA graph replay was logged "
|
||||||
|
f"for {case}"
|
||||||
|
)
|
||||||
|
|
||||||
def test_prefill_and_decode_cache_hit_kl_is_zero(self):
|
def test_prefill_and_decode_cache_hit_kl_is_zero(self):
|
||||||
server_info = requests.get(self.base_url + "/server_info", timeout=30).json()
|
server_info = requests.get(self.base_url + "/server_info", timeout=30).json()
|
||||||
@@ -243,15 +292,24 @@ class TestDPAttentionBreakablePrefillCudaGraphKL(CustomTestCase):
|
|||||||
self.assertTrue(server_info["enable_deterministic_inference"])
|
self.assertTrue(server_info["enable_deterministic_inference"])
|
||||||
self.assertEqual(server_info["attention_backend"], self.attention_backend)
|
self.assertEqual(server_info["attention_backend"], self.attention_backend)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
server_info["cuda_graph_config"]["prefill"]["backend"], "breakable"
|
server_info["cuda_graph_config"]["prefill"]["backend"],
|
||||||
|
self.prefill_backend,
|
||||||
)
|
)
|
||||||
|
|
||||||
print("=== Radix Cache KL Divergence Eval ===")
|
print("=== Radix Cache KL Divergence Eval ===")
|
||||||
print(f"Server: {self.base_url} Samples: {self.num_samples}\n")
|
print(f"Server: {self.base_url} Samples: {self.num_samples}\n")
|
||||||
|
|
||||||
|
offsets = [
|
||||||
|
os.path.getsize(path) for path in (self.stdout_path, self.stderr_path)
|
||||||
|
]
|
||||||
prefill_kl = test_prefill_cache_hit(
|
prefill_kl = test_prefill_cache_hit(
|
||||||
self.base_url, self.input_ids, self.max_new_tokens
|
self.base_url, self.input_ids, self.max_new_tokens
|
||||||
)
|
)
|
||||||
|
self._wait_for_prefill_graph_replay(
|
||||||
|
offsets,
|
||||||
|
CACHED_PREFIX_GRAPH_REPLAY_PATTERN,
|
||||||
|
"a cached-prefix request",
|
||||||
|
)
|
||||||
decode_kl = test_decode_cache_hit(
|
decode_kl = test_decode_cache_hit(
|
||||||
self.base_url, self.input_ids, self.max_new_tokens
|
self.base_url, self.input_ids, self.max_new_tokens
|
||||||
)
|
)
|
||||||
@@ -259,6 +317,32 @@ class TestDPAttentionBreakablePrefillCudaGraphKL(CustomTestCase):
|
|||||||
self.assertEqual(prefill_kl, 0.0)
|
self.assertEqual(prefill_kl, 0.0)
|
||||||
self.assertEqual(decode_kl, 0.0)
|
self.assertEqual(decode_kl, 0.0)
|
||||||
|
|
||||||
|
def test_lone_request_replays_prefill_cuda_graph(self):
|
||||||
|
_flush_cache(self.base_url)
|
||||||
|
offsets = [
|
||||||
|
os.path.getsize(path) for path in (self.stdout_path, self.stderr_path)
|
||||||
|
]
|
||||||
|
result = _generate(
|
||||||
|
self.base_url,
|
||||||
|
self.input_ids[0],
|
||||||
|
max_new_tokens=1,
|
||||||
|
return_logprob=True,
|
||||||
|
)
|
||||||
|
self.assertNotIn("error", result)
|
||||||
|
self._wait_for_prefill_graph_replay(offsets)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDPAttentionBreakablePrefillCudaGraphKL(
|
||||||
|
_DPAttentionPrefillCudaGraphKLMixin, CustomTestCase
|
||||||
|
):
|
||||||
|
prefill_backend = "breakable"
|
||||||
|
|
||||||
|
|
||||||
|
class TestDPAttentionFullPrefillCudaGraphKL(
|
||||||
|
_DPAttentionPrefillCudaGraphKLMixin, CustomTestCase
|
||||||
|
):
|
||||||
|
prefill_backend = "full"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class TestDraftDpSyncMetadata(CustomTestCase):
|
|||||||
batch = SimpleNamespace(
|
batch = SimpleNamespace(
|
||||||
global_num_tokens=[1, 3, 0, 2],
|
global_num_tokens=[1, 3, 0, 2],
|
||||||
global_num_tokens_for_logprob=[1, 3, 0, 2],
|
global_num_tokens_for_logprob=[1, 3, 0, 2],
|
||||||
can_run_dp_cuda_graph=True,
|
can_run_decode_cuda_graph=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@@ -84,7 +84,7 @@ class TestDraftDpSyncMetadata(CustomTestCase):
|
|||||||
self.assertEqual(forward_batch.num_token_non_padded.item(), 6)
|
self.assertEqual(forward_batch.num_token_non_padded.item(), 6)
|
||||||
self.assertEqual(forward_batch.num_token_non_padded.dtype, torch.int32)
|
self.assertEqual(forward_batch.num_token_non_padded.dtype, torch.int32)
|
||||||
self.assertEqual(forward_batch.num_token_non_padded_cpu, 6)
|
self.assertEqual(forward_batch.num_token_non_padded_cpu, 6)
|
||||||
self.assertTrue(forward_batch.can_run_dp_cuda_graph)
|
self.assertTrue(forward_batch.can_run_decode_cuda_graph)
|
||||||
|
|
||||||
|
|
||||||
class TestBusyIdleGraphKeyIdentity(CustomTestCase):
|
class TestBusyIdleGraphKeyIdentity(CustomTestCase):
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class TestMlpSyncPadUnpad(CustomTestCase):
|
|||||||
batch = SimpleNamespace(
|
batch = SimpleNamespace(
|
||||||
global_num_tokens=[2, 0, 3],
|
global_num_tokens=[2, 0, 3],
|
||||||
global_num_tokens_for_logprob=[2, 0, 3],
|
global_num_tokens_for_logprob=[2, 0, 3],
|
||||||
can_run_dp_cuda_graph=True,
|
can_run_decode_cuda_graph=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
fb.init_mlp_sync_metadata(batch, torch.device("cpu"))
|
fb.init_mlp_sync_metadata(batch, torch.device("cpu"))
|
||||||
@@ -71,7 +71,7 @@ class TestMlpSyncPadUnpad(CustomTestCase):
|
|||||||
torch.testing.assert_close(
|
torch.testing.assert_close(
|
||||||
fb.global_num_tokens_for_logprob_gpu, torch.tensor([4, 0, 6])
|
fb.global_num_tokens_for_logprob_gpu, torch.tensor([4, 0, 6])
|
||||||
)
|
)
|
||||||
self.assertTrue(fb.can_run_dp_cuda_graph)
|
self.assertTrue(fb.can_run_decode_cuda_graph)
|
||||||
|
|
||||||
def test_draft_input_without_hidden_states_can_be_padded(self):
|
def test_draft_input_without_hidden_states_can_be_padded(self):
|
||||||
spec_info = SimpleNamespace(
|
spec_info = SimpleNamespace(
|
||||||
|
|||||||
Reference in New Issue
Block a user