[Ascend] Add Ascend NPU support for sglang.check_env & rework proposal (#11052)
Co-authored-by: ronnie_zheng <zl19940307@163.com>
This commit is contained in:
co-authored by
ronnie_zheng
parent
086f0b79fc
commit
c550ab9125
+284
-164
@@ -5,11 +5,12 @@ import os
|
|||||||
import resource
|
import resource
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
from abc import abstractmethod
|
||||||
from collections import OrderedDict, defaultdict
|
from collections import OrderedDict, defaultdict
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from sglang.srt.utils import is_hip
|
from sglang.srt.utils import is_hip, is_npu
|
||||||
|
|
||||||
|
|
||||||
def is_cuda_v2():
|
def is_cuda_v2():
|
||||||
@@ -51,104 +52,124 @@ PACKAGE_LIST = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_package_versions(packages):
|
class BaseEnv:
|
||||||
"""
|
"""Base class for environment check"""
|
||||||
Get versions of specified packages.
|
|
||||||
"""
|
def __init__(self):
|
||||||
versions = {}
|
self.package_list = PACKAGE_LIST
|
||||||
for package in packages:
|
|
||||||
package_name = package.split("==")[0].split(">=")[0].split("<=")[0]
|
@abstractmethod
|
||||||
|
def get_info(self) -> dict:
|
||||||
|
"""
|
||||||
|
Get CUDA-related information if available.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_topology(self) -> dict:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_package_versions(self) -> dict:
|
||||||
|
"""
|
||||||
|
Get versions of specified packages.
|
||||||
|
"""
|
||||||
|
versions = {}
|
||||||
|
for package in self.package_list:
|
||||||
|
package_name = package.split("==")[0].split(">=")[0].split("<=")[0]
|
||||||
|
try:
|
||||||
|
version = importlib.metadata.version(package_name)
|
||||||
|
versions[package_name] = version
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
versions[package_name] = "Module Not Found"
|
||||||
|
return versions
|
||||||
|
|
||||||
|
def get_device_info(self):
|
||||||
|
"""
|
||||||
|
Get information about available GPU devices.
|
||||||
|
"""
|
||||||
|
devices = defaultdict(list)
|
||||||
|
capabilities = defaultdict(list)
|
||||||
|
for k in range(torch.cuda.device_count()):
|
||||||
|
devices[torch.cuda.get_device_name(k)].append(str(k))
|
||||||
|
capability = torch.cuda.get_device_capability(k)
|
||||||
|
capabilities[f"{capability[0]}.{capability[1]}"].append(str(k))
|
||||||
|
|
||||||
|
gpu_info = {}
|
||||||
|
for name, device_ids in devices.items():
|
||||||
|
gpu_info[f"GPU {','.join(device_ids)}"] = name
|
||||||
|
|
||||||
|
if len(capabilities) == 1:
|
||||||
|
# All GPUs have the same compute capability
|
||||||
|
cap, gpu_ids = list(capabilities.items())[0]
|
||||||
|
gpu_info[f"GPU {','.join(gpu_ids)} Compute Capability"] = cap
|
||||||
|
else:
|
||||||
|
# GPUs have different compute capabilities
|
||||||
|
for cap, gpu_ids in capabilities.items():
|
||||||
|
gpu_info[f"GPU {','.join(gpu_ids)} Compute Capability"] = cap
|
||||||
|
|
||||||
|
return gpu_info
|
||||||
|
|
||||||
|
def get_hypervisor_vendor(self) -> dict:
|
||||||
try:
|
try:
|
||||||
version = importlib.metadata.version(package_name)
|
output = subprocess.check_output(["lscpu"], text=True)
|
||||||
versions[package_name] = version
|
for line in output.split("\n"):
|
||||||
except ModuleNotFoundError:
|
if "Hypervisor vendor:" in line:
|
||||||
versions[package_name] = "Module Not Found"
|
return {"Hypervisor vendor:": line.split(":")[1].strip()}
|
||||||
return versions
|
return {}
|
||||||
|
except:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_ulimit_soft(self) -> dict:
|
||||||
|
ulimit_soft, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
|
||||||
|
return {"ulimit soft": ulimit_soft}
|
||||||
|
|
||||||
|
def check_env(self):
|
||||||
|
"""
|
||||||
|
Check and print environment information.
|
||||||
|
"""
|
||||||
|
env_info = OrderedDict()
|
||||||
|
env_info["Python"] = sys.version.replace("\n", "")
|
||||||
|
env_info.update(self.get_info())
|
||||||
|
env_info["PyTorch"] = torch.__version__
|
||||||
|
env_info.update(self.get_package_versions())
|
||||||
|
env_info.update(self.get_topology())
|
||||||
|
env_info.update(self.get_hypervisor_vendor())
|
||||||
|
env_info.update(self.get_ulimit_soft())
|
||||||
|
|
||||||
|
for k, v in env_info.items():
|
||||||
|
print(f"{k}: {v}")
|
||||||
|
|
||||||
|
|
||||||
def get_cuda_info():
|
class GPUEnv(BaseEnv):
|
||||||
"""
|
"""Environment checker for Nvidia GPU"""
|
||||||
Get CUDA-related information if available.
|
|
||||||
"""
|
def get_info(self):
|
||||||
if is_cuda_v2():
|
|
||||||
cuda_info = {"CUDA available": torch.cuda.is_available()}
|
cuda_info = {"CUDA available": torch.cuda.is_available()}
|
||||||
|
|
||||||
if cuda_info["CUDA available"]:
|
if cuda_info["CUDA available"]:
|
||||||
cuda_info.update(_get_gpu_info())
|
cuda_info.update(self.get_device_info())
|
||||||
cuda_info.update(_get_cuda_version_info())
|
cuda_info.update(self._get_cuda_version_info())
|
||||||
|
|
||||||
return cuda_info
|
|
||||||
elif is_hip():
|
|
||||||
cuda_info = {"ROCM available": torch.cuda.is_available()}
|
|
||||||
|
|
||||||
if cuda_info["ROCM available"]:
|
|
||||||
cuda_info.update(_get_gpu_info())
|
|
||||||
cuda_info.update(_get_cuda_version_info())
|
|
||||||
|
|
||||||
return cuda_info
|
return cuda_info
|
||||||
|
|
||||||
|
def _get_cuda_version_info(self):
|
||||||
def _get_gpu_info():
|
"""
|
||||||
"""
|
Get CUDA version information.
|
||||||
Get information about available GPUs.
|
"""
|
||||||
"""
|
|
||||||
devices = defaultdict(list)
|
|
||||||
capabilities = defaultdict(list)
|
|
||||||
for k in range(torch.cuda.device_count()):
|
|
||||||
devices[torch.cuda.get_device_name(k)].append(str(k))
|
|
||||||
capability = torch.cuda.get_device_capability(k)
|
|
||||||
capabilities[f"{capability[0]}.{capability[1]}"].append(str(k))
|
|
||||||
|
|
||||||
gpu_info = {}
|
|
||||||
for name, device_ids in devices.items():
|
|
||||||
gpu_info[f"GPU {','.join(device_ids)}"] = name
|
|
||||||
|
|
||||||
if len(capabilities) == 1:
|
|
||||||
# All GPUs have the same compute capability
|
|
||||||
cap, gpu_ids = list(capabilities.items())[0]
|
|
||||||
gpu_info[f"GPU {','.join(gpu_ids)} Compute Capability"] = cap
|
|
||||||
else:
|
|
||||||
# GPUs have different compute capabilities
|
|
||||||
for cap, gpu_ids in capabilities.items():
|
|
||||||
gpu_info[f"GPU {','.join(gpu_ids)} Compute Capability"] = cap
|
|
||||||
|
|
||||||
return gpu_info
|
|
||||||
|
|
||||||
|
|
||||||
def _get_cuda_version_info():
|
|
||||||
"""
|
|
||||||
Get CUDA version information.
|
|
||||||
"""
|
|
||||||
if is_cuda_v2():
|
|
||||||
from torch.utils.cpp_extension import CUDA_HOME
|
from torch.utils.cpp_extension import CUDA_HOME
|
||||||
|
|
||||||
cuda_info = {"CUDA_HOME": CUDA_HOME}
|
cuda_info = {"CUDA_HOME": CUDA_HOME}
|
||||||
|
|
||||||
if CUDA_HOME and os.path.isdir(CUDA_HOME):
|
if CUDA_HOME and os.path.isdir(CUDA_HOME):
|
||||||
cuda_info.update(_get_nvcc_info())
|
cuda_info.update(self._get_nvcc_info())
|
||||||
cuda_info.update(_get_cuda_driver_version())
|
cuda_info.update(self._get_cuda_driver_version())
|
||||||
|
|
||||||
return cuda_info
|
return cuda_info
|
||||||
elif is_hip():
|
|
||||||
from torch.utils.cpp_extension import ROCM_HOME as ROCM_HOME
|
|
||||||
|
|
||||||
cuda_info = {"ROCM_HOME": ROCM_HOME}
|
def _get_nvcc_info(self):
|
||||||
|
"""
|
||||||
if ROCM_HOME and os.path.isdir(ROCM_HOME):
|
Get NVCC version information.
|
||||||
cuda_info.update(_get_nvcc_info())
|
"""
|
||||||
cuda_info.update(_get_cuda_driver_version())
|
|
||||||
|
|
||||||
return cuda_info
|
|
||||||
else:
|
|
||||||
cuda_info = {"CUDA_HOME": ""}
|
|
||||||
return cuda_info
|
|
||||||
|
|
||||||
|
|
||||||
def _get_nvcc_info():
|
|
||||||
"""
|
|
||||||
Get NVCC version information.
|
|
||||||
"""
|
|
||||||
if is_cuda_v2():
|
|
||||||
from torch.utils.cpp_extension import CUDA_HOME
|
from torch.utils.cpp_extension import CUDA_HOME
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -167,7 +188,73 @@ def _get_nvcc_info():
|
|||||||
}
|
}
|
||||||
except subprocess.SubprocessError:
|
except subprocess.SubprocessError:
|
||||||
return {"NVCC": "Not Available"}
|
return {"NVCC": "Not Available"}
|
||||||
elif is_hip():
|
|
||||||
|
def _get_cuda_driver_version(self):
|
||||||
|
"""
|
||||||
|
Get CUDA driver version.
|
||||||
|
"""
|
||||||
|
versions = set()
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(
|
||||||
|
[
|
||||||
|
"nvidia-smi",
|
||||||
|
"--query-gpu=driver_version",
|
||||||
|
"--format=csv,noheader,nounits",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
versions = set(output.decode().strip().split("\n"))
|
||||||
|
if len(versions) == 1:
|
||||||
|
return {"CUDA Driver Version": versions.pop()}
|
||||||
|
else:
|
||||||
|
return {"CUDA Driver Versions": ", ".join(sorted(versions))}
|
||||||
|
except subprocess.SubprocessError:
|
||||||
|
return {"CUDA Driver Version": "Not Available"}
|
||||||
|
|
||||||
|
def get_topology(self):
|
||||||
|
"""
|
||||||
|
Get GPU topology information.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["nvidia-smi", "topo", "-m"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"NVIDIA Topology": (
|
||||||
|
"\n" + result.stdout if result.returncode == 0 else None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
except subprocess.SubprocessError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class HIPEnv(BaseEnv):
|
||||||
|
"""Environment checker for ROCm/HIP"""
|
||||||
|
|
||||||
|
def get_info(self):
|
||||||
|
cuda_info = {"ROCM available": torch.cuda.is_available()}
|
||||||
|
|
||||||
|
if cuda_info["ROCM available"]:
|
||||||
|
cuda_info.update(self.get_device_info())
|
||||||
|
cuda_info.update(self._get_cuda_version_info())
|
||||||
|
|
||||||
|
return cuda_info
|
||||||
|
|
||||||
|
def _get_cuda_version_info(self):
|
||||||
|
from torch.utils.cpp_extension import ROCM_HOME as ROCM_HOME
|
||||||
|
|
||||||
|
cuda_info = {"ROCM_HOME": ROCM_HOME}
|
||||||
|
|
||||||
|
if ROCM_HOME and os.path.isdir(ROCM_HOME):
|
||||||
|
cuda_info.update(self._get_hipcc_info())
|
||||||
|
cuda_info.update(self._get_rocm_driver_version())
|
||||||
|
|
||||||
|
return cuda_info
|
||||||
|
|
||||||
|
def _get_hipcc_info(self):
|
||||||
from torch.utils.cpp_extension import ROCM_HOME
|
from torch.utils.cpp_extension import ROCM_HOME
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -184,32 +271,8 @@ def _get_nvcc_info():
|
|||||||
}
|
}
|
||||||
except subprocess.SubprocessError:
|
except subprocess.SubprocessError:
|
||||||
return {"HIPCC": "Not Available"}
|
return {"HIPCC": "Not Available"}
|
||||||
else:
|
|
||||||
return {"NVCC": "Not Available"}
|
|
||||||
|
|
||||||
|
def _get_rocm_driver_version(self):
|
||||||
def _get_cuda_driver_version():
|
|
||||||
"""
|
|
||||||
Get CUDA driver version.
|
|
||||||
"""
|
|
||||||
versions = set()
|
|
||||||
if is_cuda_v2():
|
|
||||||
try:
|
|
||||||
output = subprocess.check_output(
|
|
||||||
[
|
|
||||||
"nvidia-smi",
|
|
||||||
"--query-gpu=driver_version",
|
|
||||||
"--format=csv,noheader,nounits",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
versions = set(output.decode().strip().split("\n"))
|
|
||||||
if len(versions) == 1:
|
|
||||||
return {"CUDA Driver Version": versions.pop()}
|
|
||||||
else:
|
|
||||||
return {"CUDA Driver Versions": ", ".join(sorted(versions))}
|
|
||||||
except subprocess.SubprocessError:
|
|
||||||
return {"CUDA Driver Version": "Not Available"}
|
|
||||||
elif is_hip():
|
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(
|
output = subprocess.check_output(
|
||||||
[
|
[
|
||||||
@@ -226,27 +289,8 @@ def _get_cuda_driver_version():
|
|||||||
return {"ROCM Driver Version": ver}
|
return {"ROCM Driver Version": ver}
|
||||||
except subprocess.SubprocessError:
|
except subprocess.SubprocessError:
|
||||||
return {"ROCM Driver Version": "Not Available"}
|
return {"ROCM Driver Version": "Not Available"}
|
||||||
else:
|
|
||||||
return {"CUDA Driver Version": "Not Available"}
|
|
||||||
|
|
||||||
|
def get_topology(self):
|
||||||
def get_gpu_topology():
|
|
||||||
"""
|
|
||||||
Get GPU topology information.
|
|
||||||
"""
|
|
||||||
if is_cuda_v2():
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
["nvidia-smi", "topo", "-m"],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
text=True,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
return "\n" + result.stdout if result.returncode == 0 else None
|
|
||||||
except subprocess.SubprocessError:
|
|
||||||
return None
|
|
||||||
elif is_hip():
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["rocm-smi", "--showtopotype"],
|
["rocm-smi", "--showtopotype"],
|
||||||
@@ -255,51 +299,127 @@ def get_gpu_topology():
|
|||||||
text=True,
|
text=True,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
return "\n" + result.stdout if result.returncode == 0 else None
|
return {
|
||||||
|
"AMD Topology": "\n" + result.stdout if result.returncode == 0 else None
|
||||||
|
}
|
||||||
except subprocess.SubprocessError:
|
except subprocess.SubprocessError:
|
||||||
return None
|
return {}
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_hypervisor_vendor():
|
class NPUEnv(BaseEnv):
|
||||||
try:
|
"""Environment checker for Ascend NPU"""
|
||||||
output = subprocess.check_output(["lscpu"], text=True)
|
|
||||||
for line in output.split("\n"):
|
|
||||||
if "Hypervisor vendor:" in line:
|
|
||||||
return line.split(":")[1].strip()
|
|
||||||
return None
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.package_list = ["torch_npu", "sgl-kernel-npu"] + self.package_list
|
||||||
|
|
||||||
def check_env():
|
def get_info(self):
|
||||||
"""
|
cuda_info = {"NPU available": torch.npu.is_available()}
|
||||||
Check and print environment information.
|
if cuda_info["NPU available"]:
|
||||||
"""
|
cuda_info.update(self.get_device_info())
|
||||||
env_info = OrderedDict()
|
cuda_info.update(self._get_cann_version_info())
|
||||||
env_info["Python"] = sys.version.replace("\n", "")
|
|
||||||
env_info.update(get_cuda_info())
|
|
||||||
env_info["PyTorch"] = torch.__version__
|
|
||||||
env_info.update(get_package_versions(PACKAGE_LIST))
|
|
||||||
|
|
||||||
gpu_topo = get_gpu_topology()
|
return cuda_info
|
||||||
if gpu_topo:
|
|
||||||
if is_cuda_v2():
|
|
||||||
env_info["NVIDIA Topology"] = gpu_topo
|
|
||||||
elif is_hip():
|
|
||||||
env_info["AMD Topology"] = gpu_topo
|
|
||||||
|
|
||||||
hypervisor_vendor = get_hypervisor_vendor()
|
def get_device_info(self):
|
||||||
if hypervisor_vendor:
|
"""
|
||||||
env_info["Hypervisor vendor"] = hypervisor_vendor
|
Get information about available NPUs.
|
||||||
|
Need to override due to torch_npu interface differences.
|
||||||
|
"""
|
||||||
|
devices = defaultdict(list)
|
||||||
|
for k in range(torch.npu.device_count()):
|
||||||
|
devices[torch.npu.get_device_name(k)].append(str(k))
|
||||||
|
|
||||||
ulimit_soft, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
|
npu_info = {}
|
||||||
env_info["ulimit soft"] = ulimit_soft
|
for name, device_ids in devices.items():
|
||||||
|
npu_info[f"NPU {','.join(device_ids)}"] = name
|
||||||
|
|
||||||
for k, v in env_info.items():
|
return npu_info
|
||||||
print(f"{k}: {v}")
|
|
||||||
|
def _get_cann_version_info(self):
|
||||||
|
cann_envs = ["ASCEND_TOOLKIT_HOME", "ASCEND_INSTALL_PATH"]
|
||||||
|
for var in cann_envs:
|
||||||
|
path = os.environ.get(var)
|
||||||
|
if path and os.path.exists(path):
|
||||||
|
CANN_HOME = path
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
default_path = "/usr/local/Ascend/ascend-toolkit/latest"
|
||||||
|
CANN_HOME = default_path if os.path.exists(default_path) else None
|
||||||
|
|
||||||
|
if CANN_HOME:
|
||||||
|
npu_info = {"CANN_HOME": CANN_HOME}
|
||||||
|
npu_info.update(self._get_cann_info(CANN_HOME))
|
||||||
|
npu_info.update(self._get_ascend_driver_version())
|
||||||
|
return npu_info
|
||||||
|
else:
|
||||||
|
return {"CANN_HOME": "Not found"}
|
||||||
|
|
||||||
|
def _get_cann_info(self, CANN_HOME: str):
|
||||||
|
cann_info = {}
|
||||||
|
cann_version_file = os.path.join(CANN_HOME, "version.cfg")
|
||||||
|
if os.path.exists(cann_version_file):
|
||||||
|
with open(cann_version_file, "r", encoding="utf-8") as f:
|
||||||
|
f.readline() # discard first line comment in version.cfg
|
||||||
|
cann_info["CANN"] = f.readline().split("[")[1].split("]")[0]
|
||||||
|
else:
|
||||||
|
cann_info["CANN"] = "Not Available"
|
||||||
|
try:
|
||||||
|
bisheng = os.path.join(CANN_HOME, "compiler/ccec_compiler/bin/bisheng")
|
||||||
|
bisheng_output = (
|
||||||
|
subprocess.check_output([bisheng, "--version"]).decode("utf-8").strip()
|
||||||
|
)
|
||||||
|
cann_info["BiSheng"] = bisheng_output.split("\n")[0].strip()
|
||||||
|
except subprocess.SubprocessError:
|
||||||
|
cann_info["BiSheng"] = "Not Available"
|
||||||
|
return cann_info
|
||||||
|
|
||||||
|
def _get_ascend_driver_version(self):
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(
|
||||||
|
[
|
||||||
|
"npu-smi",
|
||||||
|
"info",
|
||||||
|
"-t",
|
||||||
|
"board",
|
||||||
|
"-i",
|
||||||
|
"0",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for line in output.decode().strip().split("\n"):
|
||||||
|
if "Software Version" in line:
|
||||||
|
version = line.split(":")[-1].strip()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
version = "Not Available"
|
||||||
|
|
||||||
|
return {"Ascend Driver Version": version}
|
||||||
|
except subprocess.SubprocessError:
|
||||||
|
return {"Ascend Driver Version": "Not Available"}
|
||||||
|
|
||||||
|
def get_topology(self):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["npu-smi", "info", "-t", "topo"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"Ascend Topology": (
|
||||||
|
"\n" + result.stdout if result.returncode == 0 else None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
except subprocess.SubprocessError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
check_env()
|
if is_cuda_v2():
|
||||||
|
env = GPUEnv()
|
||||||
|
elif is_hip():
|
||||||
|
env = HIPEnv()
|
||||||
|
elif is_npu():
|
||||||
|
env = NPUEnv()
|
||||||
|
env.check_env()
|
||||||
|
|||||||
Reference in New Issue
Block a user