[Spec] Enable FlashInfer autotune for spec draft (#29595)
This commit is contained in:
@@ -15,12 +15,9 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
|
||||||
import hashlib
|
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from typing import TYPE_CHECKING, Any, Optional, Tuple
|
from typing import TYPE_CHECKING, Any, Optional, Tuple
|
||||||
|
|
||||||
@@ -43,6 +40,10 @@ from sglang.srt.model_executor.forward_batch_info import (
|
|||||||
PPProxyTensors,
|
PPProxyTensors,
|
||||||
)
|
)
|
||||||
from sglang.srt.model_executor.forward_context import ForwardContext, forward_context
|
from sglang.srt.model_executor.forward_context import ForwardContext, forward_context
|
||||||
|
from sglang.srt.model_executor.runner.flashinfer_autotune import (
|
||||||
|
run_flashinfer_autotune_forward,
|
||||||
|
should_run_flashinfer_autotune,
|
||||||
|
)
|
||||||
from sglang.srt.runtime_context import get_parallel
|
from sglang.srt.runtime_context import get_parallel
|
||||||
from sglang.srt.speculative.spec_info import create_dummy_verify_input
|
from sglang.srt.speculative.spec_info import create_dummy_verify_input
|
||||||
from sglang.srt.utils import (
|
from sglang.srt.utils import (
|
||||||
@@ -213,7 +214,7 @@ class BaseRunner(ABC):
|
|||||||
|
|
||||||
self._pre_initialize_flashinfer_allreduce_workspace()
|
self._pre_initialize_flashinfer_allreduce_workspace()
|
||||||
|
|
||||||
if self._should_run_flashinfer_autotune():
|
if should_run_flashinfer_autotune(self.model_runner):
|
||||||
buffers, batch_size = self._autotune_buffers()
|
buffers, batch_size = self._autotune_buffers()
|
||||||
assert (
|
assert (
|
||||||
buffers is not None
|
buffers is not None
|
||||||
@@ -250,83 +251,6 @@ class BaseRunner(ABC):
|
|||||||
dtype=mr.dtype,
|
dtype=mr.dtype,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _should_run_flashinfer_autotune(self) -> bool:
|
|
||||||
"""Check if flashinfer autotune should be run."""
|
|
||||||
mr = self.model_runner
|
|
||||||
if mr.server_args.disable_flashinfer_autotune:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# CuteDSL v1 (cutedsl runner + deepep a2a) bypasses MoeRunner and must not
|
|
||||||
# be autotuned -- its _dummy_run would dispatch more tokens per rank than
|
|
||||||
# SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK, tripping a DeepEP assert.
|
|
||||||
# Read server_args directly to avoid depending on initialize_moe_config()
|
|
||||||
# having already populated the MoE backend globals.
|
|
||||||
if (
|
|
||||||
mr.server_args.moe_runner_backend == "flashinfer_cutedsl"
|
|
||||||
and mr.server_args.moe_a2a_backend == "deepep"
|
|
||||||
):
|
|
||||||
return False
|
|
||||||
|
|
||||||
backend_str = mr.server_args.moe_runner_backend
|
|
||||||
|
|
||||||
# TODO smor- support other cases for flashinfer autotune, such as, mamba backend
|
|
||||||
|
|
||||||
moe_needs_autotune = backend_str in [
|
|
||||||
"flashinfer_trtllm",
|
|
||||||
"flashinfer_trtllm_routed",
|
|
||||||
"flashinfer_mxfp4",
|
|
||||||
"flashinfer_cutedsl",
|
|
||||||
"flashinfer_cutlass",
|
|
||||||
]
|
|
||||||
|
|
||||||
from sglang.srt.layers.quantization.fp4_utils import (
|
|
||||||
get_fp4_gemm_runner_backend,
|
|
||||||
)
|
|
||||||
|
|
||||||
model_uses_fp4 = mr.model_config.quantization in (
|
|
||||||
"modelopt_fp4",
|
|
||||||
"modelopt_mixed",
|
|
||||||
)
|
|
||||||
fp4_gemm_needs_autotune = model_uses_fp4 and (
|
|
||||||
get_fp4_gemm_runner_backend().is_flashinfer_cutlass()
|
|
||||||
or get_fp4_gemm_runner_backend().is_flashinfer_cutedsl()
|
|
||||||
)
|
|
||||||
|
|
||||||
from sglang.srt.layers.quantization.fp8_utils import (
|
|
||||||
get_fp8_gemm_runner_backend,
|
|
||||||
)
|
|
||||||
from sglang.srt.utils import is_sm100_supported
|
|
||||||
|
|
||||||
model_uses_modelopt_fp8 = mr.model_config.quantization in (
|
|
||||||
"modelopt",
|
|
||||||
"modelopt_fp8",
|
|
||||||
"modelopt_mixed",
|
|
||||||
)
|
|
||||||
# Online MXFP8 (microscaling) linears dispatch to flashinfer's
|
|
||||||
# ``mm_mxfp8``, which the flashinfer fp8 autotune dummy run does not
|
|
||||||
# exercise correctly -- it triggers an illegal memory access inside the
|
|
||||||
# mxfp8 cutlass cubin. The mxfp8 gemm is fixed-config and needs no
|
|
||||||
# tuning, so skip autotune for these models.
|
|
||||||
model_uses_mxfp8 = "mxfp8" in (mr.model_config.quantization or "")
|
|
||||||
fp8_gemm_needs_autotune = not model_uses_mxfp8 and (
|
|
||||||
get_fp8_gemm_runner_backend().is_flashinfer_cutlass()
|
|
||||||
or (model_uses_modelopt_fp8 and is_sm100_supported())
|
|
||||||
)
|
|
||||||
|
|
||||||
if not (
|
|
||||||
moe_needs_autotune or fp4_gemm_needs_autotune or fp8_gemm_needs_autotune
|
|
||||||
):
|
|
||||||
return False
|
|
||||||
|
|
||||||
major, _ = torch.cuda.get_device_capability()
|
|
||||||
if major < 9:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if mr.spec_algorithm.is_speculative():
|
|
||||||
return not mr.is_draft_worker
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _flashinfer_autotune(self, *, buffers, batch_size):
|
def _flashinfer_autotune(self, *, buffers, batch_size):
|
||||||
"""Run flashinfer autotune.
|
"""Run flashinfer autotune.
|
||||||
|
|
||||||
@@ -335,76 +259,11 @@ class BaseRunner(ABC):
|
|||||||
Supplied by warmup() (the decode runner's captured buffers when a graph
|
Supplied by warmup() (the decode runner's captured buffers when a graph
|
||||||
runner exists; a freshly-allocated dummy set in the eager path).
|
runner exists; a freshly-allocated dummy set in the eager path).
|
||||||
"""
|
"""
|
||||||
from flashinfer.autotuner import autotune
|
|
||||||
|
|
||||||
from sglang.srt.layers.logits_processor import autotune_dummy_run_mode
|
def forward_fn():
|
||||||
|
|
||||||
mr = self.model_runner
|
|
||||||
cache_path = self._flashinfer_autotune_cache_path()
|
|
||||||
if envs.SGLANG_FLASHINFER_AUTOTUNE_CACHE.get():
|
|
||||||
autotune_cache = cache_path
|
|
||||||
logger.info("Running FlashInfer autotune with cache: %s", autotune_cache)
|
|
||||||
else:
|
|
||||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
||||||
runs_dir = cache_path.parent / "runs"
|
|
||||||
runs_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
autotune_cache = (
|
|
||||||
runs_dir / f"{cache_path.stem}.{timestamp}{cache_path.suffix}"
|
|
||||||
)
|
|
||||||
logger.info(
|
|
||||||
"Running FlashInfer autotune (cache reuse DISABLED via "
|
|
||||||
"SGLANG_FLASHINFER_AUTOTUNE_CACHE=0); writing fresh result to: %s",
|
|
||||||
autotune_cache,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run warmup on the non-default stream to avoid NCCL 2.29+ cudaMemcpyBatchAsync
|
|
||||||
# calls on default stream (unsupported by CUDA) when --enable-symm-mem is used.
|
|
||||||
mr.forward_stream.wait_stream(torch.cuda.current_stream())
|
|
||||||
with torch.get_device_module(mr.device).stream(mr.forward_stream):
|
|
||||||
with (
|
|
||||||
torch.inference_mode(),
|
|
||||||
autotune(True, cache=str(autotune_cache)),
|
|
||||||
autotune_dummy_run_mode(),
|
|
||||||
):
|
|
||||||
self._dummy_run(batch_size=batch_size, buffers=buffers)
|
self._dummy_run(batch_size=batch_size, buffers=buffers)
|
||||||
torch.cuda.current_stream().wait_stream(mr.forward_stream)
|
|
||||||
logger.info("FlashInfer autotune completed.")
|
|
||||||
|
|
||||||
def _flashinfer_autotune_cache_path(self) -> Path:
|
run_flashinfer_autotune_forward(self.model_runner, forward_fn, skip_logits=True)
|
||||||
import flashinfer
|
|
||||||
|
|
||||||
mr = self.model_runner
|
|
||||||
major, minor = torch.cuda.get_device_capability(mr.device)
|
|
||||||
arch = f"sm{major}{minor}"
|
|
||||||
flashinfer_version = getattr(flashinfer, "__version__", "unknown")
|
|
||||||
|
|
||||||
server_args = mr.server_args
|
|
||||||
model_key = "|".join(
|
|
||||||
[
|
|
||||||
str(server_args.model_path),
|
|
||||||
str(mr.dtype),
|
|
||||||
str(server_args.quantization),
|
|
||||||
str(server_args.moe_runner_backend),
|
|
||||||
str(mr.tp_size),
|
|
||||||
str(mr.pp_size),
|
|
||||||
str(mr.dp_size),
|
|
||||||
str(mr.moe_ep_size),
|
|
||||||
str(mr.model_config.hf_config.__class__.__name__),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
cache_key = hashlib.sha256(model_key.encode()).hexdigest()[:16]
|
|
||||||
cache_dir = (
|
|
||||||
Path(envs.SGLANG_CACHE_DIR.get())
|
|
||||||
/ "flashinfer"
|
|
||||||
/ "autotune"
|
|
||||||
/ flashinfer_version
|
|
||||||
/ arch
|
|
||||||
/ cache_key
|
|
||||||
)
|
|
||||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
return (
|
|
||||||
cache_dir / f"rank_tp{mr.tp_rank}_pp{mr.pp_rank}_dp{mr.dp_rank or 0}.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _alloc_dummy_decode_buffers(self, max_bs: int, *, num_tokens_per_bs: int = 1):
|
def _alloc_dummy_decode_buffers(self, max_bs: int, *, num_tokens_per_bs: int = 1):
|
||||||
"""Allocate one static decode-buffer set for a dummy forward, sized to
|
"""Allocate one static decode-buffer set for a dummy forward, sized to
|
||||||
@@ -413,10 +272,9 @@ class BaseRunner(ABC):
|
|||||||
The PP-parallel DeepGEMM warmup sweeps batch sizes far larger than any
|
The PP-parallel DeepGEMM warmup sweeps batch sizes far larger than any
|
||||||
runner's max_bs (up to ~n_sms*block_m), so no pre-allocated runner buffer
|
runner's max_bs (up to ~n_sms*block_m), so no pre-allocated runner buffer
|
||||||
set fits; it builds one here and hands it to _dummy_run (reused across the
|
set fits; it builds one here and hands it to _dummy_run (reused across the
|
||||||
sweep; _dummy_run slices it per shape). The flashinfer autotune does NOT
|
sweep; _dummy_run slices it per shape). Eager FlashInfer autotune also
|
||||||
use this -- it reuses an existing runner's buffers via _autotune_buffers
|
allocates decode-shaped scratch buffers here. Decode cuda-graph autotune
|
||||||
(the eager input registry, or the decode cuda-graph runner's captured
|
reuses the captured runner buffers instead.
|
||||||
buffers).
|
|
||||||
"""
|
"""
|
||||||
mr = self.model_runner
|
mr = self.model_runner
|
||||||
return _allocate_decode_buffers(
|
return _allocate_decode_buffers(
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ from sglang.srt.model_executor.runner.base_cuda_graph_runner import (
|
|||||||
freeze_gc,
|
freeze_gc,
|
||||||
get_batch_sizes_to_capture,
|
get_batch_sizes_to_capture,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.model_executor.runner.flashinfer_autotune import (
|
||||||
|
maybe_flashinfer_autotune_speculative_draft,
|
||||||
|
)
|
||||||
from sglang.srt.model_executor.runner.shape_key import ShapeKey
|
from sglang.srt.model_executor.runner.shape_key import ShapeKey
|
||||||
from sglang.srt.model_executor.runner_backend.breakable_cuda_graph_backend import (
|
from sglang.srt.model_executor.runner_backend.breakable_cuda_graph_backend import (
|
||||||
BreakableCudaGraphBackend,
|
BreakableCudaGraphBackend,
|
||||||
@@ -853,15 +856,22 @@ class DecodeCudaGraphRunner(BaseCudaGraphRunner):
|
|||||||
|
|
||||||
with canary_ctx:
|
with canary_ctx:
|
||||||
shape_key = self._make_graph_key(bs, stream_idx, variant_label)
|
shape_key = self._make_graph_key(bs, stream_idx, variant_label)
|
||||||
self.backend.capture_one(
|
|
||||||
shape_key,
|
|
||||||
run_once,
|
|
||||||
dummies=None,
|
|
||||||
post_warmup_hook = getattr(
|
post_warmup_hook = getattr(
|
||||||
self.model_runner.attn_backend,
|
self.model_runner.attn_backend,
|
||||||
"on_after_cuda_graph_warmup",
|
"on_after_cuda_graph_warmup",
|
||||||
None,
|
None,
|
||||||
),
|
)
|
||||||
|
maybe_flashinfer_autotune_speculative_draft(
|
||||||
|
self,
|
||||||
|
run_once,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
|
skip_logits=False,
|
||||||
|
)
|
||||||
|
self.backend.capture_one(
|
||||||
|
shape_key,
|
||||||
|
run_once,
|
||||||
|
dummies=None,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
)
|
)
|
||||||
|
|
||||||
def recapture_if_needed(self, forward_batch: ForwardBatch):
|
def recapture_if_needed(self, forward_batch: ForwardBatch):
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# Copyright 2023-2026 SGLang Team
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
# ==============================================================================
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING, Callable, Optional
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from sglang.srt.environ import envs
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from sglang.srt.model_executor.model_runner import ModelRunner
|
||||||
|
from sglang.srt.model_executor.runner.base_runner import BaseRunner
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def should_run_flashinfer_autotune(
|
||||||
|
model_runner: ModelRunner, *, for_speculative_draft: bool = False
|
||||||
|
) -> bool:
|
||||||
|
"""Check if flashinfer autotune should be run."""
|
||||||
|
mr = model_runner
|
||||||
|
if mr.device != "cuda":
|
||||||
|
return False
|
||||||
|
if mr.server_args.disable_flashinfer_autotune:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# CuteDSL v1 (cutedsl runner + deepep a2a) bypasses MoeRunner and must not
|
||||||
|
# be autotuned -- its _dummy_run would dispatch more tokens per rank than
|
||||||
|
# SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK, tripping a DeepEP assert.
|
||||||
|
# Read server_args directly to avoid depending on initialize_moe_config()
|
||||||
|
# having already populated the MoE backend globals.
|
||||||
|
if (
|
||||||
|
mr.server_args.moe_runner_backend == "flashinfer_cutedsl"
|
||||||
|
and mr.server_args.moe_a2a_backend == "deepep"
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
backend_str = mr.server_args.moe_runner_backend
|
||||||
|
|
||||||
|
# TODO smor- support other cases for flashinfer autotune, such as, mamba backend
|
||||||
|
|
||||||
|
moe_needs_autotune = backend_str in [
|
||||||
|
"flashinfer_trtllm",
|
||||||
|
"flashinfer_trtllm_routed",
|
||||||
|
"flashinfer_mxfp4",
|
||||||
|
"flashinfer_cutedsl",
|
||||||
|
"flashinfer_cutlass",
|
||||||
|
]
|
||||||
|
|
||||||
|
from sglang.srt.layers.quantization.fp4_utils import (
|
||||||
|
get_fp4_gemm_runner_backend,
|
||||||
|
)
|
||||||
|
|
||||||
|
model_quantization = mr.model_config.quantization
|
||||||
|
model_uses_fp4 = model_quantization in (
|
||||||
|
"modelopt_fp4",
|
||||||
|
"modelopt_mixed",
|
||||||
|
)
|
||||||
|
fp4_gemm_needs_autotune = model_uses_fp4 and (
|
||||||
|
get_fp4_gemm_runner_backend().is_flashinfer_cutlass()
|
||||||
|
or get_fp4_gemm_runner_backend().is_flashinfer_cutedsl()
|
||||||
|
)
|
||||||
|
|
||||||
|
from sglang.srt.layers.quantization.fp8_utils import (
|
||||||
|
get_fp8_gemm_runner_backend,
|
||||||
|
)
|
||||||
|
from sglang.srt.utils import is_sm100_supported
|
||||||
|
|
||||||
|
model_uses_modelopt_fp8 = model_quantization in (
|
||||||
|
"modelopt",
|
||||||
|
"modelopt_fp8",
|
||||||
|
"modelopt_mixed",
|
||||||
|
)
|
||||||
|
# Online MXFP8 (microscaling) linears dispatch to flashinfer's
|
||||||
|
# ``mm_mxfp8``, which the flashinfer fp8 autotune dummy run does not
|
||||||
|
# exercise correctly -- it triggers an illegal memory access inside the
|
||||||
|
# mxfp8 cutlass cubin. The mxfp8 gemm is fixed-config and needs no
|
||||||
|
# tuning, so skip autotune for these models.
|
||||||
|
model_uses_mxfp8 = "mxfp8" in (model_quantization or "")
|
||||||
|
fp8_gemm_needs_autotune = not model_uses_mxfp8 and (
|
||||||
|
get_fp8_gemm_runner_backend().is_flashinfer_cutlass()
|
||||||
|
or (model_uses_modelopt_fp8 and is_sm100_supported())
|
||||||
|
)
|
||||||
|
|
||||||
|
if not (moe_needs_autotune or fp4_gemm_needs_autotune or fp8_gemm_needs_autotune):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if torch.cuda.get_device_capability()[0] < 9:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if mr.spec_algorithm.is_speculative():
|
||||||
|
return mr.is_draft_worker if for_speculative_draft else not mr.is_draft_worker
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def flashinfer_autotune_cache_path(model_runner: ModelRunner) -> Path:
|
||||||
|
import flashinfer
|
||||||
|
|
||||||
|
mr = model_runner
|
||||||
|
major, minor = torch.cuda.get_device_capability(mr.device)
|
||||||
|
arch = f"sm{major}{minor}"
|
||||||
|
flashinfer_version = getattr(flashinfer, "__version__", "unknown")
|
||||||
|
|
||||||
|
server_args = mr.server_args
|
||||||
|
model_key_parts = [
|
||||||
|
str(server_args.model_path),
|
||||||
|
str(mr.dtype),
|
||||||
|
str(server_args.quantization),
|
||||||
|
str(server_args.moe_runner_backend),
|
||||||
|
str(mr.tp_size),
|
||||||
|
str(mr.pp_size),
|
||||||
|
str(mr.dp_size),
|
||||||
|
str(mr.moe_ep_size),
|
||||||
|
str(mr.model_config.hf_config.__class__.__name__),
|
||||||
|
]
|
||||||
|
if mr.is_draft_worker:
|
||||||
|
model_key_parts.append(f"draft_quant={mr.model_config.quantization}")
|
||||||
|
model_key = "|".join(model_key_parts)
|
||||||
|
cache_key = hashlib.sha256(model_key.encode()).hexdigest()[:16]
|
||||||
|
cache_dir = (
|
||||||
|
Path(envs.SGLANG_CACHE_DIR.get())
|
||||||
|
/ "flashinfer"
|
||||||
|
/ "autotune"
|
||||||
|
/ flashinfer_version
|
||||||
|
/ arch
|
||||||
|
/ cache_key
|
||||||
|
)
|
||||||
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
return cache_dir / f"rank_tp{mr.tp_rank}_pp{mr.pp_rank}_dp{mr.dp_rank or 0}.json"
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def flashinfer_autotune_context(model_runner: ModelRunner, *, skip_logits: bool):
|
||||||
|
from flashinfer.autotuner import autotune
|
||||||
|
|
||||||
|
mr = model_runner
|
||||||
|
cache_path = flashinfer_autotune_cache_path(mr)
|
||||||
|
if envs.SGLANG_FLASHINFER_AUTOTUNE_CACHE.get():
|
||||||
|
autotune_cache = cache_path
|
||||||
|
logger.info("Running FlashInfer autotune with cache: %s", autotune_cache)
|
||||||
|
else:
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
runs_dir = cache_path.parent / "runs"
|
||||||
|
runs_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
autotune_cache = runs_dir / f"{cache_path.stem}.{timestamp}{cache_path.suffix}"
|
||||||
|
logger.info(
|
||||||
|
"Running FlashInfer autotune (cache reuse DISABLED via "
|
||||||
|
"SGLANG_FLASHINFER_AUTOTUNE_CACHE=0); writing fresh result to: %s",
|
||||||
|
autotune_cache,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Run warmup on the non-default stream to avoid NCCL 2.29+ cudaMemcpyBatchAsync
|
||||||
|
# calls on default stream (unsupported by CUDA) when --enable-symm-mem is used.
|
||||||
|
mr.forward_stream.wait_stream(torch.cuda.current_stream())
|
||||||
|
with torch.get_device_module(mr.device).stream(mr.forward_stream):
|
||||||
|
maybe_skip_logits = contextlib.nullcontext()
|
||||||
|
if skip_logits:
|
||||||
|
from sglang.srt.layers.logits_processor import autotune_dummy_run_mode
|
||||||
|
|
||||||
|
maybe_skip_logits = autotune_dummy_run_mode()
|
||||||
|
with torch.inference_mode(), autotune(
|
||||||
|
True, cache=str(autotune_cache)
|
||||||
|
), maybe_skip_logits:
|
||||||
|
yield
|
||||||
|
torch.cuda.current_stream().wait_stream(mr.forward_stream)
|
||||||
|
logger.info("FlashInfer autotune completed.")
|
||||||
|
|
||||||
|
|
||||||
|
def run_flashinfer_autotune_forward(
|
||||||
|
model_runner: ModelRunner, forward_fn: Callable[[], None], *, skip_logits: bool
|
||||||
|
) -> None:
|
||||||
|
"""Run flashinfer autotune forward."""
|
||||||
|
with flashinfer_autotune_context(model_runner, skip_logits=skip_logits):
|
||||||
|
forward_fn()
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_flashinfer_autotune_speculative_draft(
|
||||||
|
runner: BaseRunner,
|
||||||
|
forward_fn: Callable[[], None],
|
||||||
|
*,
|
||||||
|
post_warmup_hook: Optional[Callable[[], None]] = None,
|
||||||
|
skip_logits: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Run speculative draft flashinfer autotune."""
|
||||||
|
mr = runner.model_runner
|
||||||
|
phase_key = f"{runner.__class__.__module__}.{runner.__class__.__qualname__}"
|
||||||
|
tuned_phases = getattr(mr, "_flashinfer_spec_draft_autotuned_phases", None)
|
||||||
|
if tuned_phases is None:
|
||||||
|
tuned_phases = set()
|
||||||
|
mr._flashinfer_spec_draft_autotuned_phases = tuned_phases
|
||||||
|
if phase_key in tuned_phases:
|
||||||
|
return
|
||||||
|
if (
|
||||||
|
not mr.spec_algorithm.is_speculative()
|
||||||
|
or not mr.is_draft_worker
|
||||||
|
or not should_run_flashinfer_autotune(mr, for_speculative_draft=True)
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_and_reset():
|
||||||
|
forward_fn()
|
||||||
|
if post_warmup_hook is not None:
|
||||||
|
post_warmup_hook()
|
||||||
|
|
||||||
|
run_flashinfer_autotune_forward(mr, run_and_reset, skip_logits=skip_logits)
|
||||||
|
tuned_phases.add(phase_key)
|
||||||
@@ -28,6 +28,9 @@ from sglang.srt.model_executor.runner import (
|
|||||||
get_batch_sizes_to_capture,
|
get_batch_sizes_to_capture,
|
||||||
model_capture_mode,
|
model_capture_mode,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.model_executor.runner.flashinfer_autotune import (
|
||||||
|
maybe_flashinfer_autotune_speculative_draft,
|
||||||
|
)
|
||||||
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
||||||
from sglang.srt.model_executor.runner_backend_utils import (
|
from sglang.srt.model_executor.runner_backend_utils import (
|
||||||
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
||||||
@@ -440,13 +443,20 @@ class EAGLEDraftCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
forward_batch.mark_forward_metadata_ready()
|
forward_batch.mark_forward_metadata_ready()
|
||||||
self.deepep_adapter.capture(is_extend_in_batch=False)
|
self.deepep_adapter.capture(is_extend_in_batch=False)
|
||||||
shape_key = self._make_graph_key(num_seqs)
|
shape_key = self._make_graph_key(num_seqs)
|
||||||
|
post_warmup_hook = getattr(
|
||||||
|
self.draft_attn_backend, "on_after_cuda_graph_warmup", None
|
||||||
|
)
|
||||||
|
maybe_flashinfer_autotune_speculative_draft(
|
||||||
|
self,
|
||||||
|
run_once,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
|
skip_logits=False,
|
||||||
|
)
|
||||||
self.backend.capture_one(
|
self.backend.capture_one(
|
||||||
shape_key,
|
shape_key,
|
||||||
run_once,
|
run_once,
|
||||||
dummies=None,
|
dummies=None,
|
||||||
post_warmup_hook=getattr(
|
post_warmup_hook=post_warmup_hook,
|
||||||
self.draft_attn_backend, "on_after_cuda_graph_warmup", None
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _postprocess_output_to_raw_bs(self, out, raw_bs):
|
def _postprocess_output_to_raw_bs(self, out, raw_bs):
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ from sglang.srt.model_executor.runner import (
|
|||||||
get_batch_sizes_to_capture,
|
get_batch_sizes_to_capture,
|
||||||
model_capture_mode,
|
model_capture_mode,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.model_executor.runner.flashinfer_autotune import (
|
||||||
|
maybe_flashinfer_autotune_speculative_draft,
|
||||||
|
)
|
||||||
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
||||||
from sglang.srt.model_executor.runner_backend_utils import (
|
from sglang.srt.model_executor.runner_backend_utils import (
|
||||||
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
||||||
@@ -432,15 +435,22 @@ class EAGLEDraftExtendCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
)
|
)
|
||||||
with canary_ctx:
|
with canary_ctx:
|
||||||
shape_key = self._make_graph_key(bs)
|
shape_key = self._make_graph_key(bs)
|
||||||
self.backend.capture_one(
|
|
||||||
shape_key,
|
|
||||||
run_once,
|
|
||||||
dummies=None,
|
|
||||||
post_warmup_hook = getattr(
|
post_warmup_hook = getattr(
|
||||||
self.draft_extend_attn_backend,
|
self.draft_extend_attn_backend,
|
||||||
"on_after_cuda_graph_warmup",
|
"on_after_cuda_graph_warmup",
|
||||||
None,
|
None,
|
||||||
),
|
)
|
||||||
|
maybe_flashinfer_autotune_speculative_draft(
|
||||||
|
self,
|
||||||
|
run_once,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
|
skip_logits=False,
|
||||||
|
)
|
||||||
|
self.backend.capture_one(
|
||||||
|
shape_key,
|
||||||
|
run_once,
|
||||||
|
dummies=None,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, forward_batch: ForwardBatch):
|
def execute(self, forward_batch: ForwardBatch):
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ from sglang.srt.model_executor.runner import (
|
|||||||
get_batch_sizes_to_capture,
|
get_batch_sizes_to_capture,
|
||||||
model_capture_mode,
|
model_capture_mode,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.model_executor.runner.flashinfer_autotune import (
|
||||||
|
maybe_flashinfer_autotune_speculative_draft,
|
||||||
|
)
|
||||||
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
||||||
from sglang.srt.model_executor.runner_backend_utils import (
|
from sglang.srt.model_executor.runner_backend_utils import (
|
||||||
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
||||||
@@ -321,13 +324,20 @@ class FrozenKVMTPCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
)
|
)
|
||||||
self.deepep_adapter.capture(is_extend_in_batch=False)
|
self.deepep_adapter.capture(is_extend_in_batch=False)
|
||||||
shape_key = self._make_graph_key(request_bs)
|
shape_key = self._make_graph_key(request_bs)
|
||||||
|
post_warmup_hook = getattr(
|
||||||
|
self.draft_attn_backend, "on_after_cuda_graph_warmup", None
|
||||||
|
)
|
||||||
|
maybe_flashinfer_autotune_speculative_draft(
|
||||||
|
self,
|
||||||
|
run_once,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
|
skip_logits=False,
|
||||||
|
)
|
||||||
self.backend.capture_one(
|
self.backend.capture_one(
|
||||||
shape_key,
|
shape_key,
|
||||||
run_once,
|
run_once,
|
||||||
dummies=None,
|
dummies=None,
|
||||||
post_warmup_hook=getattr(
|
post_warmup_hook=post_warmup_hook,
|
||||||
self.draft_attn_backend, "on_after_cuda_graph_warmup", None
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
self.draft_attn_backend.token_to_kv_pool = saved_backend_pool
|
self.draft_attn_backend.token_to_kv_pool = saved_backend_pool
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ from sglang.srt.model_executor.runner import (
|
|||||||
get_batch_sizes_to_capture,
|
get_batch_sizes_to_capture,
|
||||||
model_capture_mode,
|
model_capture_mode,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.model_executor.runner.flashinfer_autotune import (
|
||||||
|
maybe_flashinfer_autotune_speculative_draft,
|
||||||
|
)
|
||||||
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
from sglang.srt.model_executor.runner_backend.utils import resolve_decode_backend
|
||||||
from sglang.srt.model_executor.runner_backend_utils import (
|
from sglang.srt.model_executor.runner_backend_utils import (
|
||||||
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
CUDA_GRAPH_CAPTURE_FAILED_MSG,
|
||||||
@@ -369,13 +372,20 @@ class MultiLayerEagleDraftExtendCudaGraphRunner(DecodeCudaGraphRunner):
|
|||||||
attn_backend.init_forward_metadata_out_graph(forward_batch, in_capture=True)
|
attn_backend.init_forward_metadata_out_graph(forward_batch, in_capture=True)
|
||||||
self.deepep_adapter.capture(is_extend_in_batch=True)
|
self.deepep_adapter.capture(is_extend_in_batch=True)
|
||||||
shape_key = self._make_graph_key(bs)
|
shape_key = self._make_graph_key(bs)
|
||||||
|
post_warmup_hook = getattr(
|
||||||
|
self.attn_backend, "on_after_cuda_graph_warmup", None
|
||||||
|
)
|
||||||
|
maybe_flashinfer_autotune_speculative_draft(
|
||||||
|
self,
|
||||||
|
run_once,
|
||||||
|
post_warmup_hook=post_warmup_hook,
|
||||||
|
skip_logits=False,
|
||||||
|
)
|
||||||
self.backend.capture_one(
|
self.backend.capture_one(
|
||||||
shape_key,
|
shape_key,
|
||||||
run_once,
|
run_once,
|
||||||
dummies=None,
|
dummies=None,
|
||||||
post_warmup_hook=getattr(
|
post_warmup_hook=post_warmup_hook,
|
||||||
self.attn_backend, "on_after_cuda_graph_warmup", None
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def replay(self, bs: int, seq_lens_sum: int, spec_info: EagleDraftExtendInput):
|
def replay(self, bs: int, seq_lens_sum: int, spec_info: EagleDraftExtendInput):
|
||||||
|
|||||||
@@ -276,6 +276,7 @@ class TinyModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = sliding_window_size is not None
|
self.is_hybrid_swa = sliding_window_size is not None
|
||||||
self.is_local_attention_model = sliding_window_size is not None
|
self.is_local_attention_model = sliding_window_size is not None
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ class TinyDSAModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
self.sliding_window_size = None
|
self.sliding_window_size = None
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ class TinyDSV4ModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.is_local_attention_model = False
|
self.is_local_attention_model = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ class TinyDualChunkModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
self.sliding_window_size = None
|
self.sliding_window_size = None
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ class TinyGDNModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.is_local_attention_model = False
|
self.is_local_attention_model = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ class TinyKDAModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.is_local_attention_model = False
|
self.is_local_attention_model = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ class TinyLightningModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.is_local_attention_model = False
|
self.is_local_attention_model = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ class TinyMamba2ModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.is_local_attention_model = False
|
self.is_local_attention_model = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ class TinyMLAModelConfig:
|
|||||||
self.is_encoder_decoder = False
|
self.is_encoder_decoder = False
|
||||||
self.is_multimodal = False
|
self.is_multimodal = False
|
||||||
self.is_generation = True
|
self.is_generation = True
|
||||||
|
self.quantization = None
|
||||||
self.is_hybrid_swa = False
|
self.is_hybrid_swa = False
|
||||||
self.is_local_attention_model = False
|
self.is_local_attention_model = False
|
||||||
self.attention_chunk_size = None
|
self.attention_chunk_size = None
|
||||||
|
|||||||
Reference in New Issue
Block a user