对树外加速器后端来说,真正棘手的并不是“能否适配某个 PyTorch 版本”,而是如何尽早发现上游变化带来的兼容性回归。PyTorch 的 Cross-Repository CI Relay(CRCR)提供了一条更清晰的路径:上游 CI 发出变更信号,下游项目接收信号并执行自己的验证策略,而不必把每一种硬件环境都塞进 PyTorch 主仓库。
Torch Spyre 的集成价值正在这里:它既能跟随 PyTorch 上游变化,又保留对测试范围、硬件资源和失败策略的控制权。
CRCR 解决的是“通知与决策分离”
传统的跨仓库 CI 通常落入两个极端:
- 上游完全不知道下游,后端只能定时拉取最新代码,发现问题时往往已经滞后。
- 上游直接维护下游测试细节,随着后端增多,测试矩阵、凭据和硬件调度迅速失控。
CRCR 更适合采用中间层模型:
- PyTorch 上游 CI 判断某次提交是否需要通知外部后端。
- Relay 将提交标识、变更范围等上下文传给下游。
- Torch Spyre 这样的后端根据自己的规则选择测试。
- 测试在后端控制的 runner 和设备上运行。
- 结果作为兼容性信号反馈给维护者,是否阻塞合并则由双方约定。
关键点不是“所有后端运行同一套测试”,而是每个后端可以决定哪些 PyTorch 测试最能覆盖自己的风险。例如,算子注册、设备分派或编译路径发生变化时,可以扩大算子和模块测试;文档变更则通常不值得占用稀缺的加速器资源。
从上游提交到下游信心,需要一份稳定契约
跨仓库 CI 最容易坏在边界上。一个可维护的集成至少需要明确以下信息:
- 提交身份:测试必须绑定不可变的 PyTorch commit SHA,而不是浮动分支。
- 变更范围:文件列表或变更类别可以帮助下游选择测试集。
- 触发来源:下游应验证事件来自可信的上游工作流。
- 结果身份:日志和状态必须能追溯到对应的上游提交。
- 超时语义:硬件不可用、基础设施失败和代码回归不能混为一谈。
- 阻塞级别:早期接入可以只提供建议性信号,稳定后再考虑设为必需检查。
这种契约让 Torch Spyre 保持树外开发的独立性。PyTorch 不需要理解 Spyre runner 的部署方式、驱动版本或设备拓扑;Torch Spyre 也不需要通过轮询猜测上游何时发生了重要变化。
可以这样搭建一个下游接收工作流
下面是一个可改造的 GitHub Actions 骨架。它不是对 CRCR 官方事件字段的断言,而是演示下游仓库应如何接收提交 SHA、固定上游源码并在自托管加速器 runner 上执行测试。实际接入时,需要把事件名和 payload 字段替换为 CRCR 提供的真实契约。
将以下内容保存为 .github/workflows/crcr-downstream.yml:
name: PyTorch CRCR downstream validation
on:
repository_dispatch:
types: [pytorch-crcr]
workflow_dispatch:
inputs:
upstream_sha:
description: PyTorch commit SHA to validate
required: true
type: string
permissions:
contents: read
concurrency:
group: crcr-${{ github.event.client_payload.upstream_sha || inputs.upstream_sha }}
cancel-in-progress: true
jobs:
validate:
runs-on: [self-hosted, linux, spyre]
timeout-minutes: 90
env:
PYTORCH_SHA: ${{ github.event.client_payload.upstream_sha || inputs.upstream_sha }}
steps:
- name: Check out Torch Spyre
uses: actions/checkout@v4
with:
path: torch-spyre
- name: Validate immutable upstream revision
shell: bash
run: |
set -euo pipefail
[[ "$PYTORCH_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || {
echo "Expected a full 40-character commit SHA"
exit 2
}
- name: Check out the requested PyTorch commit
uses: actions/checkout@v4
with:
repository: pytorch/pytorch
ref: ${{ env.PYTORCH_SHA }}
path: pytorch
fetch-depth: 1
- name: Run backend-selected tests
shell: bash
run: |
cd torch-spyre
chmod +x ci/run-crcr-tests.sh
./ci/run-crcr-tests.sh ../pytorch
随后在下游仓库创建 ci/run-crcr-tests.sh。下面假设 runner 已准备好 Python、PyTorch 构建依赖和 Spyre 运行环境;安装命令需要按项目实际构建方式调整:
#!/usr/bin/env bash
set -euo pipefail
PYTORCH_DIR="${1:?usage: run-crcr-tests.sh /path/to/pytorch}"
PYTHON="${PYTHON:-python3}"
"$PYTHON" -m pip install -U pip pytest
"$PYTHON" -m pip install -e "$PYTORCH_DIR"
"$PYTHON" -m pip install -e .
# 先运行成本较低、能快速发现注册和导入问题的后端测试。
"$PYTHON" -m pytest -q tests/test_import.py tests/test_device_registration.py
# 再运行项目挑选出的上游兼容性测试。
# 这里的文件只是示例,应替换为 Torch Spyre 实际支持的 PyTorch 测试集。
"$PYTHON" -m pytest -q \
"$PYTORCH_DIR/test/test_torch.py" \
"$PYTORCH_DIR/test/test_nn.py"
为了控制硬件成本,还可以在脚本中根据变更路径选择测试。例如,只有修改触及 torch/、aten/ 或编译相关目录时才启动完整设备测试;其他变化只运行导入、注册和少量冒烟用例。路径筛选只能作为优化,不能成为永久跳过兼容性验证的理由,因此仍应安排每日或每周的完整矩阵。
测试选择比“接上 CI”更重要
一个可持续的 Torch Spyre 测试组合通常可以分层:
| 层级 | 目标 | 典型运行时机 |
|---|---|---|
| 导入与注册测试 | 检查包加载、设备发现和后端注册 | 每次触发 |
| 冒烟算子测试 | 覆盖少量关键张量和模块操作 | 每次触发 |
| 受影响测试 | 根据上游变更目录选择算子、分派或编译测试 | 相关变更触发 |
| 完整兼容性矩阵 | 捕获路径规则遗漏和跨模块回归 | 定时运行或发布前 |
测试越多并不必然意味着信心越高。若失败日志无法区分设备离线、驱动故障、测试波动和真实回归,大规模测试只会制造噪声。下游工作流应给失败分类,并保存 PyTorch SHA、Torch Spyre SHA、驱动版本、设备型号和测试列表等元数据。
接入时应守住的边界
CRCR 降低了树外后端进入上游 CI 流程的门槛,但不会自动解决所有兼容性问题。落地时可以用下面的清单审视集成:
- 只接受可信来源的 relay 事件,不直接执行 payload 中的任意命令。
- 始终检出完整 commit SHA,避免测试目标在运行期间漂移。
- 对 fork 或不可信提交限制 secrets 和加速器 runner 的访问。
- 将基础设施错误与测试断言失败分开上报。
- 为稀缺硬件设置并发限制、超时和取消旧任务的策略。
- 先以非阻塞信号观察稳定性,再决定是否纳入合并门禁。
- 定期运行完整测试,验证基于路径的筛选规则没有漏掉依赖关系。
CRCR 的真正价值不是多增加一个 CI webhook,而是建立清晰的责任边界:PyTorch 负责可靠地传播上游变化,Torch Spyre 负责把变化转换成最有价值的后端验证。只有当触发契约、测试选择和失败分类都足够稳定时,跨仓库流水线才会从“能运行”升级为“值得信任”。