[core] Consolidate compiled-kernel caches under SGLANG_CACHE_DIR (#32434)

This commit is contained in:
Shu Wang
2026-08-05 13:54:27 -07:00
committed by GitHub
parent 717a559f02
commit 55b1c09e73
9 changed files with 116 additions and 24 deletions
+8
View File
@@ -1,5 +1,13 @@
# SGLang public APIs
# sglang.srt.environ must run before the rest of this file's imports
# (hf_transformers_patches, lang.api, ...), which pull in torch and
# FlashInfer: those claim these cache dirs early, and the first value set is
# the one that sticks. Safe here -- environ has no heavy dependency (no torch).
from sglang.srt.environ import redirect_third_party_caches
redirect_third_party_caches()
# Install stubs early for platforms where certain dependencies are unavailable
# (e.g. macOS/MPS has no triton, and torch.mps lacks Stream / set_device /
# get_device_properties). This must run before any downstream imports.
@@ -82,6 +82,7 @@ from sglang.multimodal_gen.runtime.utils.trace_wrapper import (
trace_slice,
)
from sglang.multimodal_gen.utils import kill_itself_when_parent_died
from sglang.srt.environ import third_party_cache_defaults
from sglang.srt.utils.network import NetworkAddress
logger = init_logger(__name__)
@@ -175,12 +176,17 @@ class GPUWorker(GPUWorkerPostTrainingMixin):
envs.SGLANG_DIFFUSION_CACHE_ROOT, "torch_compile_cache"
)
tmp_root = tempfile.gettempdir()
sglang_defaults = third_party_cache_defaults()
for env_name, sub in (
("TORCHINDUCTOR_CACHE_DIR", "inductor"),
("TRITON_CACHE_DIR", "triton"),
):
current = os.environ.get(env_name)
if current and not current.startswith(tmp_root):
if (
current
and current != sglang_defaults.get(env_name)
and not current.startswith(tmp_root)
):
# Respect an explicit, non-ephemeral user-provided cache dir.
continue
cache_path = os.path.join(compile_cache_root, sub)
+28
View File
@@ -1673,6 +1673,34 @@ def _set_envs_and_config(server_args: ServerArgs):
if gc_threshold := server_args.gc_threshold:
gc.set_threshold(*gc_threshold)
_log_legacy_kernel_cache_dirs()
def _log_legacy_kernel_cache_dirs():
"""Note the pre-SGLANG_CACHE_DIR cache dirs without touching them: other
frameworks on the box may still be using them."""
# TODO(shuwang21): drop once SGLANG_CACHE_DIR has been the default for a
# few releases.
legacy_dirs = [
d
for d in (
os.path.expanduser("~/.triton"),
os.path.expanduser("~/.cache/flashinfer"),
os.path.expanduser("~/.cache/deep_gemm"),
)
if os.path.isdir(d)
]
if not legacy_dirs:
return
logger.info(
"Compiled-kernel caches now live under SGLANG_CACHE_DIR (%s). These "
"older directories are no longer used by sglang, but may still be "
"used by other frameworks on this machine, so they were left alone: "
"%s. Remove them yourself if nothing else needs them.",
envs.SGLANG_CACHE_DIR.get(),
", ".join(legacy_dirs),
)
def _scheduler_died_error(rank: int, proc) -> RuntimeError:
"""Build a descriptive error for a scheduler process that died during init."""
+39 -2
View File
@@ -5,7 +5,7 @@ import subprocess
import warnings
from contextlib import ExitStack, contextmanager
from enum import IntEnum
from typing import Any, Optional
from typing import Any, Dict, Optional
@functools.lru_cache(maxsize=1)
@@ -25,6 +25,15 @@ def _default_hip() -> bool:
return False
def _default_cache_subdir(name: str) -> str:
"""A directory under SGLANG_CACHE_DIR, for env defaults that track it.
Pass as a callable default: SGLANG_CACHE_DIR is declared further down the
Envs body, and resolving late also lets tests override it.
"""
return os.path.join(os.path.expanduser(envs.SGLANG_CACHE_DIR.get()), name)
class EnvField:
_allow_set_name = True
@@ -737,7 +746,8 @@ class Envs:
SGLANG_JIT_DEEPGEMM_FAST_WARMUP = EnvBool(False)
SGLANG_JIT_DEEPGEMM_COMPILE_WORKERS = EnvInt(4)
SGLANG_IN_DEEPGEMM_PRECOMPILE_STAGE = EnvBool(False)
SGLANG_DG_CACHE_DIR = EnvStr(os.path.expanduser("~/.cache/deep_gemm"))
# Resolved lazily so it tracks SGLANG_CACHE_DIR, which is defined below.
SGLANG_DG_CACHE_DIR = EnvStr(lambda: _default_cache_subdir("deep_gemm"))
SGLANG_DG_USE_NVRTC = EnvBool(False)
SGLANG_USE_DEEPGEMM_BMM = EnvBool(False)
SGLANG_DEEPGEMM_SANITY_CHECK = EnvBool(False)
@@ -1367,6 +1377,33 @@ def _warn_deprecated_env_to_cli_flag(env_name: str, suggestion: str):
warnings.warn(f"Environment variable {env_name} is deprecated. {suggestion}")
def third_party_cache_defaults() -> Dict[str, str]:
base = os.path.expanduser(envs.SGLANG_CACHE_DIR.get())
return {
"TRITON_CACHE_DIR": os.path.join(base, "triton"),
"TORCHINDUCTOR_CACHE_DIR": os.path.join(base, "inductor"),
"CUDA_CACHE_PATH": os.path.join(base, "nv"),
# FlashInfer appends ".cache/flashinfer" to this base itself, so this
# is the base dir rather than the final cache dir.
"FLASHINFER_WORKSPACE_BASE": base,
}
def redirect_third_party_caches():
"""Point third-party JIT caches at SGLANG_CACHE_DIR, so a run's compiled
kernels can be cleaned, warmed or volume-mounted as one directory.
Must be called early. The redirect silently does nothing if either of
these has already happened:
- FlashInfer was imported. It resolves its workspace at import time.
- Inductor made its first ``cache_dir()`` call. That call setdefaults
TORCHINDUCTOR_CACHE_DIR itself.
"""
for key, value in third_party_cache_defaults().items():
os.environ.setdefault(key, value)
def _convert_SGL_to_SGLANG():
_print_deprecated_env("SGLANG_GC_LOG", "SGLANG_LOG_GC")
_print_deprecated_env(
@@ -35,10 +35,9 @@ _IS_FIRST_RANK_ON_NODE = envs.SGLANG_IS_FIRST_RANK_ON_NODE.get()
_IN_PRECOMPILE_STAGE = envs.SGLANG_IN_DEEPGEMM_PRECOMPILE_STAGE.get()
_FAST_WARMUP = envs.SGLANG_JIT_DEEPGEMM_FAST_WARMUP.get()
# Force redirect deep_gemm cache_dir
os.environ["DG_JIT_CACHE_DIR"] = os.getenv(
"SGLANG_DG_CACHE_DIR", os.path.join(os.path.expanduser("~"), ".cache", "deep_gemm")
)
# Force redirect deep_gemm cache_dir. Defaults under SGLANG_CACHE_DIR so it
# sits with the other compiled-kernel caches; SGLANG_DG_CACHE_DIR still wins.
os.environ["DG_JIT_CACHE_DIR"] = envs.SGLANG_DG_CACHE_DIR.get()
# Refer to https://github.com/deepseek-ai/DeepGEMM/commit/d75b218b7b8f4a5dd5406ac87905039ead3ae42f
# NVRTC may have performance loss with some cases.