执行摘要
- 一句话:XPU DP改用rank偏移分配设备,移除环境变量隔离
- 推荐动作:建议XPU后端开发者与评审仔细阅读,此PR是重要的正确性修复。lazy init模式值得借鉴;未来可考虑将activities提取为父类方法以统一profile实现。
功能与动机
原有XPU DP依赖ZE_AFFINITY_MASK限制每个worker可见设备,但DP+EP时EP需要看到全部设备,导致设备号错乱。此PR改用与GPU worker一致的方式:在init_device中根据DP local rank和TP/PP world size调整local_rank,从而每个worker使用独立的xpu设备号。见PR body及评论。
实现拆解
- 在
XPUWorker.init_device中,当满足条件(非ray/external_launcher后端且nnodes_within_dp==1)时,通过data_parallel_rank_local或data_parallel_index获取DP local rank,乘以tp_pp_world_size后累加到self.local_rank上,并添加边界断言。
- 将profiler初始化从
__init__移至新增的profile方法中,采用lazy init模式:仅在profile(is_start=True)且self.profiler is None时创建TorchProfilerWrapper实例,最后调用父类profile。
- 在
vllm/v1/engine/utils.py中,将DP设备环境隔离条件由not current_platform.is_cuda_alike()扩展为not (current_platform.is_cuda_alike() or current_platform.is_xpu()),即XPU与CUDA一样使用torch.accelerator.set_device_index而非环境变量隔离。
- 无自动化测试新增,但作者提供手动命令行验证。
关键文件:
vllm/v1/worker/xpu_worker.py(模块 XPU工作器;类别 source;类型 core-logic;符号 init_device, profile, init): 主要变更文件:实现DP rank调整、lazy profiler初始化、profile方法重写
vllm/v1/engine/utils.py(模块 引擎工具;类别 source;类型 core-logic): 调整DP设备分配条件,将XPU排除在环境变量隔离之外
关键符号:init_device, profile
关键源码片段
vllm/v1/worker/xpu_worker.py
主要变更文件:实现DP rank调整、lazy profiler初始化、profile方法重写
# xpu_worker.py - init_device 中 DP rank 调整段
def init_device(self):
# 在 DP 模式下,XPU workers 能看到所有可见设备。
# 通过 DP local rank 偏移 local_rank 来分配不同设备。
parallel_config = self.parallel_config
if (
parallel_config.distributed_executor_backend not in ("ray", "external_launcher")
and parallel_config.data_parallel_backend != "ray"
and parallel_config.nnodes_within_dp == 1
):
dp_local_rank = parallel_config.data_parallel_rank_local
if dp_local_rank is None:
dp_local_rank = parallel_config.data_parallel_index
tp_pp_world_size = (
parallel_config.pipeline_parallel_size
* parallel_config.tensor_parallel_size
)
# 核心:将 DP 偏移加到 local_rank 上
self.local_rank += dp_local_rank * tp_pp_world_size
visible_device_count = torch.accelerator.device_count()
assert self.local_rank < visible_device_count, (
f"DP adjusted local rank {self.local_rank} is out of bounds. "
)
# ... 后续设置设备
# xpu_worker.py - profile 方法(lazy init 模式)
def profile(self, is_start: bool = True, profile_prefix: str | None = None):
# 确保 profiler 已配置,否则抛异常
if self.profiler_config is None or self.profiler_config.profiler is None:
raise RuntimeError(
"Profiling is not enabled. Please set --profiler-config to enable "
"profiling. ..."
)
# lazy 初始化:只有在第一次 start 时才创建 profiler
if is_start and self.profiler is None:
from vllm.distributed.utils import get_worker_rank_suffix
rank_suffix = get_worker_rank_suffix(global_rank=self.rank)
trace_name = (
f"{profile_prefix}_{rank_suffix}" if profile_prefix else rank_suffix
)
self.profiler = TorchProfilerWrapper(
self.profiler_config,
worker_name=trace_name,
local_rank=self.local_rank,
activities=["CPU", "XPU"],
)
# 调用父类进行实际的 start / stop
super().profile(is_start=is_start, profile_prefix=profile_prefix)
评论区精华
- 正确性bug:gemini-code-assist[bot]指出
local_rank在init_device中调整后profiler已在构造函数初始化,导致profiler设备不匹配。作者将profiler改为lazy init解决。
- 设计讨论:jikunshang建议复用父类
profile方法,作者指出需提取activities列表,当前保留override,未来可提炼。
- 代码简化:jikunshang建议
utils.py中条件写法更清晰,作者接受并更新。
- profiler初始化时机 (correctness): 作者将profiler初始化从__init__移除,改为在profile方法中lazy init,解决不匹配问题
- 复用父类profile方法 (design): 作者表示需将activities提取为父类方法才能复用,当前保留override,未来可重构
- utils.py条件简化 (style): 作者接受建议并更新代码
风险与影响
- 风险:
- profiler lazy init:若
profile方法不被调用,profiler不会被创建,但现有调用路径均通过外部触发,风险低。
- DP rank调整条件排除了ray/external_launcher后端及
nnodes_within_dp != 1场景,这些场景仍使用旧环境变量隔离,可能引起不一致。
- 缺少单元测试覆盖新的rank计算逻辑,未来修改可能退化。
- 用户若依赖旧ZE_AFFINITY_MASK行为可能受影响,但新行为与GPU worker一致,更符合预期。
- 影响:影响仅限XPU平台上使用DP(可能结合EP)的用户,修复了设备分配错误。不使用DP或仅CUDA用户无影响。团队维护成本降低,因新方式复用GPU worker设计。
- 风险标记:缺少测试覆盖, profiler延迟初始化风险, DP rank假设条件敏感
关联脉络
参与讨论