仅用于站内搜索,没有排版格式,具体信息请跳转上方微信公众号内链接
作者|SeanGoedecke
译者|王强
策划|Tina
为什么DeepSeek-V3据说在大规模服务时快速且便宜,但本地运行时却太慢且昂贵?为什么有些AI模型响应很慢,但一旦开始运行就变得很快?
AI推理服务提供商经常讨论吞吐量和延迟之间的一个基本权衡:对于任何给定的模型,你要么以高吞吐量和高延迟提供服务,要么以低吞吐量和低延迟提供服务。实际上,有些模型天生是GPU效率比较低的,以至于在实践中它们必须以高延迟提供服务,才能有可行的吞吐量(例如DeepSeek-V3)。
这种权衡来自于推理服务提供商为模型选择的批处理大小:不是在单个请求内进行批处理推理,而是跨数十或数百个并发用户请求进行批处理。基于Transformer的大型语言模型的一个奇特特性是,同时计算一批补全几乎和计算单个补全一样快。为什么会这样?
什么是批处理推理?
GPU擅长执行大型矩阵乘法(GEMMs,或“通用矩阵乘法”)。假设你有一个token,你想通过模型传递(即通过乘以其所有权重,其他架构细节不谈)。你将其表示为一个与模型维度(或隐藏大小)相匹配的向量(即1x其大权重矩阵的宽度),然后将其乘过去。那就是1个GEMM。但如果你想一次通过一批十个token,也仍然只是一个GEMM,因为你可以将token堆叠成一个矩阵(10x模型维度)。这比执行十个稍小的GEMM要快得多。因此,推理服务器的实现可能看起来是这样的:
一个请求带着提示词进来
该提示被预填充(通过注意力传递,我们稍后将看到如何将它也做批处理),形成一个KV缓存和一个token大小的矩阵(1x模型大小),最终成为预测的token
那个token大小的矩阵进入一个队列
GPU服务器从该队列中拉取一些批次(例如128个),将它们堆叠成一个128x模型大小的矩阵,并通过前馈模型权重进行乘法运算
最终结果被分割成128个单独的token
针对原始请求的那个token被流回给用户
假设那个token不是序列结束token,返回步骤2以继续生成响应中的下一个token
请注意,服务器会决定拉取多大的批次大小。这是吞吐量和延迟之间的权衡。如果你不进行批处理,只是逐个处理token,那么没有用户会在队列中等待(上述步骤3),所以延迟很低(假设你有足够的GPU)。然而,如果你进行大量批处理,延迟会很高,因为用户将等待到批次大小填满,但吞吐量会高得多,因为GPU的使用效率更高。
为什么GPU在一次乘以大型矩阵时比多次乘以小型矩阵更快?有两个原因。首先,向GPU发出每个命令都涉及一些开销,而一个大乘法可以用单个命令启动。其次,每个新的GPU命令都要从内存中获取权重,这对于大型权重来说可能很昂贵。如果你运行很多小GEMMs,你最终可能会花费大部分时间在内存中进出权重,而不是进行计算。
为什么有些模型针对大批次大小进行优化?
通常,推理服务器会有一个“收集窗口”,用户请求送进这个窗口并被排队。聊天服务器通常的延迟目标是5-10毫秒,但非常高的批次后端可能会达到200毫秒。如果一个新请求在窗口开始时进来,它可能需要等待整个窗口的持续时间后才能被处理。当窗口关闭时,所有排队的请求都被批处理(即所有1x模型大小的矩阵被连接成一个单一的128x模型大小的矩阵),然后该批次通过管道发送。像这样运行一个批次有时被称为一个“tick”。
正如上述解释所建议的,你可以在任何批次大小下运行任何模型。批处理过程本身并没有什么限制会排除某些类型的模型。然而,我们有可能构建一个模型,使其GPU效率如此低下,以至于实际上它需要批处理才能满足实用要求。
为什么专家混合机制需要更高的批次大小
例如,考虑一个专家混合模型(如DeepSeek-V3或据说是原始的GPT-4所使用的机制)。你可以让它拥有数百个“专家”来获得一个强大的模型。这些“专家”是独立的前馈权重块,路由层从中选择一个子集用于每个token。但这样的模型真的对GPU效率很差。原因在于:GPU想要执行少量的大型矩阵乘法,但如果有很多专家,你会被迫做很多小型乘法。除非你以整个批次进行推理,否则这意味着吞吐量很低。
我们考虑一下5毫秒和200毫秒的“收集窗口”对于大型专家混合模型的表现如何。假设你在那个5毫秒窗口中接收到十个用户请求。如果你有很多专家,一些专家可能最终只对一个或两个token运行(即每个专家的批次大小将远低于你在窗口中接收到的总请求集)
为什么大型管道需要大的批次以避免管道气泡
对于大型模型来说,保持GPU始终活跃可能是一个挑战。大型模型通常有很多transformer层:即组成前馈网络的数百个权重矩阵。在这里进行快速推理的唯一方法是管道化这些层,让一个GPU处理前十个层,另一个处理接下来的十个层,依此类推。否则,你根本无法将所有权重放入单个GPU的内存中,这样你会花费大量时间在内存中交换权重,最终会变得非常慢。在推理过程中,每个token(通常位于每个包含几十个token的“微批次”中)会顺序通过GPU管道。
你的管道效率取决于你拥有的层数和你的收集窗口大小。当你在“tick”期间处理窗口中的token时,你会在开始时有一些空闲的GPU(因为后层的GPU还没有输入可以操作),在结束时会有更多的空闲GPU(当队列中没有更多的token时,早期层的GPU将不得不等待下一个“tick”)。这些空闲期有时被称为“预热”和“排水”。如果你有很多小窗口,你将比拥有较少大窗口时花费更多的GPU时间在预热和排水上。通过选择你的窗口大小,你就能直接在吞吐量和延迟之间进行权衡。
如果你有很多层,你的收集窗口非常短,有时最终处理的token数量可能少于层数。这被称为“管道气泡”——实际上,“排水”阶段比平时更早开始。你不能消除预热和排水(由于下面讨论的原因,推理必须以顺序“tick”操作),但可以让收集窗口足够长来消除管道气泡。管道气泡可能对模型吞吐量造成极大的影响,因此推理提供商总是设置足够宽的窗口以避免它们。这为具有许多层的模型增加了明显的延迟。
你不能保持队列满载吗?
为什么推理提供商不能通过保持GPU队列满载来完全消除预热和排水?换句话说,你不能完全摆脱tick,而只是让很多token微批次持续流动下去?当然,每个用户的推理必须是顺序的(因为你不能在当前token完成之前开始生成下一个token),但大型推理提供商应该有足够的并发流量来保持队列满载。
我承认我在理论上看不出为什么这是不可能的。据我所知,实际障碍是如何批量处理注意力步骤:如果你想批量处理注意力GEMMs,它们需要都是相同的形状(即序列中有相同数量的先前token)。所以你不得不同时运行相同形状的组,而不能只维护一个队列。在这方面至少有一些公开的研究(https ://arxiv. org/abs/2403. 02310),但如果有更多我没见过的更聪明的技巧来做这件事,我不会感到惊讶。
另一个想法:如果你需要tick来处理注意力步骤,为什么不只使用基于tick的注意力推理系统,以及更高效的连续系统用于FFN?据我了解,原因是内存开销:
由于FFN需要注意力输出,你需要在内存中有一个位置来停放它,同时等待其在FFN队列中的插槽,这将很快变得过于昂贵。
现代推理栈能够将注意力和FFN步骤合并成几个大GEMMs放在一个“操作”中。如果你在不同的GPU上进行这些操作,你必须运行不同的操作并来回移动权重。
总结
GPU在大型GEMMs上最高效,因此将许多token堆叠到一个矩阵乘法中,可以比逐个处理它们获得更高的token吞吐量。
在解码过程中,注意力只能为同一步骤的token批量处理,迫使调度器以短“tick”运行。你将多少token打包到一个“tick”中(即你等待收集token的时间),就是你的批次大小。这些token来自不同的用户。你不能从同一个用户批量处理token,因为你需要之前的token来生成下一个token,所以批处理需要不同用户组成的高流量。
更大的批次增加了延迟,因为用户token可能需要等待多达200毫秒,直到批次足够大才能开始运行,但它们通过允许FFN步骤中更大的(因此更有效的)GEMMs来提高吞吐量。
具有许多层(例如长管道)的模型需要更大的批次以避免管道气泡(确保每个tick包含的批次多于管道步骤)。
混合专家模型需要以高延迟提供服务以提升效率:每个专家只看到路由到它的token,所以你需要的是更大的全局批次以让每个专家都忙碌起来。
推理提供方选择一个批次大小/窗口,以清除流水线泡沫并让专家饱和工作。高批次大小可以为你提供更多吞吐量,但代价是更高的延迟,因为token需要等待填充。
一些模型(如DeepSeek)是具有许多层的专家混合模型,因此需要大批次大小和高延迟,否则吞吐量会急剧下降。这就是为什么说DeepSeek很难在个人场景中使用:因为只有一个用户一次运行一个推理,它的运行效率/吞吐量非常低。
OpenAI和Anthropic的模型响应迅速这一事实表明,要么:他们的模型具有更高效的架构(非MoE,更少的层),或者OpenAI/Anthropic有一些非常巧妙的推理服务技巧,或者他们支付了高昂的费用,购买了比他们所需的量更多的GPU。
编辑:这篇文章发表在HackerNews上,并附有一堆评论。我有点希望我给这篇文章起了另一个名字——它实际上讲的不是在自己的计算机上运行模型,而讲的是为个人使用场景运行模型,假设你拥有所有GPU(即批次/吞吐量权衡)。
人们观察到的transformer的一个常见优势是,它们可以在单个用户请求内批量预填充。当你给它们一个长提示时,它们可以一次性处理该提示,因为注意力机制的工作原理就是这样。以前的递归模型必须逐token进行,这要慢得多(因为它涉及更多的GEMMs)。这与我在这篇文章中谈论的批处理无关。我谈论的是如何在预填充完成后,有效地跨许多不同的用户请求来批量推理。
只要你只批处理具有相同数量token序列的注意力操作(例如每个序列预测第四个token可以一起批量处理),这也可以实现。否则,KV缓存矩阵的大小是不一样的,因此你不能轻松地将它们组合成一个批次。
技术上,它不是一个被生成的token,而是“logits”(所有可能token的概率分布)。为了保持简单性,我在这里和以后会说“token”。
注意,在实践中,现代推理栈将使用“连续批处理”,一旦批次满就发送,而不是等待固定时间窗口的全部时间。然而,推理仍是批量进行的,核心的吞吐量和延迟之间的权衡是相同的。
如果你喜欢这篇文章,请考虑订阅我的新文章的电子邮件更新(https ://buttondown. com/seangoedecke)。
原文链接:
https ://www. seangoedecke.com/inference-batching-and-deepseek/
声明:本文由InfoQ翻译,未经许可禁止转载。
InfoQ老友!请留步!极客邦1号客服上线工作啦!
后续我将通过微信视频号,以视频的形式持续更新技术话题、未来发展趋势、创业经验、商业踩坑教训等精彩内容,和大家一同成长,开启知识交流之旅
欢迎扫码关注我的微信视频号~
今日荐文
李飞飞曝创业招人标准!总结AI大牛学生经验,告诫博士们不要做堆算力项目
Altman嘲讽小扎挖走的都不是顶尖人才!OpenAI高管再营业曝内幕:ChatGPT爆红后,我火速升职了!
跳槽实现财富自由!小扎千万年薪快要“掏空”OpenAI核心人才,还高调“晒”挖人成绩单:各栈大牛,近70%是华人
华为盘古大模型开源,推理方案、基础代码全公开!
老黄亲自挖来两名清华天才;字节Seed机器人业务招一号位;清华北大浙大中科大校友跳槽去Meta|AI周报
你也「在看」吗?👇