执行摘要
- 一句话:为AMD CI准备MI300x PR运行器池,优化镜像拉取、动态路由并调整性能阈值。
- 推荐动作:该PR对于负责CI/CD基础设施和AMD平台支持的工程师非常值得精读。重点关注:
1) 如何设计镜像拉取的回退和重试策略以提升鲁棒性;
2) 如何利用GitHub Actions的表达式动态选择运行器环境;
3) 大规模性能测试阈值调整的策略和具体数值,可作为硬件平台适配的参考案例。
功能与动机
根据PR描述,目标是为AMD CI做好准备,以便在linux-mi300-*gpu-sglang运行器池上运行PR测试,同时保持计划任务在linux-mi325-*gpu-sglang池上运行。这涉及三个耦合变更:1)使用内网镜像注册表避免PR任务频繁拉取Docker Hub导致限流;2)动态路由PR、计划任务和手动触发到不同的运行器池;3)针对MI300x硬件(相比MI325x内存带宽更低)调整测试性能预算(包括阈值、预估时间和超时),并跳过两个已知的MI300x内核级数值问题导致的非确定性测试。
实现拆解
-
优化Docker镜像拉取策略以使用局域网注册表
- 修改 release-docker-amd-nightly.yml 和 release-docker-amd-rocm720-nightly.yml:在发布夜间镜像后,新增 push_local_registry 任务,将镜像重新标记为内网注册表地址(172.29.8.23:5000/)并推送。
- 修改 scripts/ci/amd/amd_ci_start_container.sh 和 amd_ci_start_container_disagg.sh:调整 find_latest_image 函数逻辑,不再通过 docker manifest inspect 探测内网注册表(因网络可达性问题)。在实际拉取时,优先尝试 docker pull "${LOCAL_DOCKER_REGISTRY}/${IMAGE}" 从内网注册表拉取,失败后回退到公共Docker Hub并辅以带指数退避的重试机制(retry_with_backoff)。拉取失败的错误信息会被捕获并添加 [local-pull] 前缀输出以便诊断。脚本还会在检测到 DOCKERHUB_AMD_USERNAME 和 DOCKERHUB_AMD_TOKEN 环境变量时执行 docker login 以提升公共拉取速率限制。
- 在相关CI工作流文件(如 pr-test-amd.yml)的 env 块中注入上述Docker Hub凭证,确保脚本可以获取到。
-
根据CI触发类型动态选择AMD运行器池
- 修改 .github/workflows/pr-test-amd.yml:将GPU密集型作业的 runs-on 从硬编码的 linux-mi325-*-sglang 改为动态表达式:${{ format('linux-{0}-Ngpu-sglang', inputs.runner_arch || (github.event_name == 'pull_request' && 'mi300' || 'mi325')) }}。这意味着:pull_request 事件使用MI300x池(更快反馈),schedule 事件使用MI325x池(全覆盖夜间测试),workflow_dispatch 事件则允许用户通过新的 runner_arch 输入进行选择(默认为 mi300)。
- 为作业名称添加格式化,例如 name: ${{ format('stage-a-test-1-gpu-small-amd (linux-{0}-1gpu-sglang)', ...) }},使Actions UI中能清晰显示实际使用的运行器架构和GPU数量。
- 暂时禁用了Docker构建缓存(通过条件判断)。
-
为MI300x硬件放宽性能测试阈值并处理已知问题
- 修改多个性能测试文件,在 is_in_amd_ci() 条件分支中放宽延迟、吞吐量等断言阈值。例如:
test/registered/perf/test_bench_serving_1gpu_part2.py:对 test_vlm_offline_throughput、test_score_api_batch_scaling、test_embeddings_api_latency_throughput 等测试,将MI300x的期望输出吞吐量从2000 token/s降至900 token/s,延迟上限提高等。
test/registered/perf/test_bench_serving_1gpu_part1.py:移除原本在AMD CI中跳过LoRA测试的代码,并为 test_online_lora_latency 和 test_online_lora_latency_with_concurrent_adapter_updates 放宽TTFT(首令牌时间)阈值。
test/registered/moe/test_torch_compile_moe.py:在非CUDA且为AMD CI时,将吞吐量阈值从≥270 tok/s放宽至≥240 tok/s。
- 修改
test/registered/tokenizer/test_multi_tokenizer.py 和 test/registered/amd/test_deepseek_v32_basic.py 等,放宽TTFT阈值或延长预估执行时间(est_time)。
- 在
pr-test-amd.yml 中将 stage-c 步骤的超时从90分钟延长至120分钟。
- 使用
@unittest.skipIf(is_in_amd_ci(), ...) 跳过两个在MI300x上因内核级数值非确定性而失败的测试:
test/registered/sampling/test_pytorch_sampling_backend.py::test_greedy:贪心解码在多次运行间无法保证比特一致。
sgl-kernel/tests/test_moe_topk_sigmoid.py::test_topk_sigmoid[2048-32-4]:特定参数下sigmoid分数在fp32 ULP内时,tie-breaking逻辑与torch.topk不同(但结果仍有效)。
关键文件:
.github/workflows/pr-test-amd.yml(模块 CI流水线;类别 infra;类型 infrastructure): 这是CI动态路由的核心配置文件,定义了运行器选择逻辑(PR用MI300,计划任务用MI325)、作业名称格式化、环境变量注入以及临时禁用缓存等关键基础设施变更。
scripts/ci/amd/amd_ci_start_container.sh(模块 CI脚本;类别 infra;类型 infrastructure): 负责在AMD CI中启动容器的关键脚本,其变更实现了优先从内网注册表拉取镜像、失败后回退到Docker Hub并重试的鲁棒策略,是解决Docker Hub限流和加速CI的关键。
test/registered/perf/test_bench_serving_1gpu_part2.py(模块 性能测试;类别 test;类型 test-coverage;符号 test_vlm_offline_throughput, test_score_api_batch_scaling, test_embeddings_api_latency_throughput, test_embeddings_api_batch_scaling): 包含多个关键性能测试的断言阈值调整,是适配MI300x硬件性能(尤其是内存带宽较低)的集中体现。其变更直接影响了CI在MI300x运行器上的通过/失败判定。
test/registered/perf/test_bench_serving_1gpu_part1.py(模块 性能测试;类别 test;类型 test-coverage;符号 test_online_lora_latency, test_online_lora_latency_with_concurrent_adapter_updates): 修改了关键的LoRA延迟测试,移除了之前为AMD CI跳过的逻辑,并调整了TTFT阈值,反映了对MI300x硬件上LoRA性能的重新评估。
test/registered/sampling/test_pytorch_sampling_backend.py(模块 采样测试;类别 test;类型 test-coverage;符号 test_greedy): 通过装饰器跳过了在AMD CI中因内核级数值非确定性而失败的贪婪解码测试,展示了处理硬件特定已知问题的一种策略。
关键符号:test_vlm_offline_throughput, test_score_api_batch_scaling, test_embeddings_api_latency_throughput, test_online_lora_latency, test_online_lora_latency_with_concurrent_adapter_updates, test_throughput, test_greedy, find_latest_image
关键源码片段
scripts/ci/amd/amd_ci_start_container.sh
负责在AMD CI中启动容器的关键脚本,其变更实现了优先从内网注册表拉取镜像、失败后回退到Docker Hub并重试的鲁棒策略,是解决Docker Hub限流和加速CI的关键。
# 定义本地注册表地址,支持通过环境变量覆盖(review 后采纳的建议)
LOCAL_DOCKER_REGISTRY="${LOCAL_DOCKER_REGISTRY:-172.29.8.23:5000}"
# 在拉取镜像的主逻辑中
IMAGE=$(find_latest_image "${GPU_ARCH}")
# 优先尝试从本地注册表拉取
if local_pull_output=$(docker pull "${LOCAL_DOCKER_REGISTRY}/${IMAGE}" 2>&1); then
echo "Pulled from local docker registry: ${LOCAL_DOCKER_REGISTRY}/${IMAGE}"
docker tag "${LOCAL_DOCKER_REGISTRY}/${IMAGE}" "${IMAGE}"
else
# 本地拉取失败,回退到公共注册表并重试
echo "Local docker registry pull failed; falling back to public registry: ${IMAGE}" >&2
printf '%s\n' "${local_pull_output}" | sed 's/^/ [local-pull] /' >&2 # 输出带前缀的错误信息便于调试
retry_with_backoff 6 docker pull "${IMAGE}" # 指数退避重试函数
fi
# 如果提供了 Docker Hub 凭证,则登录(以提升拉取速率限制)
if [[ -n "${DOCKERHUB_AMD_USERNAME}" && -n "${DOCKERHUB_AMD_TOKEN}" ]]; then
retry_with_backoff 3 docker login -u "${DOCKERHUB_AMD_USERNAME}" -p "${DOCKERHUB_AMD_TOKEN}"
fi
test/registered/perf/test_bench_serving_1gpu_part2.py
包含多个关键性能测试的断言阈值调整,是适配MI300x硬件性能(尤其是内存带宽较低)的集中体现。其变更直接影响了CI在MI300x运行器上的通过/失败判定。
def test_score_api_batch_scaling(self):
"""测试评分API在不同批次大小下的性能"""
batch_sizes = [10, 25, 50]
for batch_size in batch_sizes:
res = run_score_benchmark(...)
self.assertEqual(res["successful_requests"], res["total_requests"])
# 根据是否在 AMD CI 中调整延迟边界
if is_in_amd_ci():
# 为 MI300x 放宽边界(HBM3 带宽较低,延迟更高)
bounds = {10: (60, 65), 25: (70, 80), 50: (80, 90)}
default_bounds = (90, 90)
else:
# 非 AMD CI(如 CUDA 或其他)保持原有严格边界
bounds = {10: (45, 50), 25: (50, 60), 50: (60, 65)}
default_bounds = (60, 65)
avg_latency_bound, p95_latency_bound = bounds.get(batch_size, default_bounds)
self.assertLess(res["avg_latency_ms"], avg_latency_bound)
self.assertLess(res["p95_latency_ms"], p95_latency_bound)
评论区精华
reviewer gemini-code-assist[bot] 提出了两项主要改进建议:
- 避免硬编码内网注册表地址:建议在
amd_ci_start_container.sh 和 amd_ci_start_container_disagg.sh 中使用环境变量 LOCAL_DOCKER_REGISTRY 并设置默认值(${LOCAL_DOCKER_REGISTRY:-172.29.8.23:5000}),以提高脚本的可配置性和可维护性。最终代码采纳了此建议,将硬编码改为环境变量。
- 清理注释掉的代码:指出脚本中残留的注释行(如
CACHE_HOST=/home/runner/sglang-data)应被移除以保持代码整洁。此建议也被采纳,相关注释代码在最终提交中被删除。
这些讨论体现了对代码质量(可维护性、清晰度)的关注,并达成了共识。
- 使用环境变量提高脚本可配置性 (design): 建议被采纳,代码修改为
LOCAL_DOCKER_REGISTRY="${LOCAL_DOCKER_REGISTRY:-172.29.8.23:5000}"。
- 清理注释掉的代码以保持代码整洁 (style): 建议被采纳,相关注释代码在最终提交中被删除。
风险与影响
- 风险:
- 测试阈值放宽可能掩盖真实性能回归:由于MI300x硬件性能较低,多项性能测试的断言阈值被显著放宽(例如输出吞吐量从2000降至900)。虽然这是为了适应硬件差异,但可能使得在MI300x池上运行的PR测试对代码变更引入的性能退化不够敏感。
- 硬编码配置降低可维护性:尽管review后改用了环境变量,但默认值仍指向固定的内网IP和端口(
172.29.8.23:5000)。若内网注册表地址变更,需要更新多个脚本中的默认值或确保环境变量被正确覆盖。
- 跳过测试可能隐藏潜在问题:
test_greedy 和特定参数的 test_topk_sigmoid 在AMD CI中被跳过,理由是MI300x上存在已知的“内核级数值抖动”。虽然这些是已知问题且也存在于MI325x基线,但跳过测试意味着在这些运行器上失去了对这些功能正确性的持续验证。
- 运行器池切换的副作用:PR测试现在默认运行在MI300x而非MI325x上,性能基线不同。这要求工程师重新校准对CI结果中性能数字的期望,并注意比较基准的一致性。
- 影响:
- 对CI系统的影响:显著提升了AMD CI的稳定性和效率。通过优先使用内网镜像注册表,减少了对外部Docker Hub的依赖和可能的限流影响,加速了镜像拉取。动态路由确保了PR测试能获得更快的反馈(使用更轻量/空闲的MI300x池),而计划任务仍能在性能更强的MI325x池上进行全覆盖测试。
- 对开发团队的影响:提交PR的开发者将看到测试在MI300x硬件上运行,性能阈值已针对该硬件调整。团队需要了解新的性能基线(例如LoRA TTFT约慢2倍),并意识到一些非确定性测试在AMD CI中被跳过。
- 对系统的影响:此变更纯粹是CI/CD基础设施和测试配置的调整,不影响SGLang核心运行时、模型推理逻辑或用户API。
- 风险标记:测试阈值放松, 硬编码配置, 跳过测试
关联脉络
- PR #23073 [AMD] mirror nightly images to local registry and prefer LAN pulls: 该PR同样涉及AMD CI镜像注册表的优化(将夜间镜像推送到内网注册表并优先拉取),与当前PR的“内网镜像注册表”变更目标部分重叠。从提交历史(提交
a9a033b6)可知,当前PR在开发过程中与#23073的变更产生了冲突并进行了合并解决,表明这两项工作是并行推进的AMD CI基础设施改进。
参与讨论