187 lines
6.2 KiB
Python
187 lines
6.2 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# Adapted from vllm: https://github.com/vllm-project/vllm/blob/v0.7.3/vllm/platforms/__init__.py
|
|
|
|
import traceback
|
|
from typing import TYPE_CHECKING
|
|
|
|
from fastvideo.logger import init_logger
|
|
# imported by other files, do not remove
|
|
from fastvideo.platforms.interface import AttentionBackendEnum # noqa: F401
|
|
from fastvideo.platforms.interface import Platform, PlatformEnum
|
|
from fastvideo.utils import resolve_obj_by_qualname
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
|
|
def cuda_platform_plugin() -> str | None:
|
|
is_cuda = False
|
|
|
|
try:
|
|
from fastvideo.utils import import_pynvml
|
|
pynvml = import_pynvml() # type: ignore[no-untyped-call]
|
|
pynvml.nvmlInit()
|
|
try:
|
|
# NOTE: Edge case: fastvideo cpu build on a GPU machine.
|
|
# Third-party pynvml can be imported in cpu build,
|
|
# we need to check if fastvideo is built with cpu too.
|
|
# Otherwise, fastvideo will always activate cuda plugin
|
|
# on a GPU machine, even if in a cpu build.
|
|
is_cuda = (pynvml.nvmlDeviceGetCount() > 0)
|
|
finally:
|
|
pynvml.nvmlShutdown()
|
|
except Exception as e:
|
|
if "nvml" not in e.__class__.__name__.lower():
|
|
# If the error is not related to NVML, re-raise it.
|
|
raise e
|
|
|
|
# CUDA is supported on Jetson, but NVML may not be.
|
|
import os
|
|
|
|
def cuda_is_jetson() -> bool:
|
|
return os.path.isfile("/etc/nv_tegra_release") \
|
|
or os.path.exists("/sys/class/tegra-firmware")
|
|
|
|
if cuda_is_jetson():
|
|
is_cuda = True
|
|
if is_cuda:
|
|
logger.info("CUDA is available")
|
|
|
|
return "fastvideo.platforms.cuda.CudaPlatform" if is_cuda else None
|
|
|
|
|
|
def mps_platform_plugin() -> str | None:
|
|
"""Detect if MPS (Metal Performance Shaders) is available on macOS."""
|
|
is_mps = False
|
|
|
|
try:
|
|
import torch
|
|
if torch.backends.mps.is_available():
|
|
is_mps = True
|
|
logger.info("MPS (Metal Performance Shaders) is available")
|
|
except Exception as e:
|
|
logger.info("MPS detection failed: %s", e)
|
|
|
|
return "fastvideo.platforms.mps.MpsPlatform" if is_mps else None
|
|
|
|
|
|
def npu_platform_plugin() -> str | None:
|
|
is_npu = False
|
|
|
|
try:
|
|
import torch
|
|
# 导入 torch_npu 以初始化 NPU 后端
|
|
import torch_npu # noqa: F401
|
|
if torch.npu.is_available():
|
|
is_npu = True
|
|
logger.info("NPU is available")
|
|
except ImportError:
|
|
logger.error("NPU detection failed: PyTorch or PyTorch_NPU is not installed")
|
|
except AttributeError:
|
|
logger.error("NPU detection failed: PyTorch has no 'npu' attribute (use Ascend-adapted PyTorch)")
|
|
except Exception as e:
|
|
logger.error("NPU detection failed: unknown error - %s", str(e))
|
|
|
|
return "fastvideo.platforms.npu.NPUPlatform" if is_npu else None
|
|
|
|
|
|
def cpu_platform_plugin() -> str | None:
|
|
"""Detect if CPU platform should be used."""
|
|
# CPU is always available as a fallback
|
|
return "fastvideo.platforms.cpu.CpuPlatform"
|
|
|
|
|
|
def rocm_platform_plugin() -> str | None:
|
|
is_rocm = False
|
|
|
|
try:
|
|
import amdsmi
|
|
amdsmi.amdsmi_init()
|
|
try:
|
|
if len(amdsmi.amdsmi_get_processor_handles()) > 0:
|
|
is_rocm = True
|
|
logger.info("ROCm platform is available")
|
|
finally:
|
|
amdsmi.amdsmi_shut_down()
|
|
except Exception as e:
|
|
logger.debug("ROCm platform is unavailable: %s", e)
|
|
|
|
return "fastvideo.platforms.rocm.RocmPlatform" if is_rocm else None
|
|
|
|
|
|
builtin_platform_plugins = {
|
|
'cuda': cuda_platform_plugin,
|
|
'rocm': rocm_platform_plugin,
|
|
'mps': mps_platform_plugin,
|
|
'cpu': cpu_platform_plugin,
|
|
'npu': npu_platform_plugin,
|
|
}
|
|
|
|
|
|
def resolve_current_platform_cls_qualname() -> str:
|
|
# TODO(will): if we need to support other platforms, we should consider if
|
|
# vLLM's plugin architecture is suitable for our needs.
|
|
|
|
# Try MPS first on macOS
|
|
platform_cls_qualname = mps_platform_plugin()
|
|
if platform_cls_qualname is not None:
|
|
return platform_cls_qualname
|
|
|
|
# Fall back to ROCm
|
|
platform_cls_qualname = rocm_platform_plugin()
|
|
if platform_cls_qualname is not None:
|
|
return platform_cls_qualname
|
|
|
|
# Fall back to CUDA
|
|
platform_cls_qualname = cuda_platform_plugin()
|
|
if platform_cls_qualname is not None:
|
|
return platform_cls_qualname
|
|
|
|
# Fall back to NPU
|
|
platform_cls_qualname = npu_platform_plugin()
|
|
if platform_cls_qualname is not None:
|
|
return platform_cls_qualname
|
|
|
|
# Fall back to CPU as last resort
|
|
platform_cls_qualname = cpu_platform_plugin()
|
|
if platform_cls_qualname is not None:
|
|
return platform_cls_qualname
|
|
|
|
raise RuntimeError("No platform plugin found. Please check your "
|
|
"installation.")
|
|
|
|
|
|
_current_platform = None
|
|
_init_trace: str = ''
|
|
|
|
if TYPE_CHECKING:
|
|
current_platform: Platform
|
|
|
|
|
|
def __getattr__(name: str):
|
|
if name == 'current_platform':
|
|
# lazy init current_platform.
|
|
# 1. out-of-tree platform plugins need `from fastvideo.platforms import
|
|
# Platform` so that they can inherit `Platform` class. Therefore,
|
|
# we cannot resolve `current_platform` during the import of
|
|
# `fastvideo.platforms`.
|
|
# 2. when users use out-of-tree platform plugins, they might run
|
|
# `import fastvideo`, some fastvideo internal code might access
|
|
# `current_platform` during the import, and we need to make sure
|
|
# `current_platform` is only resolved after the plugins are loaded
|
|
# (we have tests for this, if any developer violate this, they will
|
|
# see the test failures).
|
|
global _current_platform
|
|
if _current_platform is None:
|
|
platform_cls_qualname = resolve_current_platform_cls_qualname()
|
|
_current_platform = resolve_obj_by_qualname(platform_cls_qualname)()
|
|
global _init_trace
|
|
_init_trace = "".join(traceback.format_stack())
|
|
return _current_platform
|
|
elif name in globals():
|
|
return globals()[name]
|
|
else:
|
|
raise AttributeError(f"No attribute named '{name}' exists in {__name__}.")
|
|
|
|
|
|
__all__ = ['Platform', 'PlatformEnum', 'current_platform', "_init_trace"]
|