你感到的AI变快变便宜,来自让GPU一刻不停的技巧
你每次让AI写长文、多轮对话或调用工具,背后引擎都在用各种技巧让昂贵的GPU一刻不停。你感受到的“变快了”和“更便宜了”,其实是这些技巧在起作用。
token与循环生成
模型只认识整数,不接触文本。分词在CPU上完成:文本先被切成token序列,一句英文会变成一串整数。词表大约10万条目,平均一个token约四分之三个英文单词。
生成是循环过程。把目前所有token喂进模型,得到下一个token,拼回去再跑一遍。一段500词的回答大约需要700次完整前向传播,直到出现停止符。GPU输出的logits要先依次施加temperature、top-p、top-k和各类惩罚,采样出token id。CPU再反分词、流式返回。
两种瓶颈
整个生成分两个阶段,瓶颈完全不同。Prefill一次性并行处理整个prompt,计算受限,决定首token延迟。这个延迟就是你发出消息后到看见第一个字的时间。Decode每步只生成一个token,计算量小,但要读取之前所有token的状态,属于内存带宽受限。它决定每秒能输出多少token。
KV cache避免平方级重算:每个token的Key和Value只算一次、存下来,之后每步只多算一列、读缓存。代价是这个缓存正是显存的消耗大户。它的大小正比于token数 × 层数 × 头数 × 精度字节数,还和batch里的用户数相乘。长对话越聊越慢,往往就是显存被它占满。
连续批处理与分页内存
CPU与GPU流水线并行:GPU跑第N步时,CPU已经在反分词第N−1步的输出。调度器是真正的枢纽,每次前向传播前要决定哪些请求进批、各占多少token预算、用哪些KV块。
连续批处理让吞吐量上了数量级。它每次pass重新组批,请求一结束,新请求立刻补进空位,没有一次空闲的pass。这个方法来自2022年的Orca论文。
PagedAttention把KV cache切成固定大小的块,从共享池按需分配。碎片率从预留制下的60-80%降到4%以下,同样显存能跑四个请求,池子还剩一半。一个行业共识是,限制batch size的通常不是算力,而是KV容量。
长prompt的处理
长输入同样有对策。一个8K token的prompt塞不进单次2K token的pass预算,就切成4片逐次处理。关键在每一步的预算里捎带上活跃用户的decode token。新用户的巨型prompt进来时,正在输出的用户不会干等,首token延迟的尖峰被摊平。
Agent循环
AI agent是另一种流量模式。每一轮调用都是一次全新请求,重发到目前为止的完整对话。引擎看到的现象是:每轮prompt都是上一轮的超集,只有末尾一小段是新的;输出却只有几十个token,一次工具调用而已。
拿100轮agent循环来说。没有prefix caching,总prefill工作量约等于第一轮的55倍;有缓存时降到10倍左右,每轮首token也快。做法是对KV块做哈希,相同哈希的块直接物理复用,前缀保持不变就能命中缓存,用户无需任何配置。agent在等搜索结果或代码执行时,引擎还会把它的KV cache换页到主存,腾出显存给活跃请求,下一轮再换回来。
剩下的问题藏在实现细节里:前缀动态变化时缓存如何失效、换页到主存的开销有多大。不同引擎给出的答案不一样,最终都变成你点下发送后看到的响应速度。