执行摘要
- 一句话:XPU CI 新增 multimodal_gen 触发与 24 GiB 适配扩散套件
- 推荐动作:值得精读,重点看 gpu_cases.py 中"平台能力差异驱动的测试分层":如何按显存预算从共享用例池中裁剪平台子集、如何用
_select_xpu_cases 避免用例定义漂移、为何必须原地 mutate ONE_GPU_CASES 才能让 pytest 参数化生效。对 CI 维护者而言,路径过滤器与 pr-gate 的 changes_exist 兜底设计、perf baseline 播种流程都可直接借鉴。若关注 XPU 扩散推理,denoising.py 的 None vs [] 修复也是 diffusers 集成的一个典型坑。
功能与动机
此前 .github/workflows/pr-test-xpu.yml 的 main_package 路径过滤器使用 !(multimodal_gen) 否定,导致 python/sglang/multimodal_gen/** 下的变更完全不会触发 XPU CI。PR body 明确指出:既有 1-gpu 套件为 1x H100 (80 GiB) 编写,包含 FLUX.1-dev、FLUX.2-dev、Qwen-Image 等大 checkpoint,在 24 GiB 的 Battlemage B580 上必然 OOM(实测 FLUX.2-dev 已分配 23.67 GiB / 23.91 GiB),且 "the CUDA-only FP8 / NVFP4 fast paths cannot rescue those models on XPU"。因此需要一套按显存预算裁剪、带有独立性能基线的 XPU 专用扩散套件。
实现拆解
第 1 步:重构 pr-test-xpu.yml 的过滤与门控
check-changes job 新增 multimodal_gen 路径过滤器(python/sglang/multimodal_gen/**/!(*.md|*.ipynb)、python/pyproject_xpu.toml、工作流自身、docker/xpu.Dockerfile),并保留 main_package 上的 !(multimodal_gen) 否定,与 NPU/MUSA/AMD 工作流形状对齐。
- 输出三个布尔值:
changes_exist(两者取或,含 run-all 模式)、main_package、multimodal_gen;pr-gate 条件从 main_package 改为 changes_exist,避免 multimodal_gen 变更绕过 PR 门禁。
第 2 步:新增 multimodal-gen-test-1-gpu-xpu job
- 运行在
bmg-multigen-models runner 上,if 条件为 multimodal_gen == 'true'。
- 流程:重置 workspace 所有权、checkout、启动 XPU 容器(
xpu_ci_start_container.sh)、安装 pytest/expecttest/ray 等依赖、以 pyproject_xpu.toml 覆盖本地安装、fetch tags 让 setuptools_scm 解析真实版本,最后 docker exec 运行 run_suite.py --suite 1-gpu-xpu。
第 3 步:定义 1-gpu-xpu 测试套件(gpu_cases.py)
- 新增
ONE_GPU_XPU_CASE_IDS:zimage_image_t2i、flux_2_klein_image_t2i、flux_2_klein_base_image_t2i、wan2_1_t2v_1.3b(均为 ~5B 以下、BF16 全驻留 24 GiB 的 checkpoint)。
- 新增
_select_xpu_cases():按 id 从 ONE_GPU_CASES 精确挑选并校验缺失 id,避免复制用例定义造成两处漂移。
- 注册
PARAMETRIZED_CASE_GROUPS["1-gpu-xpu"],复用 test_server_1_gpu.py 做参数化。
- 在
current_platform.is_xpu() 时原地 mutate ONE_GPU_CASES 关闭 run_consistency_check(因为 test_server_1_gpu.py 直接参数化自 ONE_GPU_CASES,只覆盖 ONE_GPU_XPU_CASES 会被 pytest 忽略——这是提交 c141c217 返工后确认的)。
第 4 步:性能基线平台注册(testcase_configs.py + xpu_b60.json)
PERF_BASELINE_FILE_BY_PLATFORM 增加 xpu_b60,别名表只保留 xpu(家族)与 bmg(代号,与 runner label 一致)。
get_perf_baseline_platform() 在 XPU 平台优先返回 xpu_b60,避免套用快约 10 倍的 h100.json 导致误报。
- 新增
xpu_b60.json:含 long_term / pr_test 两套容差、按 stage 与 denoise step 的耗时分布,数据来自 XPU CI run 32736878259(PR #36100 自身)播种。
第 5 步:附带运行时修复(denoising.py)
_prepare_denoising_loop() 中 encoder_hidden_states_image 由直接传 image_embeds 改为 image_embeds if image_embeds else None:T2V 请求下 image_embeds 为 [],而 diffusers 的 Wan transformer 只按 is not None 判断,[] 会误入不存在的图像分支。该修复影响所有平台的 T2V 路径。
测试与部署配套
- 无新增独立单元测试文件,通过 suite 注册 + CI job 提供端到端覆盖;PR 自身触发
PR Test (XPU) 自引用验证,测试计划中列出 4 个参数化用例的通过标准。
关键文件:
.github/workflows/pr-test-xpu.yml(模块 XPU 流水线;类别 infra;类型 infrastructure): 核心 CI 变更:新增 multimodal_gen 路径过滤器与 multimodal-gen-test-1-gpu-xpu 专用 job,pr-gate 改用 changes_exist 兜底,是解锁 XPU 扩散测试通道的入口。
python/sglang/multimodal_gen/test/server/gpu_cases.py(模块 测试用例;类别 test;类型 test-coverage;符号 _select_xpu_cases): 测试套件的设计核心:定义 ONE_GPU_XPU_CASE_IDS、_select_xpu_cases 与 1-gpu-xpu 注册,并处理 XPU 平台一致性校验禁用;承载了显存预算裁剪与平台分层测试的主要决策。
python/sglang/multimodal_gen/test/server/perf_baselines/xpu_b60.json(模块 性能基线;类别 test;类型 test-coverage): 新增 Arc Pro B60 平台的扩散性能基线(+222 行),避免套用快约 10 倍的 h100.json 造成性能门禁误报,是 XPU 扩散 job 可运行的关键配套。
python/sglang/multimodal_gen/test/server/testcase_configs.py(模块 用例配置;类别 test;类型 test-coverage): 性能基线平台注册:把 xpu_b60 纳入 PERF_BASELINE_FILE_BY_PLATFORM 与别名表,get_perf_baseline_platform 在 XPU 平台优先返回 xpu_b60。
python/sglang/multimodal_gen/runtime/pipelines_core/stages/denoising.py(模块 扩散流水线;类别 source;类型 core-logic): 共享运行时小修复:T2V 请求下 encoder_hidden_states_image 传 [] 会骗过 diffusers 的 is not None 守卫误入图像分支,改为 None 后影响所有平台 T2V 路径。
关键符号:_select_xpu_cases, get_perf_baseline_platform, _prepare_denoising_loop, _normalize_perf_baseline_platform
关键源码片段
python/sglang/multimodal_gen/test/server/gpu_cases.py
测试套件的设计核心:定义 ONE_GPU_XPU_CASE_IDS、_select_xpu_cases 与 1-gpu-xpu 注册,并处理 XPU 平台一致性校验禁用;承载了显存预算裁剪与平台分层测试的主要决策。
# Intel Arc Pro B60 只有 24 GiB 显存,仅 ~5B 以下 checkpoint 能全量驻留。
# ONE_GPU_CASES 里更大的 FLUX.1-dev / FLUX.2-dev / Qwen-Image / Hunyuan3D /
# SANA-Video / image-edit 系列会在 24 GiB 上 OOM,且 FP8/NVFP4 量化仅限 CUDA。
ONE_GPU_XPU_CASE_IDS = (
"zimage_image_t2i",
"flux_2_klein_image_t2i",
"flux_2_klein_base_image_t2i",
"wan2_1_t2v_1.3b",
)
def _select_xpu_cases(case_ids: tuple[str, ...]) -> list[DiffusionTestCase]:
# 从 ONE_GPU_CASES 按 id 精确挑选,避免复制用例定义造成两处漂移
cases_by_id = {case.id: case for case in ONE_GPU_CASES}
missing = [case_id for case_id in case_ids if case_id not in cases_by_id]
if missing:
raise RuntimeError(f"Unknown XPU diffusion case(s): {missing}")
return [cases_by_id[case_id] for case_id in case_ids]
# 一致性 GT 图是 H100 生成的;XPU 在 Xe2 上使用不同 attention 内核与 fp 归约,
# 像素级输出必然偏离 H100,SSIM/PSNR 对照 golden 永远失败。
# test_server_1_gpu.py 直接基于 ONE_GPU_CASES 做参数化,因此只能原地修改
# 原列表条目 —— 仅通过 ONE_GPU_XPU_CASES 覆盖会被 pytest 静默忽略。
if current_platform.is_xpu():
_xpu_ids = set(ONE_GPU_XPU_CASE_IDS)
for _i, _case in enumerate(ONE_GPU_CASES):
if _case.id in _xpu_ids and _case.run_consistency_check:
ONE_GPU_CASES[_i] = replace(_case, run_consistency_check=False)
ONE_GPU_XPU_CASES = _select_xpu_cases(ONE_GPU_XPU_CASE_IDS)
python/sglang/multimodal_gen/runtime/pipelines_core/stages/denoising.py
共享运行时小修复:T2V 请求下 encoder_hidden_states_image 传 [] 会骗过 diffusers 的 is not None 守卫误入图像分支,改为 None 后影响所有平台 T2V 路径。
def _prepare_denoising_loop(self, batch: Req, server_args: ServerArgs):
# ... 前序逻辑省略(SP latents 预处理、guidance 构建、pos/neg kwargs 组装)
image_kwargs = self.prepare_extra_func_kwargs(
getattr(self.transformer, "forward", self.transformer),
{
# 传 None(而不是 []):T2V 请求没有图像条件,batch.image_embeds 为 []。
# diffusers 的 Wan transformer 只按 `is not None` 判断是否走图像分支,
# [] 会骗过守卫进入不存在的图像分支;None 才会正确跳过。
# TODO: make sure on-device
"encoder_hidden_states_image": image_embeds if image_embeds else None,
},
)
评论区精华
review 共 3 条评论线程,全部由 mingfeima 提出、作者采纳解决:
- 过滤器形状对齐 NPU/MUSA/AMD:mingfeima 在 pr-test-xpu.yml 的 diff 上指出不应直接删除
!(multimodal_gen) 否定,而是 "prefer to match NPU/MUSA/AMD: keep !(multimodal_gen) on main_package, add a multimodal_gen: paths filter, and a dedicated XPU job that actually runs a diffusion suite",并给出 pr-test-npu.yml 的代码引用。作者在提交 d3852d2 中改为独立过滤器 + 专用 job 方案。
- 性能基线别名过多:mingfeima 对 5 个别名(xpu / xpub60 / arcprob60 / bmg / battlemage)提问 "do we need to use so many aliases? use 1 or 2 should be fine",作者回复 "done and updated" 并在提交 391537a 中精简为
xpu + bmg 两个。
- 提交历史还记录了作者自查返工:c141c217 明确说明此前用
replace() 副本覆盖 run_consistency_check 是无效的,因为 test_server_1_gpu.py 直接引用 ONE_GPU_CASES,这是有价值的工程教训。
- main_package 过滤器是否应保留 !(multimodal_gen) 否定 (design): 作者采纳,提交 d3852d2 改为独立 multimodal_gen 过滤器 + multimodal-gen-test-1-gpu-xpu 专用 job,pr-gate 条件改用 changes_exist 兜底。
- 性能基线平台别名是否过多 (design): 提交 391537a 精简为 xpu(家族)+ bmg(代号,与 runner label bmg-multigen-models 一致)两个别名。
风险与影响
- 风险:
- 共享推理代码变更:denoising.py 的
image_embeds if image_embeds else None 影响所有平台的 T2V 路径(不只 XPU),若某些 diffusers 后端对 None 与 [] 的处理有差异,可能引入回归,但概率较低且已通过 XPU CI 验证。
- 性能基线单次采样:xpu_b60.json 由单次 CI run 播种,非 denoise stage 容差高达 0.90、显存容差仅 0.05,B580 显存分配抖动可能导致误报,需要更多运行样本校准。
- 一致性校验全局关闭:在
is_xpu() 下原地 mutate ONE_GPU_CASES 关闭一致性检查,未来若有开发者以为 ONE_GPU_XPU_CASES 是独立副本而直接修改它,改动会被 pytest 静默忽略,是隐性维护陷阱。
- 过滤器组合风险:
changes_exist / main_package / multimodal_gen 三个输出的布尔组合依赖 paths-filter 的 glob 准确性,若匹配失误可能出现专用 job 不触发但 pr-gate 放行的空窗。
- CI 状态观察:merged 前 CI States 显示 PR Test (Extra) 与 AMD ROCm 7.2 失败,作者发起
/rerun-failed-ci,需确认失败与本次变更无关。
- 影响:对用户:XPU 平台上的 diffusion/multimodal_gen 服务变更从此有了 CI 守护,B580 用户可预期回归被提前拦截。对系统:XPU CI 新增 bmg-multigen-models runner 通道、1 个专用 job、1 份性能基线与 1 个 suite 注册,NVIDIA/AMD/ROCm 各套件保持不变。对团队:维护者需要同步维护 1-gpu(NVIDIA)与 1-gpu-xpu 两套用例集合,并关注 B580 显存预算变化;本 PR 也确立了"按平台显存与量化能力分层测试"的模式,后续 XPU 量化路径(INT4/INT8 weight-only via IPEX)落地后可把大模型用例重新启用。
- 风险标记:新 CI 门禁通道, 共享推理代码变更, 性能基线单次采样, 一致性校验全局关闭
关联脉络
- PR #36636 [AMD][CI] Add targeted Mori test labels: 同类平台 CI 门禁建设:为 AMD Mori 流水线增加专属测试标签,与本 PR 为 XPU 增加 diffusion 测试通道的模式一致,体现了多硬件平台 CI 分层覆盖的演进方向。
- PR #34492 XPU: remove SGLANG_USE_SGL_XPU flag: XPU 平台基础设施演进:默认启用 sgl-kernel MoE。本 PR 是其后的测试配套,说明 XPU 平台从基础推理走向 diffusion 全功能验证。
参与讨论