在 RISC-V 开源生态的持续集成场景中,长期依赖 QEMU 模拟进行构建验证带来显著的性能开销。RISE 项目推出的原生 RISC-V CI Runners 通过提供免费、可直接调用的 GitHub Actions runner,让开源项目能够在真实硬件上完成构建与测试,绕过软件模拟层的时间成本。本文从架构设计、接入配置和工程实践三个维度,梳理这一基础设施的关键技术要点。

一、为什么原生硬件 CI 至关重要

传统 RISC-V CI 流程通常采用 QEMU 用户态模拟(User Mode Emulation)来执行二进制。这种方案的优势在于跨平台兼容 —— 只要目标架构的指令集能够被 QEMU 解析,即可在 x86_64 宿主机上运行 ARM、RISC-V 等多种架构的测试。然而,模拟层带来的性能损耗在实际工程中往往被低估。以一个典型的 CMAKE 项目为例,在 QEMU 环境下完成完整构建的时间通常是原生硬件的 5 到 10 倍,在包含大量编译单元的大型项目中,这一比例会进一步恶化。更关键的是,模拟环境无法真实反映硬件特性 —— 包括缓存行为、内存访问延迟、以及特定扩展指令集(如 RVV 向量指令)在真实处理器上的运行时表现。这意味着即使 CI 通过,项目在实际 RISC-V 硬件上仍可能遭遇难以复现的运行时问题。

RISE Runners 的核心价值在于提供真实的裸金属 RISC-V 节点作为 GitHub Actions 的执行环境。这些节点基于 Scaleway EM-RV1 等 RISC-V 服务器实例,通过 Webhook 驱动的调度系统在每次任务触发时动态创建临时 Kubernetes Pod,并在其中运行 GitHub Actions runner 进程。任务执行完成后,Pod 即被销毁,整个过程对用户透明。这种设计既保证了硬件的真实性和测试有效性,又通过资源复用控制了运维成本。

二、架构设计与资源调度

RISE Runners 的底层架构采用了经典的 SaaS 化 CI 基础设施模式。GitHub Actions 的工作流通过 runs-on 标签指定目标运行器,RISE 的调度层接收任务后,在可用 RISC-V 节点池中分配资源并启动临时执行环境。每个任务拥有独立的文件系统命名空间和网络隔离,任务结束后所有资源被完全回收。这种 ephemeral(临时性)设计天然适配 CI 场景的高并发特性 —— 多个项目、不同分支的构建请求可以并行调度到同一硬件池中的不同节点上,彼此互不干扰。

硬件层面的选择也值得关注。当前 RISE 提供的免费_runner_主要基于 64 位 RISC-V 通用处理器(RV64GC),支持 RISC-V V 扩展的部分特性,但具体可用性取决于底层硬件的代数。由于 RISC-V 服务器硬件仍处于快速迭代期,不同节点的微架构可能存在差异,项目在接入时应当进行基本的架构探测和兼容性验证。这一约束也提示了一个工程实践原则:将架构相关的构建步骤与通用构建步骤分离,通过条件分支在不同的 runner 上执行相应的任务。

三、GitHub Actions 接入配置

将现有项目迁移到 RISE 原生 RISC-V CI 的操作成本极低。核心步骤只有两步:首先确认项目符合 RISE 的开源使用条款(目前仅面向公开仓库),然后在工作流文件中调整 runs-on 标签。以下是一个最小化的示例配置:

name: RISC-V CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-24.04-riscv
    steps:
      - uses: actions/checkout@v4
      - name: Print architecture
        run: uname -m
      - name: Build
        run: |
          echo "Building on native RISC-V"
          make build
      - name: Run tests
        run: make test

这个工作流的核心变化是将 runs-on 的值从传统的 ubuntu-latest(或 ubuntu-24.04)替换为 ubuntu-24.04-riscv。RISE 的调度系统识别该标签后,会将任务分发至 RISC-V 硬件节点执行。值得注意的是,actions/checkout@v4 等通用 Action 通常能够正常工作,但如果项目依赖特定的系统包或工具链,需确保目标镜像中已预装相应依赖。RISE 维护的镜像基于 Ubuntu 24.04 的 RISC-V 移植版,常用开发工具链(如 GCC、LLVM、CMake)已默认包含。

对于需要多架构构建的项目,可以利用 GitHub Actions 的矩阵(matrix)策略并行执行 x86_64 和 RISC-V 两套构建:

jobs:
  build:
    runs-on: ${{ matrix.runner }}
    strategy:
      matrix:
        runner: [ubuntu-latest, ubuntu-24.04-riscv]
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: make build

这种配置使得项目能够在单一提交中验证跨架构兼容性,同时享受 RISC-V 原生构建的性能优势。

四、性能基准与工程考量

在实际项目中切换到原生 RISC-V CI 后,性能提升的具体数值取决于项目的编译密度和测试复杂度。以一个包含约 200 个 C++ 源文件、使用 CMAKE 作为构建系统的嵌入式固件项目为例,在 QEMU 模式下完整构建耗时约 25 分钟,而在 RISE 原生硬件上相同构建的耗时降至约 4 分钟,性能提升超过 6 倍。测试阶段的提升更为明显 —— 原生执行避免了每条指令的模拟开销,单元测试的运行时间通常缩短至原来的十分之一以下。

然而,接入原生硬件 CI 也带来了新的工程考量需要正视。首先是信任模型:任务在 RISE 提供的远程节点上执行,这意味着代码、构建产物和测试数据会经由第三方基础设施处理。对于处理敏感密钥或私有业务逻辑的项目,应当在构建流程中使用 GitHub Secrets 加密敏感环境变量,并在工作流设计中遵循最小权限原则 —— 仅将必要的凭证注入构建步骤,其他环境变量应在 runner 本地配置而非通过 GitHub 传递。其次是硬件可用性:RISE 作为社区驱动的免费服务,节点池容量有限,在极端并发场景下可能出现排队。大型项目或需要稳定 SLA 的团队应当评估是否需要结合自托管 runner 或商业 RISC-V CI 服务(如 Cloud-V)构建混合部署策略。

五、监控与故障排查

原生 RISC-V CI 的监控思路与传统 CI 类似,但需要关注一些特定指标。工作流执行日志中的 uname -m 输出应当显示 riscv64 或类似的架构标识,如果仍显示为 x86_64,则说明任务被错误地调度到了非目标节点。在构建脚本层面,建议在初始化阶段输出关键环境信息 —— 包括 GCC 版本(gcc --version)、GLIBC 版本(ldd --version)以及是否支持 RISC-V 向量扩展(可通过检查 /proc/cpuinfo 中的 v 标志位)—— 这些信息在排查兼容性问题时非常关键。

如果遇到构建失败,第一步应检查 RISC-V 特定的头文件或库是否缺失。某些项目可能依赖仅在 x86_64 平台上存在的系统调用或硬件特性,此时需要通过条件编译或功能检测代码进行适配。第二步可以尝试在本地使用 RISC-V 模拟器(如 QEMU)复现问题,排除硬件差异因素后,再向 RISE 社区报告真实的兼容性问题。

六、总结与行动建议

RISE 原生 RISC-V CI 为开源项目提供了一条绕过 QEMU 性能瓶颈的可行路径,其实质是将 RISC-V 构建从软件模拟层提升到真实硬件层。在接入成本上,只需修改 GitHub Actions 工作流中的 runs-on 标签即可完成切换,无需额外的基础设施投入。建议 RISC-V 相关开源项目的维护者优先评估以下两个条件:一是项目是否面向公开仓库(当前使用条款的限制),二是构建流程中是否存在不可模拟的硬件依赖。如果满足条件,现在即可将 CI 配置中的 QEMU 步骤替换为 RISE 原生 runner,观察构建时间的变化并据此决定是否全面迁移。


参考资料