pytorch的CI集成架构
如今在大部分ISA指令集中都存在相应的向量加速扩展,例如x86的SSE,PowerPC的VSX,ARM的NEON以及RISCV的RVV扩展。得益于这些向量加速指令,以及相应架构编译器提供的intrinsic函数,pytorch能够在不同的硬件平台上使用不同的向量扩展指令进行硬件层面的加速
SLEEF的CI测试
以下说明SLEEF库中每一个yaml脚本的CI目的以及作用
1 | . |
首先以pytorch调用的第三方仓库SLEEF为例进行CI的简单学习,以SLEEF源代码中的.github/workflow路径下的build_and_test.yml脚本为例,该脚本在标准的的ubuntu环境中使用不同的qemu-cpu进行测试
工作流build_and_test的job主要分为以下四个
build-native该工作主要在github官方提供的x86_64服务器上进行SLEEF源码的构建。其步骤是将SLEEF源代码检出到测试环境中;安装相关依赖;并使用cmake指令构建并编译SLEEF源代码;使用upload-artifact@v3action将相关构建文件和编译文件上传暂存到当前工作流中
test-native该工作主要是在x86_64平台上进行ctest测试。其步骤是检出代码;安装依赖;下载build-native的编译工件;进行ctest测试;将测试结果通过upload-artifact@v3上传到工作流中
build-cross该工作主要使用矩阵策略,使用qemu-cpu进行不同硬件平台和编译器类型的测试。build-cross的编译构建工作大致和build-native相同,需要注意的是多出的两步Check sysroot cache和Create sysroot,前者使用actions/cache@v3检查本地是否存在sysroot目录缓存可用;若本地不存在可用sysroot缓存,则后者使用debootstrap指令创建新的sysroot环境
sysroot是一个用于交叉编译和隔离构建环境的重要概念,其作用是提供一个与目标系统环境相似的根文件系统,使得编译器能够找到正确的对应目标架构的库、头文件和依赖项以下是有关github action中矩阵策略的学习,在
step中可以使用${{ matrix.arch }}-${{ matrix.compiler }}表示不同的硬件架构和编译器类型组合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17matrix:
arch: [aarch64, armhf, ppc64el, s390x, riscv64]
compiler: [gcc, llvm]
include:
- arch: armhf
binfmt: arm
gnupkg: -arm-linux-gnueabihf
- arch: ppc64el
binfmt: ppc64le
gnupkg: -powerpc64le-linux-gnu
- arch: aarch64
debarch: arm64
exclude:
# Only GCC trunk supports the RISC-V V intrinsics and https://github.com/riscv-collab/riscv-gnu-toolchain
# doesn't track a recent enough version yet
- arch: riscv64
compiler: gcc在
matrix中,include字段用于扩展或覆盖矩阵的组合,在样例代码中,[armhf, *]的矩阵组合会被添加两个字段,即[armhf, *, arm, -arm-linux-gnueabihf];同时exclude字段也预示着riscv64不会和gcc进行组合test-crossSetup QEMU这一步使用docker/setup-qemu-action这一脚本,在工作流中配置不同架构的docker容器,此方法避免手动下载并配置本地QEMU,通过设置QEMU_CPU环境变量,可以告诉QEMU的docker容器模拟哪一种硬件架构。此外,在项目根目录的CMakeLists.txt文件中使用enable_testing()命令,使能项目可以使用ctest进行测试,ctest测试的可执行文件路径在build/bin/*中。这里需要注意的是,在ctest执行二进制文件时,QEMU容器会自动识别并运行非原生架构的二进制文件,从而使得二进制程序的运行透明于用户(看似直接运行于当前平台)
如何向可移植库添加硬件相关CI(以SLEEF为例)
多硬件平台模拟
假设现在需要向SLEEF仓库中添加对一个硬件平台的CI,仅需要修改test_and_build.yml文件,该文件中需要修改的部分是build-cross和test-cross两个步骤,向其中的strategy \ matrix字段添加硬件配置即可,以riscv64为例
build-cross步骤中矩阵策略添加如下内容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19strategy:
fail-fast: false # 并行作业中,存在作业失败不影响其他作业继续运行
matrix:
arch: [aarch64, armhf, ppc64el, s390x, riscv64] # 添加riscv64架构
compiler: [gcc, llvm]
include:
- arch: armhf
binfmt: arm
gnupkg: -arm-linux-gnueabihf
- arch: ppc64el
binfmt: ppc64le
gnupkg: -powerpc64le-linux-gnu
- arch: aarch64
debarch: arm64
exclude: # 此处指出矩阵策略中riscv64不要和gcc组合,因此只有[rsicv64, llvm]组合
# Only GCC trunk supports the RISC-V V intrinsics and https://github.com/riscv-collab/riscv-gnu-toolchain
# doesn't track a recent enough version yet
- arch: riscv64
compiler: gcctest-cross步骤中矩阵策略添加如下内容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65strategy:
fail-fast: false
matrix:
include:
# AArch64
- arch: aarch64
compiler: gcc
qemu_cpu: "max,sve=off"
- arch: aarch64
compiler: gcc
qemu_cpu: "max,sve=on,sve128=on"
- arch: aarch64
compiler: gcc
qemu_cpu: "max,sve=on,sve256=on"
- arch: aarch64
compiler: gcc
qemu_cpu: "max,sve=on,sve512=on"
- arch: aarch64
compiler: llvm
qemu_cpu: "max,sve=off"
- arch: aarch64
compiler: llvm
qemu_cpu: "max,sve=on,sve128=on"
- arch: aarch64
compiler: llvm
qemu_cpu: "max,sve=on,sve256=on"
- arch: aarch64
compiler: llvm
qemu_cpu: "max,sve=on,sve512=on"
# Aarch32
- arch: armhf
compiler: gcc
binfmt: arm
qemu_cpu: "max"
- arch: armhf
compiler: llvm
binfmt: arm
qemu_cpu: "max"
# PPC64
- arch: ppc64el
compiler: gcc
binfmt: ppc64le
qemu_cpu: "power10"
- arch: ppc64el
compiler: llvm
binfmt: ppc64le
qemu_cpu: "power10"
# IBM Z
- arch: s390x
compiler: gcc
- arch: s390x
compiler: llvm
# RISC-V
- arch: riscv64 # 此处配置不同的qemu_cpu向量扩展,后续让qemu容器模拟不同向量扩展的硬件平台
compiler: llvm
qemu_cpu: "rv64,zba=true,zbb=true,zbs=true,v=false"
- arch: riscv64
compiler: llvm
qemu_cpu: "rv64,zba=true,zbb=true,zbs=true,v=true,vlen=128,elen=64,vext_spec=v1.0"
- arch: riscv64
compiler: llvm
qemu_cpu: "rv64,zba=true,zbb=true,zbs=true,v=true,vlen=256,elen=64,vext_spec=v1.0"
- arch: riscv64
compiler: llvm
qemu_cpu: "rv64,zba=true,zbb=true,zbs=true,v=true,vlen=512,elen=64,vext_spec=v1.0"
交叉编译 & 交叉测试
SLEEF的做法是在github
action官方提供的服务器(x86-ubuntu)平台上,使用docker镜像模拟的qemu硬件平台进行测试,借助于sysroot文件系统在本地交叉编译出目标系统的二进制文件后,我们便可以在原生平台上直接运行非原生架构的二进制文件,qemu容器进行会自动识别并运行当前的异构二进制文件。
为此我模仿test_and_build.yml文件编写了特定于RISCV64架构的SLEEF
CI脚本,并附上详细的注释,代码链接https://github.com/ZhouBencheng/sleef/blob/master/.github/workflows/build-and-test-riscv64.yml
CI结果如下
pytorch的CI测试
以aarch64架构的SVE2向量扩展为例,首先观察pytorch添加SVE2扩展的代码触发而RVV未能触发的CI有哪些
关于添加SVE2支持会触发的工作流
check labels该工作流主要调用
.github/script/check_labels.py脚本对本次pr进行标签测试,检查本次pr是否包含应该含有的标签linux-binary-libtorch-pre-cxx11在该工作流的主要步骤是
libtorch-cpu-shared-with-deps-cxx11-build,其内容是使用.github/workflow/_binary-build-linux.yml脚本构建出libtorch共享库及其所有依赖库;在libtorch-cpu-shared-with-deps-cxx11-test步骤中使用.github/workflow/_binary-test-linux.yml脚本进行二进制文件的测试
linux-binary-libtorch-cxx11-abi
linux-binary-libtorch-pre-cxx11和linux-binary-libtorch-cxx11-abi两个工作流的内容非常相似但目的不同,其中linux-binary-libtorch-pre-cxx11用于构建编译libtorch的C++11之前的版本,旨在让libtorch能够向后兼容未启用C++11标准老旧系统;而linux-binary-libtorch-cxx11-abi在构建编译libtorch时开启C++11新特性,适用于开启了C++11特性新标准环境。注意在前者中,DESIRED_DEVTOOLSET被设置为pre-cxx11,指明使用旧的开发工具链;而后者的DESIRED_DEVTOOLSET设置为cxx11-abi,明确启用C++11的二进制接口linux-binary-manywheel同上面两个工作流,本工作流同样调用
_binary-build-linux.yml和_binary-test-linux.yml两个脚本,但是分别在py3.9-cuda11.8py3.9-cuda12.1py3.9-cuda12.4几个平台上进行构建测试linux-binary-manywheel-split该工作流关于
libtorch构建和linux-binary-manywheel基本相同,但该工作流注重split build,_binary-build-linux关于split build的描述如下[Experimental] Build a libtorch only wheel and build pytorch such that are built from the libtorch wheel.
在传统
pytorch构建中,libtorch和pytorch通常会一起构建,采用split build可以先独立构建libtorch,将libtorch作为单独一个组件进行分发和使用,这种方法对纯C++项目的开发非常有帮助,避免不必要的python依赖。其次开发者可以基于已经构建好的libtorch轮子构建pytorch,增强兼容性和稳定性linux-aarch64本工作流中,在build阶段调用
.github/workflow/_linux-build.yml脚本,并设置要拉取的docker镜像为pytorch-linux-jammy-aarch64-py3.10-gcc11。在_linux-build.yml脚本中,该工作主要在pytorch预先配置好的docker容器中运行build.sh进行构建。而在test阶段,调用.github/workflow/_linux-test.yml脚本,在改脚本中使用预先配置好的docker容器运行.ci/onnx/test.sh或.ci/pytorch/test.sh脚本进行测试trunkperiodic这两个工作流都在
linux-jammymacoswindows三个平台上进行构建测试,并通过设置cron表达式,在特定的时间点定时运行相关CI
注意上述几个binary相关的工作流都由Jinja模板linux_binary_build_workflow.j2文件动态生成
关于linux-binary-libtorch-pre-cxx11
linux-binary-libtorch-cxx11-abi
linux-binary-manywheel工作流能够触发是因为相关PR具备标签ciflow/trunk,periodic
linux-binary-manywheel-split工作流能够触发是因为具备标签ciflow/periodic。其中硬件相关的CIlinux-aarch64能够出发是因为具备标签ciflow-aarch64。
通过检查github
pr标签中的集合,发现ciflow标签下与ISA相关的特定标签仅有ciflow-aarch64一个,以其他PR——ppc64le硬件的vsx平扩展为例,一方面ppcle64硬件相关的PR不存在类似ciflow/ppc64le这样的标签,再者其触发的工作流也没有硬件相关CI,CI如下,是SVE触发CI的子集

