Pie 阅读笔记:谁来控制大模型的生成循环

笔记日期: 2026 年 10 月 8 日
笔记作者: Zhongzhu Zhou
论文题名: Pie: A Programmable Serving System for Emerging LLM Applications
论文作者: In Gim, Zhiyao Ma, Seung-seob Lee, Lin Zhong
论文编号与版本: arXiv:2510.24051v1,2025 年 10 月 28 日

1. 从一次工具调用看清问题

读 Pie 时,我最先想到的是一个常见场景:模型写到一半,需要查资料或执行代码,于是把控制权交还给客户端;客户端调用工具,把结果加进对话,再提交一次生成请求。业务上,这是一段连续思考;服务引擎看到的,却可能是两次相互独立的请求。对话状态要不要保留、哪些内容能够复用、下一步先做什么,都藏在这个接口边界里。

传统生成接口让应用提交提示词及少量选项,随后由引擎接管预填充、解码、采样、批处理和缓存管理。对于普通文本补全,这种分工很合理:引擎集中优化一个稳定的循环。但应用一旦要搜索多条推理分支、按语法动态限制输出,或者穿插多次外部操作,统一循环就未必能看见业务真正的依赖关系。

Pie 的主张是让应用提交一个程序,称为 inferlet。程序运行在服务端附近,通过细粒度接口控制嵌入、前向计算、概率分布读取、缓存和 I/O。多个程序共享一个控制层,由控制层继续负责把适合一起执行的操作组成批次。这里最关键的变化,是应用获得了生成步骤与状态生命周期的控制权。

inferlet 与共享处理器

图 1(论文 Fig. 2):inferlet 发出 API 调用,共享控制层把调用交给相应处理器。图片裁自 Gim 等人的论文;应用决定流程,系统仍然统筹执行。

这种控制权为什么有用?搜索程序知道哪些分支已经淘汰;工具型智能体知道网络请求返回后还会继续使用当前上下文;受约束解码器知道此刻哪些 token 符合语法。把这些知识留在客户端,服务引擎可能只能猜。通过接口直接表达出来,系统就有机会省掉不必要的重算、往返或状态保留。

但控制权也有成本。原来紧密衔接的 GPU 操作被接口拆开,可能失去流水重叠;应用保留缓存等待工具,也会占住别人需要的显存。因此我理解的核心问题是:业务知识究竟节省了多少工作,能否覆盖开放控制接口带来的代价?这比只问“Pie 是否更快”更有解释力。

这份笔记以 16 页 arXiv v1 为准,已读正文与参考文献,该版本没有附录。文中明确区分论文报告、自行复算、解释性例子和后续实验建议。推导和伪代码用于理解方法,不代表我运行了新的性能实验。官方项目入口供查阅资源,后续版本的功能不能倒推为这篇论文已经验证过的结论。

2. 前置知识:缓存保留的是计算状态

2.1 KV 缓存为什么既省计算又占资源

自回归 Transformer 每产生一个新位置的查询,就要访问此前位置的 key 和 value。KV 缓存保存已经计算好的投影状态,使解码时不用把整个历史重新送过模型。它不是一份“更快的文本副本”,而是特定模型、token、位置和注意力规则共同产生的计算结果。

这意味着复用有条件:模型参数、适配器、位置编号或可见历史发生变化,缓存是否仍然有效就需要重新判断。两个字符串看起来相似,也不代表它们的状态可以直接互换。理解这一点,才能看清 Pie 为什么要显式管理资源和位置,而不是只增加一个“保留对话”的开关。

设模型有 LL 层,每层 HkvH_{\mathrm{kv}} 个 KV 头,每头维度为 dd,保留 TT 个 token,每个标量占 bb 字节,则一个理想化的稠密缓存需要:

MKV=2LHkvdTb.(1)M_{\mathrm{KV}}=2L H_{\mathrm{kv}}dTb. \tag{1}

推导并不复杂:一个 token 在一层有 HkvdH_{\mathrm{kv}}d 个 key 元素,同样多的 value 元素,所以乘以 2;再乘层数、token 数和单元素字节数。这里没有计入页表、对齐、分配器和临时缓冲区,因此它是解释性容量模型,不能直接等同实际显存读数。

例如取 L=32L=32、Hkv=8H_{\mathrm{kv}}=8、d=128d=128、BF16 的 b=2b=2,每个 token 对应 131,072 字节,即 128 KiB;8,192 个 token 约占 1 GiB。十个这样的独立会话同时等工具返回,即使 GPU 暂时不为它们计算,也可能被占住约 10 GiB。这个例子说明,缓存复用同时是一项计算决策和容量决策。

2.2 分页解决连续分配,不消除保留成本

分页缓存把长序列拆成若干页。若每页装 PP 个 token,序列需要 ⌈T/P⌉\lceil T/P\rceil 页,最后一页的未用空间为:

W(T,P)=P⌈T/P⌉−T,0≤W(T,P)<P.(2)W(T,P)=P\left\lceil T/P\right\rceil-T, \qquad 0\le W(T,P)<P. \tag{2}

因此分页可以避免为每个请求一次性预留最大长度,但已经保留的状态仍然占空间。Pie 描述的 KvPage 通常对应 8–32 个 token。论文中的某些代码示例为了说明接口,暂时把一页当成一个 token;不能把这个教学简化误当实际物理配置。

此外,“页”与“token 级操作”并不矛盾。分配粒度可以是页,而掩码或复制接口仍然按 token 描述需要的区域。读论文时最好分别问:内存如何分配、注意力能看哪些位置、应用拿到什么句柄。这三件事处在不同层次。

2.3 忘掉历史后,重算会怎样累积

设一次任务包含 RR 轮交互,初始提示词有 P0P_0 个 token,每轮之后又增加 uu 个需要预填充的新 token。如果每次都重放全部文本,与保留有效缓存相比,累计预填充 token 数分别是:

Creplay=RP0+uR(R−1)2,Cretain=P0+u(R−1).(3)C_{\mathrm{replay}} =RP_0+\frac{uR(R-1)}{2}, \qquad C_{\mathrm{retain}}=P_0+u(R-1). \tag{3}

第一项来自等差数列求和:第 rr 轮输入长度为 P0+ruP_0+ru,其中 rr 从 0 到 R−1R-1。保留状态后,初始前缀只处理一次,之后仅处理新增内容。该式不计解码生成本身,也假定缓存有效且没有被驱逐。

重放历史的累计预填充成本

图 2(原创解释图):按式(3)绘制,初始提示词 1,000 token,每轮新增 200 token。纵轴是累计预填充 token 数,不是测得的延迟或加速比。

真实系统中,自动前缀缓存可以减少重放,长提示的 GPU 利用率也会变化,所以不能把图上的 token 比值直接当时间比值。它的作用,是把“持续会话被拆成独立请求”可能引入的重复工作画出来。Pie 给应用一个明确保留状态的接口,但仍要面对保留多久、何时释放的问题。

3. 三层架构把哪些责任分开了

Pie 分为应用层、控制层和推理层。应用层运行隔离的 Wasm 程序并管理其生命周期;控制层处理资源请求、管理命令队列、组织批处理和分发完成事件;推理层保存物理资源,执行实际的 GPU 操作。应用决定做什么、何时等待,控制层决定哪些工作适合一起送下去,推理层负责把它们高效算出来。

应用操作的两类基本资源是 Embed 和 KvPage。前者表示输入或输出的嵌入状态,后者表示注意力缓存页。分配接口返回不透明句柄,而不是任意 GPU 地址。每个 inferlet 拥有自己的虚拟资源地址空间,控制层把它映射到真正的存储位置。

这层间接性不是多余包装。若应用直接持有物理指针,系统就很难隔离、迁移或统一管理资源。句柄允许应用表达使用关系,同时让系统保有物理资源的管理权。通过 import/export 接口可以有意共享缓存,但“能共享”不等于已经定义好完整的授权机制、撤销规则和持久性保证。

另一个关键抽象是 命令队列 Queue。它描述一串有顺序关系的调用,让批处理器知道哪些操作存在依赖。多个队列可以暴露彼此独立的工作;同一队列中的调用则不能随意交换。需要结果的异步调用以 future 的形式返回,程序在真正需要结果的地方等待。

因此,单线程 inferlet 并不意味着每一步都同步阻塞。事件驱动程序可以启动一个网络请求,再推进另一段独立逻辑,最后等待结果。这里的并发来自多项未完成操作的交错,而不是让应用自己管理若干操作系统线程。对经常等待 I/O 的智能体,这是合适的执行模型。

读接口时,我会刻意区分三个时刻:发出调用、完成执行、观察结果。把 forward 放进队列,不等于输出已可读;把一块缓冲区传给异步操作后,也不能马上当作闲置空间重复分配。虽然高层库可以隐藏常规细节,这些生命周期关系仍是整个模型正确性的基础。

论文把能力组织成 trait,例如 Allocate、Forward、InputText、InputImage、Tokenize、OutputText。trait 之间可以有依赖,应用能够查询模型支持哪些能力。论文描述的接口总数为 42,其中 18 个与模型执行和资源管理相关。这是所读版本的设计概貌,不是对未来版本接口数量的固定承诺。

前向接口尤其值得注意:应用可以指定输入嵌入、已有缓存、输出位置和注意力掩码,资源还关联显式位置编号。因此它可以改变后续查询直接看到的上下文。不过,假设 C 的状态曾经注意过 A 和 B,之后把 B 从 D 可见的缓存中移走,并不会抹掉 C 内部已经编码的 B 的影响。遮掉旧缓存不等于重新计算一个从未出现过该 token 的世界。

这也是“开放缓存控制”的语义边界。接口能表达注意力规则,但规则变化后的质量与等价性还需要论证。对于某些近似策略,变化本来就是有意的;对于声称无损的复用,就必须保持更严格的条件。

最后,Pie 把内核融合、量化和张量并行等底层工作留给推理层。应用不必因此去实现 GPU 内核,但如果某项新策略需要接口没有暴露的中间量,后台仍可能需要新增能力。可编程性意味着能组合已有能力,并不自动意味着能访问模型内部的所有信息。

4. 把最普通的生成循环拆开看

算法 1:显式管理状态的自回归生成

下面按论文的编程模型重述步骤,名称用于解释,不是可直接运行的 API 示例。为突出状态关系,先一次性准备所需缓存;按需增量分配属于另外一项策略。

01  确认模型支持文本输入和文本输出。
02  建立队列 q,并把提示词转成 token ID。
03  分配输入嵌入、一个输出槽及所需 KV 页。
04  按真实位置编号,将提示词嵌入操作加入队列。
05  加入前缀前向计算,保存 KV 和最后的输出表示。
06  在未达到生成上限时重复:
07      等待从当前输出表示取得下一个 token 的分布。
08      应用采样规则,选择 token x。
09      输出 x;若满足终止条件,则结束循环。
10      在下一个序列位置嵌入 x。
11      使用有效历史与 x 的嵌入执行前向计算。
12      保存 x 的 KV 状态及新的输出表示。
13  等待所有仍在使用资源的操作完成。
14  释放不再需要且未共享的嵌入和缓存页。

这个拆分最容易暴露一个细节:被预测出来的 token,与已经经过模型计算的 token,不是同一个状态。 模型根据已有最后位置的表示预测下一个 token;选出 token 后,还要把它送入模型,才会得到它自己的 KV。若刚完成采样就把缓存长度当作已经增长,而对应 forward 尚未完成,程序描述的历史就超前了。

同样,位置编号不能只用“当前缓存数组下标”替代。应用可能保留不连续的上下文,或者组合多个分支的状态。物理存储位置和原始序列位置各自承担不同作用,不能因为页面被搬动、筛选或共享,就随意改变模型看到的位置关系。

普通用户不需要每次都手写以上操作。论文提供 Context 等高层抽象,把生成、停止条件、采样和常见分支操作包装起来。直接 API 更像高层库的基础:常见流程交给库维持不变量,特殊流程再下沉到较细粒度接口。若把所有资源责任都暴露给业务开发者,少数特殊场景的灵活性可能换来大量普通场景的错误。

我会把接口分为三个层次理解。完成式端点最简单,适合一个提示接一段输出;Context 库暴露状态与结构,但维持常见约束;直接资源操作能力最强,也需要最多正确性判断。论文的意义不是要求每个人都使用最低层,而是让新策略能够在这一层被表达,再打包成别人可复用的组件。

一个概念上的等价性检查是:整段提示词一次前向,与先处理前缀、再借助前缀缓存处理后半段,是否表达同一个因果计算?只有模型、位置、掩码和相关数值约定一致时,才有理由期待等价。这里提出的是阅读与实验设计上的判断标准,不是声称已经实测。

还要考虑错误路径。工具超时后,程序是继续持有缓存重试,还是释放资源结束?如果资源紧张时 inferlet 被终止,客户端还能恢复哪部分状态?一个“成功路径”只有十几步的程序,到了共享平台中仍需要明确失败、取消和清理的含义。Pie 给出了表达这些流程的位置,但不能把“可以编程”直接理解成“所有失败行为都已定义完善”。

5. 批处理的第一原则是不能改变依赖

把流程交给应用,并不意味着每个应用可以独占 GPU。控制层必须重新找到不同程序之间可以共享执行的部分。Pie 提出横向与纵向两种批处理:横向把不同队列中同类型的操作组合起来;纵向把同一队列中连续、兼容的操作组合起来。这里的“纵向”不是沿 Transformer 层切分的流水并行。

横向和纵向批处理

图 3(论文 Fig. 4):同一类型的调用可以跨队列组合,也可以在满足依赖条件时沿队列组合。图片裁自 Gim 等人的论文。

先用一个保守的资源模型理解兼容性。设操作 aa 读取资源集合 R(a)R(a)、写入集合 W(a)W(a)。若要把 aa 和 bb 当成相互独立的工作并发执行,一个充分条件是:

W(a)∩(R(b)∪W(b))=∅,W(b)∩(R(a)∪W(a))=∅.(4)W(a)\cap\bigl(R(b)\cup W(b)\bigr)=\varnothing, \qquad W(b)\cap\bigl(R(a)\cup W(a)\bigr)=\varnothing. \tag{4}

第一条防止 aa 写入 bb 要读写的区域;第二条反向检查同类冲突。两个操作都只读同一页则没有这种问题。这个集合表达式是笔记中的解释,并非论文给出的调度定理。

还必须区分“相互独立”与“能够融合”。一个专门的处理器可以在单个批次内部保留先后依赖,因此独立执行的条件只是充分条件,不是所有合法批处理的必要条件。论文就提到:前缀 forward 及使用该前缀的后续 forward,可以在满足语义的情况下纵向合并。不能看见“同一批次”就默认里面的操作被无序并发了。

Pie 按处理器类型形成候选批次。不同类型的操作不能因为来自同一个应用,就自动一起算;嵌入、前向和分布读取各自有执行路径。在批次内部,队列优先级影响顺序,超过后端最大批量时从尾部截断;不同候选批次之间,选择含有最老待处理调用的那个。

算法 2:随 GPU 空闲事件触发批处理

下面概括论文第 5.2、6.1 节的策略。兼容性检查被写成抽象步骤,不能把它当作论文已经穷尽所有调度细节。

01  新调用到达时,识别所属队列及资源依赖。
02  控制层能够直接处理的操作走控制路径。
03  GPU 操作进入相应队列,保留顺序信息。
04  GPU 正忙时,继续积累新调用。
05  收到 GPU 空闲通知后:
06      按处理器类型收集当前可执行调用。
07      横向组合不同队列里的兼容调用。
08      加入能保持语义的纵向组合。
09      按队列优先级整理每个候选批次。
10      将候选截断到后端允许的批量上限。
11      派发包含最老待处理调用的候选批次。
12  返回完成事件,使依赖结果的后续调用可执行。

这个策略称为 work-conserving:只要 GPU 已空闲且有可执行工作,就不故意等待。GPU 正在计算时自然积累请求,计算结束再形成下一批。这样避免为每个小调用单独派发,也不需要在所有负载下固定等待同样长的时间。

替代方案各有边界。凑够固定数量再发,在低流量下可能等很久;固定计时器可以限制等待,但不能适应所有操作时长;每个调用立即发,则可能牺牲并行与摊销。Pie 的选择很朴素,也便于说明架构本身的收益。

不过,不空闲不代表尾延迟最优,更不代表公平。一个大 forward 批次可以让短操作等待;高优先级队列也可能反复占先。不同 API 类型之间选择最老调用,不等于给每个用户相同的资源份额。论文用吞吐实验证明策略在特定负载下有效,但没有由此解决完整的多租户调度问题。

6. 工具调用与模型状态如何放进同一个程序

Pie 最直观的收益,出现在生成和外部操作频繁交替时。传统部署由远端客户端接收工具调用,再去访问工具,最后重新向模型发请求。inferlet 可以在服务端发出外部请求,同时保留当前模型状态。网络上的工具依然需要时间,变化的是编排位置和状态是否跨越这次等待。

两种工具编排的位置

图 4(论文 Fig. 5):传统客户端与 Pie 把工具处理逻辑放在不同位置。图片裁自 Gim 等人的论文;把处理器移近推理服务,不会让外部工具的执行时间消失。

用一个简化分解可以看清收益来源。GG 表示两种方案共有的模型工作,II 是不可避免的工具耗时,mm 次编排往返各耗时 rr,CC 是可以避免的上下文重算;inferlet 每个输出 token 多付 δ\delta,共生成 NN 个 token,启动开销为 LL,其他调度成本为 SS:

Tremote=G+I+mr+C,Tinferlet=G+I+Nδ+L+S,ΔT=mr+C−Nδ−L−S.(5)\begin{aligned} T_{\mathrm{remote}}&=G+I+mr+C,\\ T_{\mathrm{inferlet}}&=G+I+N\delta+L+S,\\ \Delta T&=mr+C-N\delta-L-S. \end{aligned} \tag{5}

先把共同项相减,剩下的就是获益条件:省掉的往返与重算,要多于额外控制、启动和调度成本。这是解释模型,真实批处理也可能改变 GG,工具并发可能改变关键路径上的 II,所以不能把它当未经验证的性能预测器。

例如,减少 8 次各 20 ms 的往返,共省 160 ms;若生成 200 个 token,每个多付 1.5 ms,就多花 300 ms。只靠消除往返,在这组示例数字下反而不划算。但若还能省掉 1 秒重复预填充,结果就变成明显获益。这里所有输入都是说明性假设,用来找边界,不是论文实验参数。

算法 3:保留状态并穿插工具调用

论文第 7.2 节的定制工作流可以概括为以下过程。是否允许提前执行,必须由应用语义决定。

01  读取任务和允许使用的工具说明。
02  导入仍然有效、且有权复用的前缀状态。
03  逐步生成,同时保留当前对话的 KV。
04  识别完整并已验证的工具名及参数。
05  若调用独立且允许提前执行:
06      异步发起调用,保存完成结果的 future。
07  否则等待相关生成依赖得到确认。
08  仅在后续计算真正需要时等待工具结果。
09  把必要观察加入上下文,继续生成。
10  若某工具说明能够确认不再需要:
11      按选定策略处理其上下文保留。
12  释放任务私有状态;只导出可复用的状态。

论文选择的工作流具有三个特点:一部分工具说明经常复用,多数调用发出后无需立即等待,多数工具只调用一次。于是可以分别保留常用说明的缓存、尽早并发调用,以及移除已经用完的说明。这三项优化分别利用复用频率、依赖结构和状态寿命。

我在伪代码中加上“完整并已验证”与“能够确认”,是为了避免把前提藏起来。模型刚输出工具名,不代表参数已经完整;一次调用失败后,所谓单次工具可能需要重试;不等待返回也不代表外部动作可以随意重复。若提前执行改变了任务含义,速度更快并不能说明优化正确。

同样,缓存掩码可能改变后续预测。应用需要说明这段工具定义为何不再影响任务,而不是只报告显存下降。对于只做状态保留的流程,语义问题较小;对于主动删减可见上下文的流程,质量检查就是结果的一部分。

这也揭示了一条责任边界:应用可以知道自己的历史很有用,却不知道整个集群还有多少用户在等待。保留决策需要应用知识,准入和回收则需要全局视角。Pie 提供了表达前者的接口,但后者不能仅靠把更多策略交给单个应用来解决。

7. 把采样交给应用,接口不能只考虑常见路径

采样是另一个很容易被“开放接口”四个字掩盖成本的地方。模型前向计算最终给出词表分布,但应用一般只需要一个 token。若推理引擎内部直接完成采样,传回一个 token 即可;若采样策略由应用控制,分布信息就要跨过推理层与控制层的边界。

Pie 默认返回前 256 个候选 token 及其分布信息。论文以约 128K 词表为例解释,这远小于返回完整向量。它适合很多常见的低温采样或 top-k 策略,但“返回得更少”和“表达任意策略”之间有明确张力。若某个语法状态只允许概率很低的符号,合法 token 可能根本不在默认候选集中。

为了看清这个边界,设原始分布为 pp,接口返回的 token 集合为 KK,这些 token 总概率为 ZKZ_K。如果直接将它们重新归一化,得到:

ZK=∑i∈Kpi,qi={pi/ZK,i∈K,0,i∉K.(6)\begin{aligned} Z_K&=\sum_{i\in K}p_i,\\ q_i&= \begin{cases} p_i/Z_K,&i\in K,\\ 0,&i\notin K. \end{cases} \end{aligned} \tag{6}

这个操作改变了分布。用总变差距离衡量,集合外的差异总和为 1−ZK1-Z_K;集合内因为统一放大,差异总和同样为 1−ZK1-Z_K。因此:

TV⁡(p,q)=12∑i∣pi−qi∣=12[∑i∈Kpi(1ZK−1)+∑i∉Kpi]=1−ZK.(7)\begin{aligned} \operatorname{TV}(p,q) &=\frac12\sum_i|p_i-q_i|\\ &=\frac12\left[ \sum_{i\in K}p_i\left(\frac1{Z_K}-1\right) +\sum_{i\notin K}p_i \right]\\ &=1-Z_K. \end{aligned} \tag{7}

这段推导是本文对接口边界的分析,并非论文报告的误差保证,也不表示 Pie 的所有采样器都会进行这种截断再归一化。它只说明:若把截断结果当成完整分布使用,丢失的概率质量就是一个可以明确描述的问题。

对普通生成来说,遗漏低概率尾部也许符合预期;对强约束解码来说,问题不同。假设全部合法 token 的总概率只有 0.001,而且都在集合外,原分布仍然有合法选择,截断分布却会错误地告诉应用“无路可走”。此时不能简单把温度调低,需要扩大候选集、取回完整分布,或者将过滤操作放到推理层的专用 handler 中。

这些替代方案的代价各不相同。完整分布增加传输和处理;专用 handler 保留效率,却要求系统支持新的操作契约;动态扩大候选集增加额外轮次。一个可扩展接口应当让应用识别何时走到了边界,而不是在后台静默改变算法的含义。

这也是理解 Pie 的一个尺度:论文展示的是一组有用而可扩展的原语,并没有证明默认接口在零开销下覆盖全部解码方法。可编程性应该允许显式地处理例外路径,不能只在最常见的采样配置下成立。

8. 如何读工具调用实验:先看部署条件,再看加速

论文主要实验使用 GCP g2-standard-32 实例和一张 24 GB NVIDIA L4,模型为 Llama 3 系列的 1B、3B 和 8B,权重、激活及 KV 使用 BF16。比较对象包括 vLLM 0.6.0、SGLang 0.4.4、LMQL 0.7.3 和 StreamingLLM。Pie、vLLM、SGLang 使用共同的 FlashInfer 内核基础,这有助于减少一部分底层差异,但不能消除所有调度与执行路径差异。

我尤其在意客户端位置:Python 客户端位于校园网络,而服务端在 GCP。也就是说,消除应用与模型服务之间的往返不是一个抽象论点,而是在这组实验中能够实际影响结果的条件。将两者放到同一进程、同一台机器或高延迟广域网,都可能改变获益幅度。

论文在 1B 模型上测试 ReACT、CodeACT 和 Swarm。前两者每次工作流有 8 次 I/O,Swarm 有 32 次。以下是 Pie 的绝对数值,保留它们是为了避免只有百分比却看不出工作量:

工作流I/O 次数Pie 延迟Pie 吞吐
ReACT84.27 秒29.94 agent/秒
CodeACT83.18 秒40.18 agent/秒
Swarm326.14 秒5.21 agent/秒

这里的 agent/秒是完整工作流的完成速率,不是 token/秒,三个任务的吞吐不能脱离各自工作量直接比较。比如 Swarm 的单次任务包含更多交互,它的完成速率较低并不代表 GPU 的 token 处理能力一定更差。

三种工具工作流的归一化延迟与吞吐

图 5(论文图 6):三种工具工作流的延迟和吞吐分别归一化比较。图来自 Gim 等人的原论文,不能把不同工作流的柱高直接当作绝对秒数比较。

ReACT 的比较中,作者报告延迟最多降低约 15%,吞吐最多提高约 30%。当交互次数少于 2 时,论文设置下没有明显差异。这一点比一个总括的“更快”更有用:它提示我们,收益来源与应用的控制流密度有关。若程序只要求一次连续补全,就没有多少中间往返可以省。

随着模型变化,主导机制也会变化。对于较小的 1B 和 3B,减少远程往返比较显著;对于 8B,保留 KV、避免重新预填充更重要。不能把小模型上的解释原样搬到大模型,也不能只用 token 数预测整个 agent 的耗时。第 6 节的收支模型至少要求分别估计往返、重算与额外控制成本。

组合三类应用优化后的吞吐

图 6(论文图 7):特定工具工作流逐步加入工具定义缓存、提前执行和已用说明处理后的吞吐。图中的最高收益依赖该工作流的复用及依赖前提。

定制工具工作流进一步组合三项优化,图 7 给出相对基线 vLLM 约 3.5 倍的吞吐。这不是一般聊天、所有工具代理或任意输入都能获得的常数。应用必须真的复用工具定义,调用必须允许提前执行,被处理的说明也必须不再是后续任务所需的信息。

对我来说,这组实验最有说服力的是机制链条:应用表达生命周期,系统保留必要状态,并减少不必要的边界往返。最需要额外证据的则是可迁移性。工具延迟分布、上下文长度、同机客户端、不同 KV 容量压力下,三项优化各自还能保留多少收益,论文没有给出完整答案。

9. 表达能力实验说明了什么,又没有说明什么

Pie 还展示了多种生成流程,包括简化的 Tree of Thoughts、Recursive of Thoughts、Graph of Thoughts 和 Skeleton of Thoughts。它们共同要求应用能安排多次模型操作,保存局部结果,并在搜索分支间分配状态。用传统单次补全接口也可以组合其中一些流程,但通常需要应用反复调用服务、重新表达上下文,或依赖服务端预先支持某一种搜索策略。

论文的 RoT 设置使用深度 5、最多 32 个分支,报告延迟最多降低 28%、吞吐最多提高 34%。这个结果支持“较复杂的生成流程可以从靠近模型状态的控制中获益”,但不应当被扩写成复杂推理能力本身提高了。生成过程更高效,与输出在同样预算下更正确,是不同的实验问题。

多种生成流程的归一化比较

图 7(论文图 8):多个生成流程的归一化延迟和吞吐比较。叉号表示该项未获支持或无法按该设置比较,不能将其当作零吞吐或无限延迟。

图中“不支持”的标记也需要认真处理。若某个框架缺少相应接口,说明实现这一流程的方便程度不同;它不能证明该框架使用任何扩展方式都做不到。反过来,为一个框架添加专门实现也有工程成本,不能假装该成本不存在。表达能力与某个已优化算子的速度需要分开讨论。

投机解码部分使用的是基于 n-gram 的 prompt lookup。这个选择能展示 inferlet 如何安排候选与验证,却不能直接代表带学习型草稿模型的投机系统。后者还涉及第二套权重、草稿与目标模型调度、候选接受率,以及额外显存竞争。读到“支持投机解码”时,应当继续问支持的是哪一种、承担了什么工作量。

StreamingLLM 比较更需要保留作者自己的限定。论文报告约 1.5 倍延迟优势和超过 30 倍吞吐优势,同时承认其参考实现采用的内核基础不同,底层执行差异贡献很大。因此,这组数字可以说明该 attention-sink 方法在 Pie 的后端中能够高效表达,不能单独归因于可编程控制层。

类似地,表 2 中简短的 inferlet 代码行数说明应用接口有一定紧凑性,但共用库和基础设施并没有消失。几行搜索调度能成立,是因为内存管理、上下文抽象和模型 handler 已经提供了大量服务。代码行数既不是开发时间,也不是调试难度,更不能替代可靠性评价。

我会把这些案例当成“接口覆盖面”的证据:应用级控制确实能容纳多种不同策略。若要判断部署价值,还需要各自补上质量、并发、资源和失败恢复的维度。一个方法可以很容易表达,但在高负载下不值得启用;也可能只在某些模型和上下文规模下有明显优势。

10. 开放控制的开销:最大的部分并不是 Wasm

10.1 每 token 的小数,累积起来是什么

论文表 3 把 8B 模型下的生成开销拆开。基线每输出 token 用时为 64.06 ms,Pie 为 65.59 ms,相差约 1.53 ms。最大增量来自不能继续与其他工作重叠的采样,约 1.320 ms;其次还包括不能重叠的 embedding、调度和返回分布等。

将表中各项相加:

Δtitems=1.320+0.070+0.050+0.070+0.006+0.001+0.001=1.518 ms.(8)\begin{aligned} \Delta t_{\text{items}} &=1.320+0.070+0.050+0.070\\ &\quad +0.006+0.001+0.001\\ &=1.518\ \text{ms}. \end{aligned} \tag{8}

它与总差 1.53 ms 接近但不完全相同。四舍五入或各项独立测量可以造成这种小差异,不能仅凭 0.012 ms 的缺口就断言结果错误。真正值得抓住的是数量级:应用与控制层边界、Wasm 执行各约 0.001 ms,而失去的流水重叠大得多。

因此,把整个问题简化成“Wasm 太慢”会误判优化方向。如果应用需要在一次结果返回后决定下一次操作,系统就不再总能提前准备后续步骤。即使用本地机器码替代 Wasm,也不会自动恢复被应用依赖切断的重叠。

表 4 给出的三个模型对应关系是:

模型规模基线 TPOTPie TPOT绝对增量相对增量
1B16.83 ms18.75 ms1.92 ms11.41%
3B30.30 ms32.01 ms1.71 ms5.64%
8B64.06 ms65.59 ms1.53 ms2.39%

TPOT 表示每个输出 token 的时间。相对增量的计算方法为:

roverhead=tPie−tbasetbase,r1B=18.75−16.8316.83≈11.41%,r8B=65.59−64.0664.06≈2.39%.(9)\begin{aligned} r_{\text{overhead}} &=\frac{t_{\text{Pie}}-t_{\text{base}}}{t_{\text{base}}},\\ r_{\text{1B}} &=\frac{18.75-16.83}{16.83}\approx 11.41\%,\\ r_{\text{8B}} &=\frac{65.59-64.06}{64.06}\approx 2.39\%. \end{aligned} \tag{9}

绝对增量相近时,小模型原本的 token 时间更短,因此相对损失更大。这并不与前面“小模型更容易受往返影响”矛盾:它同时更容易从减少远程交互中受益,也更容易被固定控制成本伤害。最后取决于工作流省下多少次往返,而不是只看参数规模。

10.2 启动成本应该摊到完整任务上

论文报告 warm inferlet 启动约 10–50 ms,cold 启动约 35–81 ms;使用预建 Wasm 实例池,测试并发最高到 896,池中预留 1000 个实例。这说明启动成本可以被控制,但仍然不是零。

若完整任务除启动外需要 TT,启动需要 LL,启动在总耗时中的占比为:

flaunch=LT+L,flaunch(T=1 s,L=0.05 s)≈4.76%,flaunch(T=0.05 s,L=0.05 s)=50%.(10)\begin{aligned} f_{\text{launch}}&=\frac{L}{T+L},\\ f_{\text{launch}}(T=1\text{ s},L=0.05\text{ s}) &\approx 4.76\%,\\ f_{\text{launch}}(T=0.05\text{ s},L=0.05\text{ s}) &=50\%. \end{aligned} \tag{10}

这两个数是说明性计算,不是额外实测。长时间的工具代理通常更容易摊薄启动;很短的连续补全则未必需要启动独立 inferlet。由此可以自然想到混合服务:普通补全使用成熟快路径,需要控制的任务再进入可编程路径,而不是强迫所有请求支付同样成本。

API 边界的延迟测量

图 8(论文图 10):不同并发下的 API 延迟。该实验关闭批处理,且不把推理 handler 的执行时间算入,因此不能直接把图中数值视为端到端 token 时间。

正文称控制层 API 延迟低于 30 微秒,但图 10 的最高点看起来更接近 40 微秒。因此,更稳妥的解读是“几十微秒量级”,而不是严格的 30 微秒上界。推理层 API 约 10–300 微秒,并发上升时有变化。但这些 API 是异步的,且测量排除了 handler 执行。把所有 API 延迟机械相加再乘调用次数,不一定得到关键路径时间;同样,低微秒级边界成本也不能证明调度对尾延迟完全没有影响。

10.3 批处理表格要独立算一次

表 5 给出 Eager、K-only、T-only、Adaptive 四种策略吞吐,分别为 5.61、30.09、78.11、84.85 请求/秒。按这些表中数字计算:

84.855.61≈15.12,84.8530.09≈2.82,84.8578.11≈1.086.(11)\begin{aligned} \frac{84.85}{5.61}&\approx15.12,\\ \frac{84.85}{30.09}&\approx2.82,\\ \frac{84.85}{78.11}&\approx1.086. \end{aligned} \tag{11}

也就是说,自适应策略约为 eager 的 15.12 倍,比 K-only 高约 182%,比 T-only 高约 8.6%。这与相邻正文所写的“17 倍”以及相对两种静态策略的“8–40%”不能完全对应。本文保留表格原值并给出可复算的比例,不替作者猜测是否存在另一组配置。

重新呈现每 token 成本与批处理吞吐

图 9:依据论文表 4、表 5 的数据重绘。左图是每 token 时间,右图是四种批处理策略的吞吐;不同子图单位不同。本图不包含新的性能实验。

表格仍然清楚地支持“批处理很重要,自适应策略在此设置下最好”,但不支持无条件照抄正文中的每一个倍数。数字不一致不是否定整个设计的理由,却应当限制我们在摘要、宣传或横向比较中使用多精确的结论。

11. 几个设计选择,为什么这样做

11.1 为什么应用程序要放在模型附近

最直接的替代方案是保留远程客户端,把更多配置项塞进 completion API。这种方式对已知、固定的策略很方便,也容易维护兼容性。但当应用需要根据中间结果决定下一步,参数组合很快会膨胀成一种隐蔽的编程语言。与其不断增加特殊开关,Pie 选择承认它就是一个程序。

另一种方案是在服务端直接运行任意 Python 插件。它更接近已有 agent 框架,但共享进程中的隔离、依赖冲突和资源控制会更困难。Wasm 的价值主要是提供受控执行边界和可移植接口,而非保证任意用户逻辑都足够便宜。长期不让出控制、持续创建状态或滥用外部 I/O,仍然需要额外限制。

把程序放近也不等于把所有工具都搬到 GPU 服务器。数据库、搜索和外部业务系统往往必须留在原处。真正迁移的是控制逻辑与模型状态的交互位置,外部工具仍通过 I/O 到达。这样可以省掉一部分“远程客户端收到 token 后再决定模型下一步”的绕行,但不能消除实际工具工作。

11.2 为什么暴露不透明资源,而不是直接给显存地址

直接给应用 GPU 指针,看似最灵活,却把模型后端、内存布局、页迁移和安全边界全都耦合起来。不透明的 Embed 与 KvPage 让应用表达“需要这个状态”,而把物理存储交给推理层。这种抽象也使后端将来替换布局或内核时不必要求每个 inferlet 重写。

相应代价是应用只能做契约允许的操作。想查看某个层的 attention statistics、操作特殊状态或引入新模态,不一定能靠现有句柄完成。论文通过 trait 扩展保留这个空间,但每个新能力都需要明确含义和生命周期。抽象不是免费获得所有能力,而是选择哪些能力跨过边界。

11.3 为什么应用管生命周期,系统仍管批处理

每个应用单独直接驱动 GPU,容易造成大量小操作互相竞争,失去跨应用合并的机会。完全由后端隐藏调度,则会让应用难以表达复杂依赖。Pie 的折中是:应用描述操作与依赖,控制层选择何时把兼容工作交给 handler。

这种分工有一个前提:应用表达出来的信息足以支持安全调度。例如,同一资源是否共享、何时释放、某个结果是否仍被需要,都不能只有开发者脑中知道。否则控制层要么保守串行,要么冒险越过依赖。明确队列顺序和资源引用,正是在为共享批处理建立可用的契约。

11.4 为什么先有通用 API,再保留专用快路径

逐步调用提供灵活性,却可能牺牲流水重叠。对完全确定的长段解码,一个更粗粒度的操作有机会在 handler 内持续执行;对需要分支判断的流程,细粒度接口更合适。系统没有理由要求两者只有一个赢家。

我认为后续设计应该允许应用声明“这一段没有中间干预”,从而让后端安全地使用融合或流水化快路径;到达需要控制的边界再返回。这是基于本文分析提出的方向,论文没有给出一个已经验证的完整混合方案。困难在于定义可撤销性、错误传播以及共享资源限制,不能只加一个“执行更多 token”的参数了事。

12. 局限:资源会等待工具,其他用户不会消失

12.1 KV 的正确寿命,不一定是便宜的寿命

保留 KV 可以消除重复预填充,但工具等待时显存仍然被占着。传统请求结束即释放的方式虽然浪费重算,至少有清楚的占用边界。inferlet 活着、工具尚未返回、分支可能再用,都可能让状态长时间驻留。

在稳定系统中,可以用一个简化的占用时间关系理解平均显存压力。对第 jj 类任务,设到达率为 λj\lambda_j,单次保留量为 mjm_j,保留时间为 τj\tau_j,则:

M‾resident≈∑jλj E[mjτj],E[mjτj]≠E[mj] E[τj](一般情况下).(12)\begin{aligned} \overline{M}_{\text{resident}} &\approx \sum_j \lambda_j\,\mathbb{E}[m_j\tau_j],\\ \mathbb{E}[m_j\tau_j] &\neq \mathbb{E}[m_j]\,\mathbb{E}[\tau_j] \quad\text{(一般情况下)}. \end{aligned} \tag{12}

这相当于把一次任务占用的“字节×秒”乘到达率。它是忽略共享页、溢出和回收等细节的解释模型,不是论文的资源保证。尤其当长上下文恰好伴随更慢的工具时,占用量与等待时间相关,不能随意把期望拆开。

因此,少算一次 prefill 是否值得,取决于这段显存能否用于服务其他任务。应用可能认为保留总是有用;集群却需要比较重算成本、未来复用概率和排队代价。本文看不到一个已经充分验证的全局最优回收方案。

12.2 公平性不能只靠“有工作就做”

工作守恒能提高利用率,但不自动保证公平。一个不断产生大 forward 的 inferlet,与很多短小交互共存时,吞吐最好不代表每个人体验都好。不同 API 类型也可能把等待转移到不同边界,平均延迟很难看出哪些用户长期受损。

论文当前的资源竞争机制包括先到先服务,以及内存不足时终止最近创建的 inferlet。这是一个可描述、可实现的策略,但不是完整的多租户服务目标。新用户更容易被终止、长驻程序更容易占住状态,都是部署时需要面对的结果。

12.3 隔离执行不等于控制全部外部效果

Wasm 限制程序执行边界,无法凭空为工具操作建立权限,也无法决定一次远程支付、文件修改或数据库写入能否重试。inferlet 被取消后,已发出的外部调用仍可能完成;工作流恢复后,重复发出调用可能产生第二次效果。

因此,可靠部署需要把取消传播、调用去重、结果持久化和授权绑定到应用协议。这些不是附带的界面问题,而是“把控制流搬到服务侧”以后仍然存在的正确性条件。论文的高性能案例没有覆盖所有失败路径,不应据此认为它们已经解决。

12.4 单后端结果与分布式服务之间还有距离

集中控制和单 GPU 后端让原型机制更容易辨认。跨机器后,KV 位置、迁移、控制层故障、网络分区、模型副本切换都会影响句柄语义。某个 KvPage 在一台机器上有明确归属,不代表它在故障转移之后仍能以相同成本使用。

同样,tensor parallel、量化与 GPU 内核优化属于后端的重要能力,却不是仅仅定义 inferlet 就自然获得的结果。可编程控制可以与这些技术正交组合,但组合是否高效,需要另外测量。

12.5 接口和实验还没有覆盖全部应用目标

默认 top-k 返回会约束某些采样策略;主动减少可见上下文可能改变生成质量;投机解码案例并不覆盖所有草稿器。单一硬件、有限模型和选定工作流,也不足以证明对每类 LLM 应用都有收益。

这些限制并不取消 Pie 的价值。它们给出了正确的阅读边界:论文有效展示了一种控制与执行分离方式,以及若干应用获益;它还没有证明这组机制已经构成完善、普适且公平的生产平台。

13. 独立批判性分析

13.1 真正开放的是控制权,真正稀缺的是共享资源

我最认可的部分,是 Pie 把应用本来就知道的信息带进了服务系统:哪个分支不会再用、什么时候需要工具结果、哪段前缀未来还会复用。这些信息在纯 completion API 中经常丢失。让应用表达它们,比由后端根据请求字符串猜测更直接。

但最重要的下一步,恰恰不是继续无限扩大应用权限。单个应用知道自己的一页 KV 值多少钱,却不知道其他请求此刻有多急。系统必须把“允许表达保留意愿”与“保证永久保留”分开。否则最积极缓存的应用可能夺走其他用户的空间,而所有人都认为自己只是在合理优化。

我会优先研究带预算的保留契约:应用能表达未来复用的紧迫程度,系统则限制可驻留字节和占用时间,并允许安全回收或回退重算。这是研究建议,不是论文已有结论。难点是共享前缀如何计费、缓存失败如何传播,以及应用是否会虚报复用价值。

13.2 加速结果需要沿着机制拆开,才能知道如何迁移

Pie 的收益混合了多个来源:减少网络往返、消除重复预填充、提前工具执行、缩小活跃上下文,以及后端本身的效率。把这些全部压成一个最大加速数,会让读者不知道自己到底满足哪项条件。

更有诊断力的实验应该逐个关闭机制,并在相同输入和输出条件下比较。比如先保持 KV 不变,只移动控制位置;再保持控制位置不变,只调整保留策略;最后启用提前工具调用。这样才能看出某项收益是否依赖另一项,是否只是把时间移到了未统计的路径上。

StreamingLLM 的比较尤其提醒我,底层内核差异可能压过控制接口本身的差异。一个大倍数值得报告,但必须连同归因限制一起报告。否则读者可能误以为换用相同 API 就能在任何后端复制结果。

13.3 “同一个工作流”需要同时约束质量与行为

某些优化只改变调度顺序而不改变依赖,比较相对清楚;另一些优化直接改变可见上下文或采样候选,语义就不再天然相同。二者都可以有价值,但实验问题应该不同。

对于上下文裁剪,我希望同时看到任务成功率、输出约束满足率和需要回退的比例。对于提前工具调用,我希望看到调用数量、失败重试、被取消调用及外部效果是否一致。对于搜索分支,我希望比较相同搜索预算,而不是一个系统实际探索更多或更少分支后,只报告完成时间。

在系统研究里,“更快但答错更多”不一定完全没有用,因为应用可以接受明确的质量交换;问题在于交换必须公开。论文当前证据更适合支持系统表达能力和特定任务效率,不足以概括出一个普遍质量不变的保证。

13.4 两处数字表述需要澄清

第一处是表 5 与相邻正文的倍数差异。按表格可复算,自适应吞吐为 eager 的约 15.12 倍,而不是 17 倍;相对 K-only 为约 2.82 倍,明显不是高 8–40% 范围内的一项。本文据表格呈现数据,并把正文差异留作未解决的来源问题。

第二处是图 11 的 beam 数。图注明确写 beam size 5,而相邻讨论在说明每个最终 token 对应约 17 次推理层 API、13 次控制层 API 时写 beam size 3。两个设置可能对应不同测量,但当前文本不足以将它们无歧义地关联起来。因此,不能据此推导一个精确、普适的“每 token 固定调用次数”。

我不会因为这些问题否定架构论点,也不会自行修改原文数字来让叙述显得更整齐。更合适的态度是区分两类结论:控制层如何工作,可以从接口和流程理解;某个精确倍数是否可复现,则需要配置和表述一致的证据。

13.5 我会怎样安排下一轮系统评价

下面的评价矩阵是本文建议,全部属于尚待开展的实验,并非作者已报告结果,也不是本文进行了额外运行:

变化维度应保持不变的部分关键观察
客户端远近、网络往返生成内容与工具逻辑控制迁移本身的收益
工具等待时长与尾部分布KV 大小及调用数量驻留显存、尾延迟、超时
并发数与任务类型混合每类请求的工作量公平性、完成率、吞吐
KV 保留预算模型和提示分布重算与显存占用的交换
采样与上下文优化开关任务集合及评分标准质量、回退比例和成本

这里最值得避免的是只在满负载下测一个平均吞吐。应用真正关心的往往是它在混合工作负载里能否按时完成,以及在工具长时间等待、任务突然取消时会发生什么。平均值很好看,可能掩盖少数用户被反复终止。

总的判断是,Pie 为“服务一个程序,而不只是服务一次补全”提供了有力的系统实例。它的重要性来自接口边界改变后能表达什么,而不是某一个最大加速数字。下一阶段最有价值的工作,是让这份表达能力同时具备可预测的资源、公平性和失败语义。

14. 结论:让应用表达意图,也让系统保留约束能力

读完 Pie,我会把核心收获归纳为三个层次。第一,模型应用已经不总是一段连续生成;工具、搜索和定制采样需要中间控制。第二,控制只有接近模型状态才容易避免无谓的往返与重算。第三,把控制开放出去以后,共享系统仍必须负责协调、限制和保护资源。

因此,Pie 最自然的使用场景是控制流丰富、状态复用明显、工具调用频繁的应用。对于很短的单次补全,或者没有中间干预的连续生成,额外控制成本未必划算。是否使用可编程路径,应当由工作流的依赖和生命周期决定。

对于研究者,这篇论文给出了一个值得继续发展的接口起点;对于部署者,它更像一张检查清单:保留的状态到底值不值得,省掉的往返是否足以覆盖控制成本,语义改变有没有测量,资源压力是否会转嫁给别人。把这些问题答清楚,才能把“可编程”变成稳定的应用收益。

参考来源与配图说明

  1. In Gim、Zhiyao Ma、Seung-seob Lee、Lin Zhong,Pie: A Programmable Serving System for Emerging LLM Applications,arXiv:2510.24051v1,2025 年 10 月 28 日。论文 PDF。
  2. 作者提供的 Pie 项目仓库,作为进一步查找材料的入口。本文的技术与实验讨论依据上述论文版本。
  3. 本文阅读范围为该版本全部 16 页,包括参考文献。数字按原论文图、表及实验设置记录;说明性计算和评价建议均已单独标明。
  4. 文件 pie-01 至 pie-07 分别裁自论文图 2、4、5、6、7、8、10。原论文采用 CC BY 4.0 许可;本文保留作者归属,并说明图片作了裁剪。
  5. pie-08 为本文绘制的重复预填充说明图,使用图注明确列出的假设;pie-09 为依据论文表 4、表 5 重绘的图表。两图均不代表新的性能实验。