跳到主要内容

P99 是产品决策,而非基建决策

· 阅读需 10 分钟
Tian Pan
Software Engineer

在几乎每一个发布 AI 功能的团队中,都会上演这样一种“仪式”:有人运行负载测试,看着 p99 延迟攀升并超过 2 秒,然后提交了一个工单:“让它快一点。”这个工单落到了基建(infra)团队手中。他们调整批处理大小(batch sizes),增加 GPU,争论调度器(scheduler),最终费尽心思将这个数字降到了 1.4 秒。每个人都点头认可。p99 被“解决”了。

整个过程都建立在一个错误的前提之上。这个前提认为延迟目标是系统的一个既定事实——是基建团队发现并为之优化的一个物理常数。事实并非如此。目标是一个选择,而且是一个产品层面的选择。什么才算“足够快”,完全取决于用户等待时界面的表现,而界面并不在基建团队的设计范围内。

证据在于,两个后端延迟完全相同的功能,其体验可能天差地别。其中一个在模型开始生成的瞬间就开始流式传输 token;用户在 0.5 秒左右就开始阅读,根本不会注意到整个响应花了 4 秒才完成。另一个则一直显示加载图标(spinner),直到完整答案就绪,然后一次性全部弹出。同样的 p99。一个感觉是即时的,另一个感觉则是出了故障。如果这个数字真的是基建属性,那么这种情况就不可能发生。

你正在优化的数字是错误的

第一个错误是将“延迟”视为一个单一的标量。对于任何人类需要等待的事情,感知上起决定作用的指标是 首个 Token 耗时 (TTFT) —— 即在任何内容出现之前,屏幕保持空白的时间。相比之下,总生成时间几乎可以忽略不计。

相关研究由来已久且结论一致。IBM 的 Walter Doherty 在 1982 年发现,当系统响应时间降至 400 毫秒 以下时,生产力和满意度会出现不连续的跃升;人们会进入一种“心流”状态,不再等待机器。Jakob Nielsen 在 1993 年提出的阈值也说明了同样的情况:100 ms 以下感觉是即时的,1 秒以内可以保持用户的思维连贯,而超过 10 秒,除非你展示明确的进度,否则你就会失去用户。人类的视觉反应时间大约在 200 ms 左右,这就是为什么首个 token 在此时间段内出现的聊天界面会让人感觉非常灵敏。

这些阈值都不关心你的总延迟。它们关心的是反馈之前的间隙。一个在 400 ms 内开始并流式传输 4 秒的响应,优于一个在 2 秒后开始并瞬间完成的响应——尽管后者的总延迟只有前者的一半。在忽视 TTFT 的情况下优化端到端时间,是在优化一个用户感知不到的数字。

背后还隐藏着第二个指标:Token 间延迟 (ITL),即流开始后的节奏。每 token 25 ms 的平滑节奏读起来就像自然的打字。同样的平均值,但如果带有抖动(jitter)——比如每十个 token 就飙升到 200 ms ——就会产生明显的卡顿,让人感觉系统在挣扎。用户感知到的是偏差,而不仅仅是平均值。你的 p99 可能看起来不错,但体验仍然可能感觉支离破碎,因为 token 间间隙的分布参差不齐。

因此,在有人提交“让它快一点”的工单之前,真正的问题是:在什么方面快一点?TTFT、ITL 和总时间是三个不同的预算,它们之间存在权衡。即使总时间稍微变差,你也可以通过更早开始流式传输来降低 TTFT。这几乎总是正确的权衡,而如果你只追踪一个汇总指标,这一点是无法察觉的。

界面能隐藏的,后端就不必交付

这里有一个重新构思的方法,可以将 p99 从基建队列移至设计评审中:界面每能隐藏一毫秒,后端就不必再为此买单。

前端团队十年来一直知道这一点,只是换了个名字——体感性能(perceived performance)。工具箱已经非常成熟:

  • 流式传输 (Streaming) 是最大的杠杆。与加载图标相比,在生成 token 时立即显示它们可以将体感响应速度提高 10-100 倍,因为用户在 TTFT 窗口内就开始阅读,而不是等待整个响应。这对总延迟没有额外成本;它只是让用户的时钟提前了。
  • 乐观 UI (Optimistic UI) 在用户操作的瞬间就更新界面,假设操作成功,仅在失败时才进行纠正。Twitter 的点赞按钮在网络请求完成之前就会播放动画。这约 500 ms 的动画为请求完成争取了实际时间,由于反馈与网络解耦,交互感觉是瞬时的。
  • 骨架屏 (Skeleton screens) 模仿即将出现的内容形状,而不是显示加载图标。它们设定了内容即将到达的预期,并能显著减少感知的等待时间和跳出率。
  • “思考中……”的示能设计 (A "thinking…" affordance) 将缓慢的回答重塑为深思熟虑的回答。同样的 3 秒被解读为“模型正在推理”而不是“应用卡住了”——延迟变成了质量的证据,而非缺陷。
  • 预加载 (Pre-emptive loading) 在悬停或聚焦时就开始执行工作,甚至在用户还没决定提问之前,这样可见的延迟就只剩下抢跑后剩余的部分。
加载中…
References:Let's stay in touch and Follow me for more thoughts and updates