代码修复平台设计台
章首给定:每秒 10 项任务;每项 3 轮,每轮先调模型再跑工具;工具用 1 个 CPU 核执行 1 秒;活跃环境是 1 vCPU、2 GiB 的 microVM;工具平台是一台 m5d.metal(48 核、约 358 GiB);模型服务是 10 个 DeepSeek V4-Flash 副本(每副本 4 张 B200)。目标:至少 95% 的任务在 24 秒内通过测试。
按书中的解题顺序点下面五步,或者自己拖动参数。
根据任务执行过程计算资源需求
先分清两个词:任务是要交付最终结果的整件工作(得到通过测试的补丁);一次模型请求或一次工具执行只是其中一个步骤。控制器保存任务记录、决定下一步,执行器实际跑工具,运行环境承载工具需要的文件、进程和内存。
工具要等模型给出完整参数才能执行,所以任务的大部分时间里工具并不占 CPU,环境却还占着内存。流式输出能早点返回部分文本,但改哪个文件、怎么改,要收到完整调用才知道。
CPU·秒
各个核实际执行时间之和。一核跑 3 秒,或三核各跑 1 秒,都是 3 CPU·秒。本任务:3 CPU·秒。
GiB·秒
内存占用 × 持续时间,即时间线下的面积。2 GiB 环境保留 30 秒:60 GiB·秒。
平台需求
资源需求率 = 到达率 × 每任务平均需求。10 × 3 = 30 核;10 × 60 = 600 GiB;10 × 3 × 9 = 270 个并发调用。
Ttask = Tqueue + Tprepare + Σr=1..R(Tmodel,r + Ttool,r) + Tcommit Nenv = λenv · Tenv = 10 × 30 = 300 个环境
例 11-1 · 为什么先撞上的是内存
CPU 30/48 · 模型 270/320 · 内存 600/358358 GiB 最多同时保留 178 个 2 GiB 环境,每个要占 30 秒,平台每秒最多只能开始 5.9 项任务,其余排队。此时 CPU 还空着 18 核,加 CPU 解决不了内存问题。模型从 9 秒变慢到 12 秒(设计台第 ② 步),CPU 需求不变,内存却涨到 780 GiB,并发调用 360 也超过了 320 个槽:模型变慢会抬高工具平台的内存需求,因为环境要保留得更久。
错开启动:用时间换内存峰值
一组四进程 CPU 工具实验:同时启动与错开启动的累计 CPU 时间 0.29 / 0.27 秒,几乎一样;RSS 峰值从约 320 MiB 降到 80 MiB,整组完成时间却从 0.10 秒拉长到 0.54 秒。
释放之前,先想清楚丢了要重做什么
两轮结束时已过 20 秒,工具 CPU 只用了 2 秒。如果丢了工作目录要从头来,重做的是 20 秒,不是 2 秒。
| 状态 | 需要记录的信息 | 丢失后的恢复方式 |
|---|---|---|
| 模型上下文和生成进度 | 任务、模型版本、输入前缀、已采用的输出 | 重新提交上下文,重建模型状态 |
| 工作目录与文件 | 仓库版本、文件摘要、补丁版本 | 从持久副本取回或重新应用补丁 |
| 进程与内存 | 环境实例、进程状态、运行时条件 | 恢复快照或重新执行工具 |
| 工具结果 | 工具操作、执行尝试、输入文件版本 | 查询已保存结果或再次执行 |
| 外部操作 | 目标系统中的操作标识与提交状态 | 查询、去重或按业务规则补偿 |
任务、工具操作、每次执行尝试要各有标识:同一份结果被传两次只需去重;同一项测试真执行了两次,资源已经花了两份。还要区分失败原因:测试没过要修程序,进程因资源不足中止要恢复执行。RL 验证也一样,答案对错用于奖励,执行故障先由运行系统处理。
运行环境的创建、保留与释放
最多 320 个并发调用
完整工具参数 →
工具何时用环境、哪些文件要跨轮保存
← 保存结果
48 核 · 358 GiB · microVM
隔离边界:逐级加强
进程
私有地址空间
私有地址空间
靠地址空间和权限做基本隔离。
容器
私有视图与配额
私有视图与配额
命名空间 + 文件系统视图 + 资源控制。
microVM
保留虚拟机隔离,精简设备降低开销。本章选这个。
Firecracker 由 AWS 为无服务器计算开发,2018 年开源(NSDI 2020 论文);E2B 用 Firecracker 跑工具环境,模板可由容器镜像构建:镜像决定装了什么,microVM 决定怎么隔离。需要 GPU 的工具(比如验证 CUDA 程序)可用 QEMU + VFIO 设备直通。
创建环境:少加载,晚加载
例 11-2 · 100 个环境装载模板要多少内存
25 GbE · 3.125 GB/s另有一份共享模板不计入各环境。共享只读减少重复存储,留热点页减少远端读取。按需加载让创建接口更早返回,代价是首次访问缺页时要等:再多访问 1 GiB 未加载的页,这条链路又要工作约 0.34 秒。
暂停:保存时间随内存增长
图 11-11 / 11-12 · 暂停还是保留
E2B:暂停约 4 s/GiB,恢复约 1 s暂停省钱的条件:pM·M·G > Ctransition。三种恢复继承的状态不同:暂停后恢复,继续同一任务;快照派生,从同一状态开多个分支;从初始模板新建,不继承任何修改(RL 验证常要求每条样本从同一份干净目录出发)。外部存储里已提交的写入不会随快照回滚,恢复后先查结果再继续。
每轮重建:只在要用的 3 秒里占内存
工作目录和结果已经持久化,下一轮只需要文件,不需要进程。模板已缓存时,从模板启动 microVM、载入工作目录、初始化运行时共约 2 秒(热路径);模板不在本机还要多取 2 GiB,约 2.7 秒(冷路径)。第 1 轮模型第 9 秒返回,就在第 7 秒开始准备,第 9 秒执行工具,第 10 秒保存并销毁。
图 11-13 / 11-14 · 同样 30 秒,内存面积 60 → 18 GiB·秒
预测下一个工具,提前准备
章首任务的下一轮工具是确定的。工具种类多时,SpecBox 用流式输出里透露的工具类型提前准备环境。设准备要 P 秒,提前 L 秒开始,命中率 h,猜错立即取消再按需准备:
E[W] = h·max(P−L, 0) + (1−h)·P = P − h·min(P, L)
图 11-15 ~ 11-19 · 提前量与命中率
调用在第 5 s 到达只要能在调用到达前准备好,就没必要更早创建。本地重放实验:从提前 50 ms 改为一开始等模型就准备,工具总时间不变(0.26–0.28 s),累计内存占用却从约 0.0193 涨到 0.467 GiB·秒,约 24 倍。
共享资源池的分配与调度
多卡作业要的不只是“空闲 GPU 够数”,还要卡型、每卡显存、配套 CPU 与主机内存,以及适合通信模式的位置,并且所有进程要一起启动。空闲资源分布在哪些节点上,决定作业能不能启动。
例 11-3 · 资源碎片:需要同节点 4 张 H100 + 16 核
生产集群也一样:阿里巴巴 ASI 的研究(六个月、155,410 张 GPU)显示,配套 CPU 不足、成组启动和网络位置限制都会让空闲 GPU 用不上。加入可抢占的低优先级作业后,GPU 分配比例从 68% 提高到 93%。
同样的工作量,到达方式决定排队
图 11-22 / 11-23 · 均匀到达 vs 同时到达
平台上的突发:48 核全空时同时来 80 个 1 秒的工具调用,前 48 个立即跑,32 个等 1 秒,平均等 0.4 秒、2 秒内全部完成;两台机器(96 核)1 秒完成。但 80 个环境要 160 GiB,全程驻留方案(600 GiB)根本放不下,每轮重建方案(180 GiB)剩下的约 178 GiB 正好够。
抢占的代价
在线任务最多等 2 秒,离线作业保存状态要 30 秒:即使能抢占,资源也要 30 秒后才交还。所以仍要另外预留能 2 秒内交给在线任务的资源,还要计入保存、恢复和原作业被推迟的时间。
怎样算公平
两个任务各得两张 GPU,份额相同;一种卡比另一种慢一倍,进度就不同。DRF 按每个任务占比最高的那类资源分配;Gavel、Pollux 进一步考虑卡的处理能力和训练进度;限制最长等待防止饿死。
最迟开始时间
batch 里最慢一条第 120 秒才完成。一项 ≤100 秒的工作最迟第 20 秒启动就不会推迟整批;若环境准备要 5 秒,准备最迟第 15 秒开始。由完成时刻反推,把资源先给更早到期的 batch。
RL:生成、验证、更新的动态配比
图 11-24 · 只加生成加速器,收益递减
Titer = 生成 + 验证 + 更新 + 发布例 11-4 · 临时 rollout 实例:先得把权重送到
Qwen3-8B BF16 · 16.38 GBTP=2 只是把实例内部的计算分到两张卡,每个实例从外部收的仍是完整 16.38 GB(PolyRL 先收进 CPU 缓冲区再交给 GPU 组)。传完还要加载到显存、确认版本,调度器才派请求。
可用窗口 = max(L − T₀, 0)
临时实例值不值
训练 84 $/h + 6 × 5.3 $/h = 116 $/h盈亏点 116/84 ≈ 1.38×。被提前收回时,保存生成前缀可以带走已完成的工作:PolyRL 把一组响应截到最短的已保存长度(4,000 与 1,000 token → 各取 1,000,复用 2,000,重算 3,000)。但一次 Qwen3-8B 4-bit 对照里,保留前缀省了 96 个 token,总耗时却是 12.8 秒,比从头重做的 11.6 秒还长:前缀重建和重新调度的开销更大。
验证:早提交,按条件估长尾
图 11-26 / 11-27 · 同样 30 秒验证,完成时刻差 20 秒
样本在第 0、10、20 秒陆续生成,每条验证 10 秒,只有一个验证进程。整批完成下界 Tbatch ≥ maxi(ai + ci + gi)(到达 + 编译 + 执行)。
图 11-28 · 已经跑了 e 秒,还剩多久
9 条 1 s + 1 条 100 s · 平均 10.9 s还没结束的样本只可能来自 100 秒那一类。应该用条件剩余时间 E[S − e | S > e],而不是总体平均减去已用时间。DistRS 的调度就用这类条件估计。
省验证资源,却让训练组多等 2 秒
验证少用 60 H100·秒超时 ≠ 停止
一次受控验证中,等待方返回超时后,后台线程还工作了约 130 ms;主动终止子进程后约 0.8 ms 就退出。超时一到就把资源交给下一项而不终止后台线程,两项任务就会同时用这份资源;CUDA 性能验证里,这还会改变测得的时间和奖励。
模型服务的选择与调用成本
每轮重建已把内存从 600 降到 180 GiB,但任务还是 30 秒。要满足 24 秒,只能动关键路径上的模型调用:每副本会话从 32 减到 16,每 token 从 25.3 ms 降到 16.9 ms,355 token 的调用从 9 秒降到 6 秒,三轮 21 秒。模型没换,通过率不变。
例 11-5 · 思考缩到十分之一,成本只降 28%
Sonnet 5 · 输入 $2 / 生成 $10 每百万 token前缀缓存:命中省钱,但要算清谁命中
Ccall = (Iupu + Iwpw + Ihph + O·po) / 10⁶ + Cstorage + Ctool (普通输入 / 缓存创建 / 缓存读取 / 生成)
例 11-6 · 跨轮缓存公共前缀
普通 $2 · 5 分钟缓存创建 $2.5 · 读取 $0.2缓存更省的条件:n·pu > pw + (n−1)·ph,这里第 2 次访问就收回了创建溢价。命中要按 token 加权:两次调用前缀 1,000 和 9,000 token,只有短的命中时请求命中率 50%、读取占比 10%;只有长的命中时还是 50%,读取占比却是 90%。
为命中缓存,值得多排一会儿队
网络 50 ms · A 命中处理 50 ms · B 未命中 500 ms、排队 20 ms例 11-7 · 贵一倍的模型何时反而更省
A Haiku 4.5 · B Sonnet 5 · 每次 20,000 输入(19,000 前缀)| 普通输入 | 缓存读取 | 生成 | 思考 token | 可见输出 | 成功率 | 完整用时 | |
|---|---|---|---|---|---|---|---|
| A · Haiku 4.5 | $1 | $0.1 | $5 | 1,800 | 200 | 0.80 | 10 s |
| B · Sonnet 5 | $2 | $0.2 | $10 | 100 | 200 | 0.98 | 命中 4 s / 未命中 12 s |
B 每项单价都是 A 的两倍,高命中率下却更省:实际生成少、成功率高。期限要求“90% 的提交在 6 秒内通过”,只有 B 命中的 4 秒路径满足,需要 0.98h ≥ 0.90。
自建、按量,还是订阅
快速服务每副本只服务 16 个会话,10 个副本只有 160 个槽,装不下 180 个并发调用,要增到 12 个副本。每次调用占用的 GPU 时间也从 4×9/32 = 1.125 增到 4×6/16 = 1.5 B200·秒。按量 API 则分别限请求数和 token 数,输入输出变多时 token 额度可能先用完。
图 11-33 · 预留 4 张 B200 一个月 vs 按秒计费
Runpod:Pod $6.79/卡时 · Serverless $8.64/卡时预留 F = 4 × 720 × 6.79 = 19,555.2 美元,按量每项 c = 3 × 1.125 × 8.64/3600 = 0.0081 美元。交点 241 万项,对应 78.6% 利用率,正是两种单价之比 6.79/8.64。预留线止于每月 307.2 万项的处理上限。
提高执行效率能省多少
每 token 25.3 ms = decode 15.9 + 输入处理与上下文重建 9.4完整任务的恢复、成本计算与扩容
控制器没收到测试完成的确认,分不清是“测试还没完”还是“完了但确认丢了”。恢复时先按操作标识和输入文件版本查已持久化的结果,有就接着走,没有再判断能否重试。
幂等与去重是两回事
工具执行一次 3 CPU·秒,结果被发了两次:接收方按操作标识去重,仍是 3 CPU·秒;控制器因确认丢失又执行一次,就是 6 CPU·秒。两种情况最后都只有一条结果,资源却不同。付款、建资源、发消息这类外部操作重复调用可能真发生两次,需要目标系统支持操作标识、事务或补偿。
“返回了”不等于“存好了”
write() 返回时数据可能还在内核页缓存里;要等数据落盘,得调用 fsync() 并检查结果。Agent 运行时也要约定持久性语义:报告完成时,只保证答案生成了,还是消息、工具结果和恢复位置都已保存?写进本地页缓存、完成本地 fsync、收到云端确认,承受得住的故障各不相同。
图 11-35 · 异步上传的缺口
点击云端格子切换上传成功 / 失败云端要记的不是“收到的最后一轮”,而是“从起点连续保存到哪一轮”。第 3 轮上传成功,也不能把连续位置从第 1 轮跳到第 3 轮。要删旧日志,先存一份能恢复到那一轮结束时的完整状态;两者都找不到时应返回 incomplete。把一轮“模型决策—工具执行—保存结果”当作一个事务提交,就是 Agent 事务研究的思路。
保存得越勤,正常越慢,故障越省
100 个请求 × 200 ms · 每次提交 8 ms + 每条记录 2 ms一个成功任务到底花了多少钱
失败的尝试也花了钱,不能从总支出里扣掉。Cq = 全部支出 ÷ 通过测试的任务数;Cq,d = 全部支出 ÷ 按时通过的任务数。十项任务花了 1 美元、五项成功,每项实际 0.2 美元;只算成功尝试的 0.5 美元会得出 0.1,漏掉一半。
例 11-8 · 有限恢复树
首次 Sonnet 5 $0.010 / 10 s · 修复 +$0.006 / 4 s · 升级 Opus 5 +$0.030 / 8 s恢复提高了完成比例,也提高了单位完成成本。先修复再升级要 10 + 4 + 8 = 22 秒,超过 20 秒期限:这批任务花了钱、过了测试,却不算按时。把期限拖到 14、18、22 秒看看按时比例怎么跳(练习 11-9)。
模型更快以后,环境准备会重新露出来
图 11-39 · 冷路径 2.7 秒能否藏进模型调用
恢复时要准备两组状态:操作系统管的进程、文件与环境,和模型服务管的 KV。以 V4.1 Flash 会话为例,全局 KV 可以取回,局部 SWA 状态要重放提示末尾的 token 重建。两者能并行时,较晚完成的一项决定等待时间。
例 11-9 · 最终选择
先排除“只加 CPU”:三轮工具共 3 秒,即使工具快一倍也要 27 + 1.5 = 28.5 秒,工具降到零还有 27 秒模型调用。剩下四个组合:
| 方案 | 完成时间 | CPU·秒 | GiB·秒/项 | 平均内存 | $/千项 | 按时通过 | |
|---|---|---|---|---|---|---|---|
| 普通服务 + 全程驻留 | 30 s | 3 | 60 | 600 GiB | 6.68 | 0 | |
| 普通服务 + 每轮重建 | 30 s | 3.3 | 18 | 180 GiB | 6.49 | 0 | |
| 快速服务 + 全程驻留 | 21 s | 3 | 42 | 420 GiB | 8.72 | 95% | |
| 快速服务 + 每轮重建 | 21 s | 3.3 | 18 | 180 GiB | 8.61 | 95% |
模型费用占 98% 以上,所以每轮重建的主要收益是容量,不是钱:每千项按时通过 8.61/0.95 ≈ 9.07 美元,快速 + 驻留即使另配内存也要 9.18 美元,只差 1.2%。时间表:每项到达后第 4–6、11–13、18–20 秒准备,6–7、13–14、20–21 秒跑工具;稳定后最多约 90 个活跃环境(180 GiB),180 个并发调用对 12 个副本的 192 个槽。21 秒完成后只剩 3 秒,再加一次 4 秒的局部修复会到 25 秒,所以三轮后直接结束。
保留还是重建:看等待多长
保留:2G × 0.0000045;重建:2 × 2 × 0.0000045 + 0.1 × 0.000014 = 0.0000194 美元。G ≈ 2.16 秒时相等。模型继续加速,最合适的环境策略也会变。
同样加速 2 倍,加速谁
S = 1 / ((1−f) + f/s)。占执行时间比例越高的部分,加速收益越大。
从“总共做多少”到“按什么顺序做”
一、内存被等待吃掉
平台平均只用 30 核,环境却要 600 GiB,因为三轮里有 27 秒在等模型。在模型调用期间准备环境,每项从 60 降到 18 GiB·秒,代价只是多 0.3 CPU·秒。
二、按阶段分配资源
环境管理器知道模型调用期间有多少时间可用来准备,就让进程只在准备和执行期间存在,持久化文件保存跨轮状态。原本贯穿全程的占用变成按阶段分配。
三、关键路径定方案
内存放开后,模型调用成为 24 秒期限的关键:16 会话/副本,9 → 6 秒,30 → 21 秒,副本 10 → 12。最后才在满足质量和期限的方案之间比成本。
同样的方法也用于 RL:成组作业先取得配套资源,临时 rollout 实例先完成权重传输和加载,样本生成后立即提交验证,失败重试累加各次尝试的工作量。分析资源需求要算总工作量,分析完成时间还要看这些工作的先后与并行。下一章在相同任务下比较端侧、边缘和云端执行。
动手验证
实验 11-1〔核心〕· 从多轮任务推算模型并发、CPU 与环境内存
每秒 8 项任务,一半两轮、一半四轮;每轮模型 6 秒、工具 1 核 1 秒。求模型调用率、平均并发调用、平均 CPU 核数;环境全程 2 GiB 时的平均内存;只在每轮 2 秒准备 + 1 秒执行期间保留时的平均内存与每秒创建数。(设计台可分别按 2 轮、4 轮各算一半。)
实验 11-2〔核心〕· 重建环境与恢复快照
环境里有可重新下载的依赖、未保存的编辑缓冲、已持久化的补丁和已提交的外部写入,逐项说明故障后从哪里恢复。再比较冷路径重建 2.7 秒,与保存 2 GiB 快照 8 秒 + 恢复 1 秒,在下一次模型 9 秒 + 工具 1 秒的窗口里各自的环境驻留时长。
实验 11-3 · 提前准备省多少等待、占多少内存
创建 2 秒,预测准确率 0.6,分别提前 1、2、4 秒准备,求期望等待与错误准备的期望内存占用;准确率提到 0.9 时哪些变好。(图 11-15 的演示可直接调 P、L、h。)
实验 11-4 · 迁移何时优于等待
等待 12 秒,本地执行 20 秒,迁移耗时 m 并让原作业推迟 m+2 秒。分别以“新作业最早完成”和“新作业等待 + 原作业延迟之和最小”为目标求条件。
实验 11-5 · 启动耗时与短期实例产出
准备 8 秒,之后 100 token/s;三个实例可用 10、20、40 秒,求产出和有效比例;准备降到 4 秒后谁改善最多。
实验 11-6 · 验证进程数与长尾
样本在 0、6、12 秒到达,验证 10、2、8 秒;一个进程与两个进程时整批何时完成;最后一条改为 80 秒,加进程还有没有用。
实验 11-7 · 命中与正确性相关时
B 命中时成功率 99%、未命中 90%,推导每个成功任务成本与 6 秒内通过比例随命中率的关系,求达到 90% 按时通过的最低命中率。
实验 11-8 · 自建与 API 的成功任务成本交点
自建 19,555.2 美元/月、成功率 90%;API 每项三次调用、每次 0.0088 美元、成功率 98%。求交点;自建上限每月 50 万项时怎么决策。
实验 11-9 · 期限与提前停止
期限改为 14、18、22 秒求按时通过比例(恢复树演示可直接拖期限);再设计“剩余时间不足以完成下一节点就停止”的策略,重新算期望成本。
实验 11-10〔核心〕· 到达率提高后扩容
到达率 15 项/s,每次输出 118 token,快速服务调用降到约 2.0 秒,其余不变。比较全程驻留与每轮重建,决定加哪项资源、加多少(设计台:到达率 15、模型 2 秒、16 会话)。再画出 15 项同时到达时的工具执行时间线与 CPU 并发峰值。