Add out-of-tree DFlash extension points (#38740)
Co-authored-by: Yuhan Chen <yuhanc@fb.com> Co-authored-by: Xiaoyu Zhang <1182563586@qq.com>
This commit is contained in:
co-authored by
Yuhan Chen
Xiaoyu Zhang
parent
9f21fbc34b
commit
99a44c88d4
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
import torch
|
||||
import triton
|
||||
import triton.language as tl
|
||||
@@ -25,6 +27,14 @@ if _is_cpu:
|
||||
from sgl_kernel import assign_extend_cache_locs_cpu
|
||||
|
||||
|
||||
def _get_oot_speculative_cache_locs_fn() -> Callable[..., torch.Tensor] | None:
|
||||
from sglang.srt.platforms import current_platform
|
||||
|
||||
if not current_platform.is_out_of_tree():
|
||||
return None
|
||||
return current_platform.get_speculative_cache_locs_fn()
|
||||
|
||||
|
||||
@triton.jit
|
||||
def assign_draft_cache_locs_contiguous(
|
||||
req_pool_indices,
|
||||
@@ -489,6 +499,18 @@ def assign_extend_cache_locs_func(
|
||||
draft_token_num: int,
|
||||
device,
|
||||
) -> torch.Tensor:
|
||||
platform_fn = _get_oot_speculative_cache_locs_fn()
|
||||
if platform_fn is not None:
|
||||
return platform_fn(
|
||||
req_pool_indices=req_pool_indices,
|
||||
req_to_token=req_to_token,
|
||||
start_offset=start_offset,
|
||||
end_offset=end_offset,
|
||||
batch_size=batch_size,
|
||||
draft_token_num=draft_token_num,
|
||||
device=device,
|
||||
)
|
||||
|
||||
if _is_cuda or _is_hip or _is_musa or _is_xpu:
|
||||
out_cache_loc = torch.empty(
|
||||
(batch_size * draft_token_num,),
|
||||
|
||||
@@ -16,6 +16,7 @@ from sglang.srt.arg_groups.overrides import (
|
||||
resolving_view,
|
||||
run_post_process_pass,
|
||||
)
|
||||
from sglang.srt.platforms import current_platform
|
||||
from sglang.srt.runtime_context import get_platform
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -230,11 +231,17 @@ def handle_speculative_decoding(server_args: ServerArgs) -> None:
|
||||
def _handle_dflash(server_args: ServerArgs) -> None:
|
||||
cfg = resolving_view(server_args)
|
||||
|
||||
if not (
|
||||
cfg.device.startswith("cuda") or cfg.device == "npu" or cfg.device == "xpu"
|
||||
):
|
||||
algorithm = "DFLASH"
|
||||
if current_platform.is_out_of_tree():
|
||||
is_supported = current_platform.supports_speculative_algorithm(algorithm)
|
||||
else:
|
||||
is_supported = (
|
||||
cfg.device.startswith("cuda") or cfg.device == "npu" or cfg.device == "xpu"
|
||||
)
|
||||
if not is_supported:
|
||||
raise ValueError(
|
||||
"DFLASH speculative decoding only supports CUDA, NPU and XPU devices."
|
||||
f"{algorithm} speculative decoding is not supported by "
|
||||
f"{type(current_platform).__name__} on device {cfg.device!r}."
|
||||
)
|
||||
|
||||
# DFLASH + dp attention is validated on NPU only.
|
||||
@@ -759,15 +766,55 @@ def _resolve_dflash_draft_attention_backend(server_args: ServerArgs) -> None:
|
||||
cfg = resolving_view(server_args)
|
||||
|
||||
supported_draft_backends = DRAFT_ATTENTION_BACKEND_CHOICES
|
||||
# FlashInfer is CUDA-only; fall back to triton on XPU and ROCm.
|
||||
fallback_backend = (
|
||||
"triton" if (get_platform().is_xpu or get_platform().is_hip) else "flashinfer"
|
||||
)
|
||||
|
||||
def is_supported_backend(backend: str) -> bool:
|
||||
if current_platform.is_out_of_tree():
|
||||
return current_platform.supports_speculative_draft_attention_backend(
|
||||
"DFLASH", backend
|
||||
)
|
||||
return backend in supported_draft_backends
|
||||
|
||||
def get_fallback_backend() -> str:
|
||||
if current_platform.is_out_of_tree():
|
||||
try:
|
||||
fallback_backend = (
|
||||
current_platform.get_default_speculative_draft_attention_backend(
|
||||
"DFLASH"
|
||||
)
|
||||
)
|
||||
except NotImplementedError as error:
|
||||
raise ValueError(
|
||||
f"{type(current_platform).__name__} must implement "
|
||||
"get_default_speculative_draft_attention_backend() to use DFLASH."
|
||||
) from error
|
||||
if not is_supported_backend(fallback_backend):
|
||||
raise ValueError(
|
||||
f"{type(current_platform).__name__} returned unsupported DFLASH "
|
||||
f"draft attention backend {fallback_backend!r} from "
|
||||
"get_default_speculative_draft_attention_backend()."
|
||||
)
|
||||
return fallback_backend
|
||||
# FlashInfer is CUDA-only; fall back to triton on XPU and ROCm.
|
||||
return (
|
||||
"triton"
|
||||
if (get_platform().is_xpu or get_platform().is_hip)
|
||||
else "flashinfer"
|
||||
)
|
||||
|
||||
draft_backend = cfg.speculative_draft_attention_backend
|
||||
if draft_backend is None:
|
||||
draft_backend, _ = attention_backends_of(resolved_view(server_args))
|
||||
if draft_backend is None:
|
||||
draft_backend = get_fallback_backend()
|
||||
elif not is_supported_backend(draft_backend):
|
||||
fallback_backend = get_fallback_backend()
|
||||
logger.warning(
|
||||
"DFLASH draft worker does not support attention_backend %r on %s. "
|
||||
"Falling back to '%s'.",
|
||||
draft_backend,
|
||||
type(current_platform).__name__,
|
||||
fallback_backend,
|
||||
)
|
||||
draft_backend = fallback_backend
|
||||
elif draft_backend == "trtllm_mha":
|
||||
from sglang.srt.speculative.dflash_utils import get_dflash_layer_types
|
||||
@@ -791,6 +838,7 @@ def _resolve_dflash_draft_attention_backend(server_args: ServerArgs) -> None:
|
||||
)
|
||||
all_causal = getattr(draft_text_config, "is_causal", False) is True
|
||||
if not (all_sliding or all_causal):
|
||||
fallback_backend = get_fallback_backend()
|
||||
logger.warning(
|
||||
"DFLASH only enables 'trtllm_mha' when all layers use sliding "
|
||||
"attention or the draft is explicitly causal; got "
|
||||
@@ -801,15 +849,6 @@ def _resolve_dflash_draft_attention_backend(server_args: ServerArgs) -> None:
|
||||
fallback_backend,
|
||||
)
|
||||
draft_backend = fallback_backend
|
||||
elif draft_backend not in supported_draft_backends:
|
||||
logger.warning(
|
||||
"DFLASH draft worker only supports attention_backend in %s for now, "
|
||||
"but got %r. Falling back to '%s'.",
|
||||
supported_draft_backends,
|
||||
draft_backend,
|
||||
fallback_backend,
|
||||
)
|
||||
draft_backend = fallback_backend
|
||||
# FIXME: avoid overriding server args directly; pass the resolved draft
|
||||
# backend to the draft worker explicitly instead.
|
||||
declare_resolution(
|
||||
|
||||
@@ -12,11 +12,13 @@ Out-of-tree platforms register via setuptools entry_points under the
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional, Type
|
||||
from typing import TYPE_CHECKING, Any, Callable, Optional, Type
|
||||
|
||||
from sglang.srt.platforms.device_mixin import DeviceMixin, PlatformEnum
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import torch
|
||||
|
||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||
|
||||
# Re-export for convenience
|
||||
@@ -56,6 +58,10 @@ class SRTPlatform(DeviceMixin):
|
||||
"""Return the default attention backend name for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_default_speculative_draft_attention_backend(self, algorithm: str) -> str:
|
||||
"""Return the default draft attention backend for an algorithm."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_graph_runner_cls(self) -> type:
|
||||
"""Return the graph runner class for this platform."""
|
||||
raise NotImplementedError
|
||||
@@ -91,6 +97,12 @@ class SRTPlatform(DeviceMixin):
|
||||
"""Return the piecewise compilation backend class for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_speculative_cache_locs_fn(
|
||||
self,
|
||||
) -> Optional[Callable[..., torch.Tensor]]:
|
||||
"""Return a platform implementation for speculative KV-cache locations."""
|
||||
return None
|
||||
|
||||
def get_quantization_config(
|
||||
self, quantization: str
|
||||
) -> Optional[Type[QuantizationConfig]]:
|
||||
@@ -107,6 +119,16 @@ class SRTPlatform(DeviceMixin):
|
||||
"""Whether this platform supports FP8 quantization."""
|
||||
return False
|
||||
|
||||
def supports_speculative_algorithm(self, algorithm: str) -> bool:
|
||||
"""Whether this platform supports the named speculative algorithm."""
|
||||
return False
|
||||
|
||||
def supports_speculative_draft_attention_backend(
|
||||
self, algorithm: str, backend: str
|
||||
) -> bool:
|
||||
"""Whether this platform supports a draft backend for an algorithm."""
|
||||
return False
|
||||
|
||||
def support_cuda_graph(self) -> bool:
|
||||
"""Whether this platform supports device graph capture and replay.
|
||||
Controls CUDA graph (CudaGraphRunner) for the decode path.
|
||||
|
||||
@@ -40,6 +40,7 @@ from sglang.srt.model_executor.runner_utils.pool import (
|
||||
disable_graph_pool_borrow,
|
||||
graph_pool_borrow_enabled,
|
||||
)
|
||||
from sglang.srt.platforms import current_platform
|
||||
from sglang.srt.runtime_context import (
|
||||
get_exec,
|
||||
get_parallel,
|
||||
@@ -619,6 +620,17 @@ class DFlashWorkerV2(BaseSpecWorker):
|
||||
capture_decode_cuda_graph = (
|
||||
get_exec().graph.cuda_graph_config.decode.backend != Backend.DISABLED
|
||||
)
|
||||
if (
|
||||
capture_decode_cuda_graph
|
||||
and current_platform.is_out_of_tree()
|
||||
and not current_platform.support_cuda_graph()
|
||||
):
|
||||
capture_decode_cuda_graph = False
|
||||
logger.warning(
|
||||
"Disable DFLASH draft cuda graph because %s does not support "
|
||||
"device graph capture.",
|
||||
type(current_platform).__name__,
|
||||
)
|
||||
if get_parallel().enable_dp_attention and capture_decode_cuda_graph:
|
||||
# Idle DP ranks skip the draft step, so they cannot join a
|
||||
# shared graph capture/replay; keep the draft eager under dp
|
||||
|
||||
Reference in New Issue
Block a user