Prhub

#44823 [ROCm][CI] Defer AITER sampler import and isolate server test PYTHONPATH

原始 PR 作者 AndreasKaratzas 合并时间 2026-06-10 16:56 文件变更 3 提交数 3 评论 5 代码增减 +117 / -17

执行摘要

延迟 AITER sampler 导入并隔离测试子进程 PYTHONPATH 修复 ROCm CI 崩溃

在 AMD MI300 上的 OpenAI API server 测试中,vLLM 启用 AITER sampler,但某些请求使用 per-request generators(AITER 不支持)。即使在回退到 native 之前,AITER 导入时 TileLang 会修改进程 PYTHONPATH(在其 vendored TVM 路径前添加),导致长期运行的 pytest worker 中后续子进程继承被污染的环境并因缺少 encodings 模块而崩溃。此 PR 将 AITER 导入延迟到确认请求可使用 AITER 之后,同时隔离子进程环境,避免 TileLang 的进程级别副作用。

建议精读本 PR,尤其是延迟导入与 TileLang 副作用的权衡讨论。对于 ROCm 开发者,理解 forward_hip 中的延迟初始化模式可帮助避免类似库导入副作用。tests/utils.py 中的 PYTHONPATH 清理函数可作为处理环境隔离问题的参考模式。

讨论亮点

在 Review 中,tjtanaa 建议将 AITER 导入放在 __init__ 中以保持逻辑清晰("we should do this in the init and keep the logic clean")。AndreasKaratzas 解释若在 __init__ 中导入,即使请求因 per-request generators 无需 AITER 也会触发 TileLang 副作用,造成环境污染,因此必须延迟到 forward_hip 中按需导入。tjtanaa 最终接受了该方案,但表达了对更干净初始化方法的偏好("Let's go with this for now. But I still prefer a cleaner approach...")。

实现拆解

  1. 延迟 AITER sampler 导入:在 vllm/v1/sample/ops/topk_topp_sampler.py 中,将 __init__ 中的 import aiter.ops.sampling 移入新增的 _init_aiter_ops() 方法。__init__ 仅设置 self.aiter_ops = None 和失败标记,并选择 forward_hip 作为前向方法。forward_hip 首先检查是否落入回退场景(如 per-request generators),若是则直接调用 forward_native;否则当首次需要 AITER 时调用 _init_aiter_ops() 并缓存结果,确保只在确需使用 AITER 时才触发 TileLang 的副作用。
  2. 添加 PYTHONPATH 清理工具:在 tests/utils.py 中定义 _TILELANG_TVM_PYTHONPATH_FRAGMENT 常量,并实现 _sanitize_pythonpath_value_sanitize_pythonpath_env_sanitize_current_pythonpath_env_temporarily_sanitized_pythonpath_env 上下文管理器,用于从 PYTHONPATH 中移除 TileLang 的 vendored TVM 路径片段。
  3. 隔离服务器子进程环境:修改 RemoteVLLMServer._start_server,在构造子进程 env 时调用 _sanitize_pythonpath_env(env) 清理环境;在 __init__ 中的模型预下载阶段和 _run_in_new_process_groupproc.start() 前后使用 _temporarily_sanitized_pythonpath_env 保护当前进程环境,并确保退出时恢复。
  4. 新增测试验证延迟导入:在 tests/v1/sample/test_topk_topp_sampler.py 中添加 test_rocm_aiter_sampler_defers_import_when_generators_force_native 测试,使用 monkeypatch 模拟 ROCm 平台和 AITER 启用,但设置 per-request generators,通过守卫 __import__ 验证 aiter.ops.sampling 未被导入。
文件 模块 状态 重要度
vllm/v1/sample/ops/topk_topp_sampler.py 采样器 modified 6.19
tests/utils.py 测试工具 modified 6.97
tests/v1/sample/test_topk_topp_sampler.py 采样器测试 modified 6.25

关键符号

_init_aiter_ops forward_hip _sanitize_pythonpath_value _sanitize_pythonpath_env _temporarily_sanitized_pythonpath_env test_rocm_aiter_sampler_defers_import_when_generators_force_native

关键源码片段

vllm/v1/sample/ops/topk_topp_sampler.py core-logic

核心变更文件,实现 AITER sampler 的延迟导入逻辑以避免 TileLang 副作用。

class TopKTopPSampler:
    def __init__(self, ...):
        # ... 其他初始化 ...
        # 不再在 __init__ 中导入 aiter.ops.sampling
        if rocm_aiter_ops.is_enabled():
            # 仅设置状态,不触发导入
            self.aiter_ops = None
            self._aiter_ops_import_failed = False
            logger.info_once("Using aiter sampler on ROCm (lazy import, sampling-only).")
            self.forward = self.forward_hip # 选择 forward_hip 作为前向方法
        else:
            self.forward = self.forward_native
​
    def _init_aiter_ops(self) -> bool:
        """按需导入 AITER 操作。若之前导入失败则直接返回 False。"""
        if self._aiter_ops_import_failed:
            return False
        try:
            import aiter.ops.sampling # noqa: F401
        except ImportError:
            self._aiter_ops_import_failed = True
            self.forward = self.forward_native
            logger.warning_once("aiter.ops.sampling is not available on ROCm. "
                                "Falling back to PyTorch-native implementation.")
            return False
        self.aiter_ops = torch.ops.aiter
        return True
​
    def forward_hip(self, logits, generators, k, p):
        """ROCm 上的采样前向,支持延迟回退到 native。"""
        # 如果请求使用 per-request generators,AITER 不支持,直接回退
        if generators:
            return self.forward_native(logits, generators, k, p)
        # ... 其他回退条件(fp64 gumbel, top-k/top-p 无工作等)...
        if self.aiter_ops is None and not self._init_aiter_ops():
            return self.forward_native(logits, generators, k, p)
        # 此处确保 aiter_ops 可用
        return self.aiter_sample(logits, k, p, generators), None
tests/utils.py infrastructure

新增 PYTHONPATH 清理工具并修改 RemoteVLLMServer 以隔离子进程环境,是修复的另一关键部分。

# TileLang 在导入时会将以下路径添加到 PYTHONPATH 前端
_TILELANG_TVM_PYTHONPATH_FRAGMENT = os.path.join("tilelang", "3rdparty", "tvm", "python")def _sanitize_pythonpath_value(pythonpath: str | None) -> str:
    """从 PYTHONPATH 值中移除 TileLang TVM 路径片段。"""
    if not pythonpath:
        return ""
    entries = []
    for entry in pythonpath.split(os.pathsep):
        normalized = entry.replace(os.sep, "/")
        if _TILELANG_TVM_PYTHONPATH_FRAGMENT.replace(os.sep, "/") in normalized:
            continue
        entries.append(entry)
    return os.pathsep.join(entries)def _sanitize_pythonpath_env(env: MutableMapping[str, str]) -> None:
    """清理环境变量字典中的 PYTHONPATH。"""
    cleaned = _sanitize_pythonpath_value(env.get("PYTHONPATH"))
    if cleaned:
        env["PYTHONPATH"] = cleaned
    else:
        env.pop("PYTHONPATH", None)@contextmanager
def _temporarily_sanitized_pythonpath_env():
    """临时清理当前进程的 PYTHONPATH,退出时恢复。"""
    original = os.environ.get("PYTHONPATH")
    _sanitize_current_pythonpath_env() # 清理当前环境
    try:
        yield
    finally:
        if original is None:
            os.environ.pop("PYTHONPATH", None)
        else:
            os.environ["PYTHONPATH"] = original# 在 RemoteVLLMServer 中使用
class RemoteVLLMServer:
    def __init__(self, ...):
        # ...
        with _temporarily_sanitized_pythonpath_env():
            self._pre_download_model(model, args) # 避免 TileLang 副作用污染预下载环境
        # ...
    def _start_server(self, ...):
        # ...
        _sanitize_pythonpath_env(env) # 清理子进程环境
        serve_cmd = [...]
        self.proc = subprocess.Popen(serve_cmd, env=env, ...)

评论区精华

延迟导入位置讨论 设计

tjtanaa 建议在 __init__ 中完成所有初始化以保持逻辑清晰。AndreasKaratzas 解释若在 __init__ 中导入会触发 TileLang 副作用,即使请求回退到 native 也会污染环境。

结论:接受延迟导入方案,但 tjtanaa 仍偏好更干净的初始化方法。 · 已解决

风险与影响

主要风险是 forward_hip 中每次调用需检查 self.aiter_ops is None,但该检查仅首次或失败时执行,后续直接使用缓存,性能影响可忽略。可能遗漏需要 AITER 的场景导致始终回退到 native(测试已覆盖典型路径)。PYTHONPATH 清理逻辑可能误删用户期望的路径(包含 'tilelang/3rdparty/tvm/python' 子串),但概率极低且影响可控。TileLang 副作用的根本解决依赖于 TileLang 自身改进,当前方案是局部缓解。

主要影响 ROCm 平台用户,尤其是使用 AITER sampler 且存在 per-request generators 的场景。该 PR 修复了可能导致 server 启动崩溃的问题,提升了测试稳定性。非 ROCm 平台无影响。tests/utils.py 中的 PYTHONPATH 清理工具可被其他测试模块复用,增强测试基础设施健壮性。

延迟导入引入条件分支 TileLang 副作用隐性依赖 子进程环境隔离依赖正确清理逻辑

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论