fix: avoid tilelang cuda runtime pollution (#30870)

This commit is contained in:
Mick
2026-07-14 22:30:27 +08:00
committed by GitHub
parent f853293440
commit 04af94d150
3 changed files with 98 additions and 27 deletions
@@ -9,6 +9,7 @@ convenient for use when we just need to call a few functions.
import ctypes import ctypes
import logging import logging
import os
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
@@ -47,24 +48,30 @@ def find_loaded_library(lib_name) -> Optional[str]:
shared libraries loaded by the process. We can use this file to find the path of the shared libraries loaded by the process. We can use this file to find the path of the
a loaded library. a loaded library.
""" # noqa """ # noqa
found = False candidates = []
with open("/proc/self/maps") as f: with open("/proc/self/maps") as f:
for line in f: for line in f:
if lib_name in line: if lib_name not in line or "/" not in line:
found = True continue
break path = line[line.index("/") :].strip()
if not found: if path.endswith(" (deleted)"):
path = path[: -len(" (deleted)")]
filename = os.path.basename(path)
if filename.rpartition(".so")[0].startswith(lib_name):
candidates.append(path)
if not candidates:
# the library is not loaded in the current process # the library is not loaded in the current process
return None return None
# if lib_name is libcudart, we need to match a line with:
# address /path/to/libcudart-hash.so.11.0 # TileLang ships a ``libcudart_stub.so`` that is only sufficient for its
start = line.index("/") # JIT loader. It can precede the actual CUDA runtime in /proc/self/maps;
path = line[start:].strip() # choosing it makes CUDA IPC and FlashInfer all-reduce fail when resolving
filename = path.split("/")[-1] # symbols such as cudaDeviceReset.
assert filename.rpartition(".so")[0].startswith( for path in candidates:
lib_name if "stub" not in os.path.basename(path):
), f"Unexpected filename: {filename} for library {lib_name}" return path
return path return candidates[0]
class CudaRTLibrary: class CudaRTLibrary:
+39 -13
View File
@@ -1,13 +1,17 @@
from __future__ import annotations from __future__ import annotations
import concurrent.futures import concurrent.futures
import functools
import logging import logging
import time import time
from contextlib import nullcontext from contextlib import nullcontext
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any,
Callable,
Iterable, Iterable,
List, List,
NamedTuple,
Optional, Optional,
Set, Set,
Tuple, Tuple,
@@ -129,15 +133,6 @@ if not _is_hip:
prepare_context_parallel_metadata, prepare_context_parallel_metadata,
) )
if _is_xpu:
from sgl_kernel import hc_split_sinkhorn
else:
from sglang.kernels.ops.layernorm.mhc import (
hc_split_sinkhorn,
mhc_fused_post_pre,
npu_hc_pre,
)
from sglang.srt.utils import ( from sglang.srt.utils import (
LazyValue, LazyValue,
add_prefix, add_prefix,
@@ -155,6 +150,37 @@ from sglang.srt.utils.hf_transformers_utils import get_rope_config
if _is_npu: if _is_npu:
import torch_npu import torch_npu
class MhcOps(NamedTuple):
hc_split_sinkhorn: Callable[..., Any]
mhc_fused_post_pre: Optional[Callable[..., Any]]
npu_hc_pre: Optional[Callable[..., Any]]
@functools.cache
def _get_mhc_ops() -> MhcOps:
"""Load MHC kernels only when a DeepSeek-V4 layer needs them.
Model modules are imported eagerly by the registry. Importing
``sglang.kernels.ops.layernorm.mhc`` owns TileLang-backed MHC kernels.
Import it only when a DeepSeek-V4 layer executes so registry discovery
cannot initialize an optional CUDA runtime before unrelated models set up
their communication workspaces. DeepSeek-V4 is the sole consumer here.
"""
if _is_xpu:
from sgl_kernel import hc_split_sinkhorn
return MhcOps(hc_split_sinkhorn, None, None)
from sglang.kernels.ops.layernorm.mhc import (
hc_split_sinkhorn,
mhc_fused_post_pre,
npu_hc_pre,
)
return MhcOps(hc_split_sinkhorn, mhc_fused_post_pre, npu_hc_pre)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_FP8_WO_A_GEMM = envs.SGLANG_OPT_FP8_WO_A_GEMM.get() _FP8_WO_A_GEMM = envs.SGLANG_OPT_FP8_WO_A_GEMM.get()
@@ -1349,7 +1375,7 @@ class DeepseekV4DecoderLayer(nn.Module):
shape, dtype = x.size(), x.dtype shape, dtype = x.size(), x.dtype
if _is_npu: if _is_npu:
return npu_hc_pre( return _get_mhc_ops().npu_hc_pre(
x, x,
hc_fn, hc_fn,
hc_scale, hc_scale,
@@ -1426,7 +1452,7 @@ class DeepseekV4DecoderLayer(nn.Module):
else: else:
x_flat, mixes = hc_pre_torch_impl(x, hc_fn) x_flat, mixes = hc_pre_torch_impl(x, hc_fn)
pre, post, comb = hc_split_sinkhorn( pre, post, comb = _get_mhc_ops().hc_split_sinkhorn(
mixes, mixes,
hc_scale, hc_scale,
hc_base, hc_base,
@@ -1497,7 +1523,7 @@ class DeepseekV4DecoderLayer(nn.Module):
use_fused = self.use_fused_mhc_post_pre use_fused = self.use_fused_mhc_post_pre
if prev_residual is not None and use_fused: if prev_residual is not None and use_fused:
residual, post, comb, hidden_states = mhc_fused_post_pre( residual, post, comb, hidden_states = _get_mhc_ops().mhc_fused_post_pre(
hidden_states, hidden_states,
prev_residual, prev_residual,
prev_post, prev_post,
@@ -1567,7 +1593,7 @@ class DeepseekV4DecoderLayer(nn.Module):
if fused_mhc is not None: if fused_mhc is not None:
residual, hidden_states, post, comb, norm_fused = fused_mhc residual, hidden_states, post, comb, norm_fused = fused_mhc
else: else:
residual, post, comb, hidden_states = mhc_fused_post_pre( residual, post, comb, hidden_states = _get_mhc_ops().mhc_fused_post_pre(
hidden_states, hidden_states,
residual, residual,
post.unsqueeze(-1) if post.ndim == 2 else post, post.unsqueeze(-1) if post.ndim == 2 else post,
@@ -0,0 +1,38 @@
import io
import pytest
from sglang.test.ci.ci_register import register_cuda_ci
register_cuda_ci(est_time=2, stage="base-b", runner_config="1-gpu-small")
from sglang.srt.distributed.device_communicators import cuda_wrapper
def test_find_loaded_library_prefers_real_cudart_over_tilelang_stub(monkeypatch):
maps = """\
7f000000-7f010000 r-xp 00000000 00:00 0 /site-packages/tilelang/lib/libcudart_stub.so
7f020000-7f030000 r-xp 00000000 00:00 0 /cuda/lib64/libcudart.so.13
"""
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: io.StringIO(maps))
assert (
cuda_wrapper.find_loaded_library("libcudart") == "/cuda/lib64/libcudart.so.13"
)
def test_find_loaded_library_strips_deleted_suffix(monkeypatch):
maps = """\
7f020000-7f030000 r-xp 00000000 00:00 0 /cuda/lib64/libcudart.so.13 (deleted)
"""
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: io.StringIO(maps))
assert (
cuda_wrapper.find_loaded_library("libcudart") == "/cuda/lib64/libcudart.so.13"
)
if __name__ == "__main__":
raise SystemExit(pytest.main([__file__, "-v"]))