SureVM 工程笔记

云端 Mac CI 进程优先级治理:用 QoS、nice 与 taskpolicy 降低任务干扰

云端 Mac CI 进程优先级治理:用 QoS、nice 与 taskpolicy 降低任务干扰

同一台云端 Mac 同时执行 Xcode 构建、测试、日志压缩和产物校验时,最常见的问题不是某个进程彻底失控,而是多个“都能晚一点完成”的辅助任务与关键构建争抢 CPU、磁盘和内存带宽。结果往往表现为编译耗时波动、测试超时或远程桌面响应迟缓。解决这类问题,第一步不是盲目降低所有进程优先级,而是先区分关键路径与后台路径。

先建立可比较的进程基线

在没有其他任务时执行一次代表性构建,再在压缩、校验和索引同时运行时重复执行。两次都记录构建耗时、失败阶段以及主要进程的 CPU、内存、状态和 nice 值。

mkdir -p "$HOME/ci-observe"

ps -axo pid,ppid,user,%cpu,%mem,state,nice,etime,command \
  | sort -k4 -nr \
  | head -n 40 \
  > "$HOME/ci-observe/processes.txt"

iostat -w 2 -c 15 > "$HOME/ci-observe/iostat.txt"
vm_stat 2 15 > "$HOME/ci-observe/vm-stat.txt"

采样要覆盖实际工作区和真实依赖规模,但不要把首次下载依赖与稳定构建混在同一基线里。若构建时间变长,同时 iostat 持续繁忙,而压缩进程占据明显 CPU,就有理由调整辅助任务;若主要问题是内存压力或测试并发过高,仅改优先级通常无效。

优先级是一种调度提示,不是资源上限。它适合解决“谁先运行”,不能替代并发控制,也不能保证某个任务获得固定比例的 CPU。

按关键路径给任务分级

可以把 CI 进程分成三类。第一类是编译、链接、测试和产物导出,它们决定流水线是否完成,应保持正常优先级。第二类是日志压缩、校验和生成、旧产物整理,这些任务必须完成,但通常不必抢在构建之前。第三类是编辑器索引、交互式分析和临时诊断,只应在需要时启动。

不要按工具名称一刀切

同一个工具可能处于不同路径。例如 tar 在构建前解包必需依赖时属于关键任务,在构建后压缩日志时属于后台任务。判断标准应是:它是否阻塞下一步、失败是否必须终止流水线、输出是否被当前任务立即使用。

建议把分级写入流水线配置,而不是依靠操作者临时执行。这样脚本审查时可以直接看到哪些命令被降级,也能避免新任务默认继承不合适的策略。

用 nice 与 taskpolicy 包装后台任务

nice 调整进程的调度优先值,正数表示更愿意让出执行机会。普通用户可以降低自己启动进程的优先级,但不能随意把进程提升到更高优先级。macOS 的 taskpolicy -b 可把命令放入后台策略,影响范围不只限于 CPU,因此应只用于已经确认不阻塞关键路径的任务。

下面的脚本让校验和在后台低优先级运行,同时让 Xcode 构建保持正常策略,并分别保留退出状态:

#!/bin/zsh

set -u
mkdir -p Artifacts Logs

taskpolicy -b nice -n 10 /bin/sh -c \
  'find Artifacts -type f -print0 | xargs -0 shasum -a 256 > Logs/checksums.txt' &
audit_pid=$!

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  build > Logs/xcodebuild.log 2>&1
build_rc=$?

wait "$audit_pid"
audit_rc=$?

if (( build_rc != 0 )); then
  exit "$build_rc"
fi

exit "$audit_rc"

不要给整个 CI Runner 统一套上 taskpolicy -b。这样会连编译器、测试进程和子进程一起降级,表现为整条流水线变慢,却看不出具体责任任务。包装应尽量靠近单条后台命令。

在应用代码中正确使用 QoS

如果辅助工作由自研 Swift 工具执行,应在代码层使用队列 QoS,而不是启动后再猜测进程职责。用户发起并等待结果的工作可使用 .userInitiated;日志整理、缓存清点等非即时工作可使用 .utility.background 只适合用户不等待、延后完成也不会影响当前结果的任务。

let queue = DispatchQueue(
    label: "com.surevm.ci.artifact-audit",
    qos: .utility
)

queue.async {
    auditArtifacts()
}

不要为了“更快”把所有队列设为 .userInteractive。过高的 QoS 会让辅助任务与关键工作争抢调度资源,还可能造成优先级反转:高优先级任务等待一个被低优先级队列持有的锁。共享锁、串行队列和同步调用都应纳入检查。

验证效果并设置回退条件

调整后至少重复三轮同样的构建输入,比较中位耗时、失败位置和资源采样。单次变快不能证明策略有效。还要检查日志压缩、校验和等后台任务是否完整结束,不能用“关键构建成功”掩盖辅助任务失败。

验收时可依次确认:

如果降级后辅助任务积压到下一轮构建,说明问题已从资源争抢变成吞吐不足。此时应减少后台任务数量、缩小处理范围或将其移到构建完成后的独立阶段,而不是继续提高 nice 值。SureVM 节点上的具体可选配置应在控制台确认,但无论使用哪档配置,先分清关键路径、再改变调度策略,通常比全局调整更容易验证和回退。

常见问题

nice 和 taskpolicy 能限制进程最多使用多少 CPU 吗?

不能。它们调整的是调度倾向和后台策略,不是硬性资源配额。需要限制并发时,应同时控制构建任务数、测试分片数和后台作业数量。

哪些 CI 任务适合放入后台策略?

日志压缩、校验和生成、旧产物整理和非阻塞索引通常适合降级;编译、链接、测试执行及其直接依赖不应默认放入后台策略。

SureVM 云端 Mac

为构建、开发与实验选择独享物理 Mac 节点

比较两档 Mac mini M4 配置、四种租期和五个可订购节点,再按实际工作流完成选择。

选择租用方案