Wink Pings

单进程 4096 个会话:pi-durable 把数据库提交合批后,CPU 也顺带快了

Mario Zechner 给 pi-durable 做了一轮性能优化。每个 agent 步骤都要落库,旧实现让 commit 排队,128 个会话时单次运行等约 25 秒。合批后降到 0.28 秒,本地 SQLite 快 18% 到 58%。注意测试用假模型和空工具,测的是基线。

durable 的账单总是后付。每一步都落库,崩溃恢复干净,平时就得等 IO。pi-durable 这次做的是把账单合起来付。

Mario Zechner 在推文里给了一个数字:单个 Node 进程跑到 4096 个会话时,系统明显饱和,但还能用。

pi-durable 性能提升图表,标题显示从 46 次/秒到 772 次/秒

先把背景补齐。pi-durable 的 durable 来自落库:agent 每走一步,先把状态写进数据库,然后才继续。1 次保存等于 1 次 commit。一次 agent run 大约 12 次 commit。崩溃发生在中途,就从最后一次 commit 恢复。代价是每一步都要等数据库。

pi-durable 一次 agent 回合的流程图,user message、model call、tool runs、model call、done,每个圆柱体代表一次 commit

旧实现慢在串行等待。session 把每个 commit 单独发给数据库,然后等返回。一次 DB 往返 4ms,每个会话的 commit 排在其他会话后面。128 个会话时,一次 run 要等约 25 秒才轮到自己。Mario 的改法是:一个 commit 在途中时,后面堆积的 commit 合到下一次 DB 调用一起发。128 个会话时,一次 DB 调用大约带走 33 个 commit。等待从约 25 秒降到 0.28 秒。

优化后 128 个会话的等待时间图,坐标轴为对数刻度

合批听起来简单,麻烦在依赖。不能把所有 commit 无脑打包,单个 commit 之间可能有读写依赖。Mario 说这块相当棘手。有网友追问崩溃场景:数据库提交完一批,但调用方还没收到 ack 时杀掉 coordinator,再恢复,依赖读取和工具调用能不能干净重放。Mario 回复 yes。

commit 合批示意图,在 128 个会话时一次 DB 调用携带约 33 个 commit

效果不只在网络数据库。本地 SQLite 快了 18% 到 58%。Cloudflare Durable Objects 读取少 40% 行,写入少 13% 行,静态内存不变。

不同数据库和工具场景下的性能对比图

主图标题把提升写成 46 次/秒到 772 次/秒。底部小字更具体:本地 SQLite 在 2048 个对话时从 88 次/秒到 459 次/秒;三个并行工具在 2048 个对话时从 58 次/秒到 617 次/秒;单个对话性能保持不变。

限定条件容易被忽略。Mario 在回复里注明:测试用的是进程内假模型 provider 和无操作工具。这组数据测的是 harness 能跑出的基线计算量。换成真实、吃 CPU 或 I/O 的工具,runs/s 会下降。

对跑多 agent 的开发者,结论比较直接。如果 coordinator 通过 Postgres 之类扇出到几十个子 agent,不用再为了吞吐把 session 分片。瓶颈先落在单个 CPU 上,Node 进程会先饱和。真到 CPU 打满,再考虑分片。

有网友 Mykyta Pavlenko 总结得挺准:修完存储后 CPU 白赚,说明进程之前只是在等 commit。4096 个会话,一次 run 约 12 次保存,放在一个 Node 盒子里,已经算能干活的范围。Mario 感谢 @she_llac 催他动手。

发布时间: 2026-10-31 13:33