【引】有的事情别人不问时我们明白,一旦要我们解释它我们就不明白了,而这正是我们必须留心思索的东西。于是,开启了一次又一次的论文阅读之旅。
开发并部署大模型应用肯定要考虑它们的服务成本。然而,钱并不是唯一的考虑因素,如果不能解决模型性能方面的问题,即使有很大的预算,大模型服务仍会受到影响。本文尝试讨论将 LLM 推理服务更改为高吞吐量引擎的挑战与应对方法。
1. 大模型服务面临的挑战
大模型的能力令人惊叹,但其独特的工作特性却给高性能服务部署带来了挑战。其处理过程主要分为两个阶段:预填充和解码。在预填充阶段,当你输入提示词(包含上下文、对话历史、问题等信息)时,模型需要一次性处理所有输入的 token。随后进入解码阶段,模型开始逐个生成输出 token,且每个新 token 的生成都严格依赖于之前生成的 token。可以这样类比:预填充就像为一盘象棋游戏精心布局(耗时较长),而解码则类似于后续一步接一步的落子(单步较快)。然而,现实并非如此轻松——部署大型模型远非易事,必须仔细考量其带来的延迟问题。
1.1 数据稀疏性问题
在神经网络中,尤其是前馈网络(FFN),许多神经元的激活值为零。这种稀疏性导致矩阵乘法中存在大量零元素,从而浪费了计算资源。如果我们能够跳过这些零值,仅对非零元素进行计算,将显著提升推理效率。
更重要的是,在深度学习系统中,数据在 CPU 和 GPU 之间传输所消耗的时间往往远高于实际计算时间。此外,随着模型规模的增长,一些包含数万亿参数的超大规模模型根本无法容纳在单个 GPU 中,使得稀疏性优化变得尤为关键。
1.2 请求调度问题
大模型通常需要同时处理多个用户请求。在这种多任务场景下,短小快速的请求(例如查询天气、时间或简短答案)可能不得不排队等待长时间请求完成。这导致整体平均响应时间主要受制于等待时间,而非实际计算耗时。
即使你的模型计算速度非常快,也必须等待前面的请求执行完毕才能开始处理下一个。因此,如何高效地调度和优先处理不同类型请求,是提升服务吞吐量与用户体验的关键挑战。
1.3 顺序解码问题
当前的语言模型生成机制限制了token之间的并行化能力。每个前向传播只能生成一个新 token(或少量 token),这意味着长文本回复必须逐字逐句地生成。这也是为什么像 ChatGPT 这类模型在生成长文时,通常采用“流式输出”的方式呈现结果。
有趣的是,尽管流式输出能带来更即时的反馈体验,但其本质仍然是串行生成过程。因此,“先看到一部分”并不意味着更快完成整个生成任务,反而揭示了当前解码机制在并行性上的瓶颈。
1.4 KV 缓存增长问题
注意力机制是 LLM 推理的核心环节,尤其是在长序列中,计算所有 token 之间的相关性会带来巨大的计算负担。每当模型生成一个新的 token,都需要重复计算之前所有 token 的注意力权重,造成大量冗余操作。
KV 缓存(Key-Value Cache)是一种有效的优化策略,它通过缓存已生成 token 的中间状态,避免重复计算,从而加速推理过程。然而,随着生成序列变长,KV 缓存占用的内存也会持续增长,成为影响推理效率和部署成本的重要因素。
2. 推理优化之KV Cache 管理
KV 缓存是 LLM 推理过程中占用内存最多的部分之一。随着上下文长度的增加,KV 缓存所需的存储空间也随之增长。例如,一个支持最大输入长度为 2048 个 token 的模型,需要预留 2048 个缓存插槽。如果用户仅输入了一个包含 7 个 token 的提示词,那么其余 2000 多个插槽虽然未被使用,却依然被系统预留,造成内部内存碎片。
在每一步推理中,模型都会生成新的 KV 对,并在后续 attention 计算中使用,因此必须将它们缓存起来。KV 缓存通常以连续的内存块或“页”形式进行分配。然而,当某个序列生成完成后,其占用的内存页被释放,但这些页可能并不连续。这就导致了外部内存碎片:大量小块空闲内存分散在内存中,无法满足后续请求所需的连续内存空间。
为了解决这一问题,研究者借鉴操作系统的内存管理机制,提出了页面注意力机制(PagedAttention)。该机制将 KV 缓存组织成逻辑内存块,并通过页表进行管理,从而实现灵活的内存映射和高效利用。其核心思想包括以下几个关键方式:
固定大小的内存块:页面注意力机制采用固定大小的小型内存单元(称为“页”)来存储 KV 缓存,类似于操作系统中的分页机制。
共享内存块:这些内存页可以在多个请求之间共享,提高资源利用率。
按需动态分配:内存块根据生成过程动态分配,无需预先估计最大序列长度,避免了不必要的内存浪费。
通过引入这种高效的内存管理策略,页面注意力机制显著提升了推理时的内存利用率和并发处理能力,是当前大模型部署优化的重要方向之一。
2.1 基于 Radix Tree 的 KV 缓存优化
在计算机科学中,Radix Tree(也称为紧凑前缀树或压缩 Trie 树)是一种空间优化的树形数据结构。它通过对具有相同前缀的节点进行合并,减少了存储开销,从而提升了查找效率。
在大语言模型(LLM)推理中,基于 Radix Tree 的 KV 缓存技术被用于高效地重用多个推理请求之间的缓存数据,尤其适用于多个请求共享相同输入前缀的场景。通过将 KV 缓存组织为 Radix Tree 结构,系统可以快速检索和复用已有的缓存内容,并在不同请求之间实现灵活共享。
相比传统的线性缓存管理方式,Radix Tree 在内存利用和访问效率上更具优势。其构建成本约为 O(n log n),而在注意力计算中的额外开销相对较小,约为 O(n²) 量级,这对于提升多请求并发处理能力具有重要意义。
2.2 多种注意力机制下的 KV 管理策略
多头注意力机制(Multi-Head Attention)是 Transformer 模型的核心组成部分,也是当前大多数 LLM 的核心架构。每个注意力头从不同的角度理解文本内容:有的关注主语与动词的关系,有的聚焦词汇本身,还有的分析句子结构。这种多头设计显著增强了模型的理解能力。
然而,每个注意力头都需要独立维护一组 Key 和 Value 向量,导致 KV 缓存的内存占用急剧上升。特别是在处理长文本或多任务并发时,这些向量会占用大量显存资源,成为性能瓶颈。
为了缓解这一问题,研究者提出了多种优化方案:
- 组查询注意力(Grouped Query Attention, GQA):
允许部分注意力头共享相同的 Key 和 Value 向量,从而减少整体缓存需求。
- 多查询注意力(Multi-Query Attention, MQA):
仅使用一组 Key 和 Value 向量供所有查询头共享,是目前最节省内存和计算时间的方法之一。
此外,像 DeepSeek 这类开源模型进一步引入了 Flash Multi-Latent Attention(Flash MLA) 技术,在训练和推理阶段实现了更高效的注意力计算。该方法通过低秩压缩技术,将 Key 和 Value 向量向下投影到一个维度更低的潜在空间,从而大幅减小缓存体积。在实际计算注意力时再进行向上投影。
更巧妙的是,该方法还将向上投影的权重矩阵与查询矩阵进行融合,从而加快注意力的计算速度,进一步提升推理效率。
3. 推理优化之 Query-sparsity attention
在 MIT 发表的论文《QUEST: Query-Aware Sparsity for Efficient Long-Context LLM Inference》中,研究者指出:Transformer 层中普遍存在高度稀疏性。这意味着,在实际推理过程中,并非网络中的所有神经元都会被激活。
基于这一观察,研究人员提出了一种高效的模型推理方法——利用这种稀疏性进行剪枝,从而显著减少计算开销。其背后的逻辑非常直观:并不是每个 token 都对上下文理解有贡献。
举个简单的例子:
我们输入提示词:“A is B, C is D. A is”,期望模型输出下一个词:“B”。在这个任务中,模型只需要关注最相关的几个 token 即可完成预测,而其余部分则可以忽略。这表明,模型的注意力机制具有明显的查询依赖性,即“查询感知稀疏性(Query-Aware Sparsity)”。
基于这一洞察,QUEST 提出了一种高效策略:在注意力计算中,只选择与当前查询最相关的 KV 缓存块进行处理。具体来说,该方法会在所有数据块中找出前 k 个最关键的数据块来进行后续计算。
以下是 QUEST 的核心流程:
- 块级特征提取
对于每一个 KV 数据块,QUEST 首先提取其最小和最大 Key 值以及通道极值。
- 查询特征生成
接着,根据当前查询向量,逐元素生成对应的 max 和 min Key 值。
- 快速筛选机制
通过上述技巧,系统能够快速评估哪些 KV 块与当前查询最为相关,从而避免大量无效计算。
- Top-k 选择
最终,仅保留与查询最相关的前 k 个 KV 块,用于后续注意力计算。
通过这一系列优化,QUEST 显著减少了注意力机制中的冗余计算,从而提升了长上下文场景下的推理效率。
当然,一个关键问题是:如何选择合适的 k 值?
k 是一个需要通过实验调优的超参数。研究表明,当设置 k = 4096 时,模型性能几乎接近完整计算的水平(约 100%),同时又能带来显著的效率提升。因此,这是一个兼顾准确率与效率的推荐值。
4. 推理优化之推测性解码
推测性解码(Speculative Decoding) 是加速大语言模型推理的重要技术之一。这一方法的重要性也得到了 Andrej Karpathy 的认可,并在 2022 年由 Google 首次提出并应用于实际系统中。
其核心思想非常直观且巧妙:
与其仅依赖一个庞大、准确但缓慢的目标模型逐 token 地生成结果,不如先使用一个轻量级、快速但相对不够精准的小模型(称为“草稿模型”)来预测多个后续 token。然后,再由大模型(即目标模型)对这些预测进行验证。
如果目标模型认同草稿模型的预测,则可以直接接受这些 token,从而大幅提升生成效率;如果不一致,则从分歧点开始重新生成。虽然这种机制存在一定的回退成本,但在多数情况下,草稿模型的预测是准确的,因此整体上节省了大量计算资源。
草稿模型可以是一个小型神经网络模型,例如参数规模在 1B~3B 的模型,甚至也可以是基于统计的 N-gram 模型。而目标模型则通常是拥有数十亿甚至上万亿参数的大模型。
尽管使用两个模型看似会增加内存和计算开销,但在实际应用中,由于草稿模型的预测准确率较高,尤其是对于常见词汇(如“是的”、“这个”、“是”、“等等”)几乎不会出错,因此能显著提升推理速度。
更重要的是,所有由草稿模型生成的 token 可以被目标模型一次性并行验证,而不是传统的逐 token 自回归生成方式。这种方式大幅减少了生成延迟,为长文本输出带来了实质性的性能提升。
5. 推理优化之资源调度
在大模型推理中,调度(scheduling) 是一项关键挑战,其核心在于如何在有限的硬件资源(如 GPU、CPU 和硬盘)之间实现高效的负载平衡。一个优秀的调度策略不仅能通过并行计算加速推理过程,还能让拥有上百亿参数的大模型(例如 100B 参数模型)在低配置设备(如搭载 T4 GPU 的 PC)上顺利运行。
要实现这一目标,通常依赖于两个关键技术要素:
- 智能地在 GPU、CPU 和硬盘之间加载和卸载模型权重
- 高效管理计算单元之间的数据 I/O 传输
为了解决这两个问题,来自斯坦福大学、加州大学伯克利分校和卡内基梅隆大学的研究者提出了 FlexGen,这是一套具有代表性的系统级优化方案,旨在提升大规模语言模型在受限硬件上的推理效率。
5.1 FlexGen 的核心机制
FlexGen 将每个需要处理的数据块定义为“一批数据”,这些数据被依次加载到模型的不同层进行计算。其中,列方向表示批处理维度,而行方向则对应模型层数的顺序处理。
为了保证执行效率和资源约束,FlexGen 定义了一条“有效路径”——即遍历所有数据块的最优执行路径,必须满足以下条件:
数据必须从左到右按顺序执行
同一批次的所有数据必须位于同一设备上
激活值必须按照正确的滑动窗口进行处理
KV 缓存需保留至当前批次完成
在任意时刻,设备上存储的张量总大小不能超过其内存容量
假设我们有 N 个 token,每个 token 的数据将按照顺序依次加载并计算。每层的权重仅在需要时加载,在计算完成后立即卸载。然而,这种频繁的加载/卸载操作会带来显著的时间开销——因为虽然 GPU 的计算速度极快,但内存传输却相对缓慢。
5.2 FlexGen 的优化策略
为了解决上述瓶颈,FlexGen 引入了灵活的执行调度方式,例如通过调整扫描顺序(从行到列、之字形块调度等),从而避免不必要的 I/O 操作。它不仅能够节省下一层模型权重的加载时间,还能提前保存下一批激活值。
在每个块的执行过程中,FlexGen 会重叠执行以下三个步骤:
- 加载下一层的权重
- 存储前一批的激活值 / KV 缓存
- 计算当前批次的数据
这种流水线式处理大大缓解了内存传输带来的性能限制,提升了整体推理吞吐能力。
除了执行调度之外,另一个关键问题是:如何在不同的硬件设备上合理分配模型权重?
FlexGen 采用一种基于线性规划的搜索策略,来寻找最优的权重分布方案,目标是最小化整个模型推理所需的时间。
这里:
N: 每个序列的输出token数
𝑙: transformer层数
block size: 在一个块中处理多少个示例 (批次大小 × 批次数)
实验数据显示,FlexGen 在推理效率方面表现优异推理速度可达到主流框架的数倍以上,成为当前大模型部署中极具潜力的优化方案。
6. 系统级优化
当前主流的 LLM 服务系统(如 vLLM、LLMDeploy 等)通常采用先来先服务(FCFS)的调度策略,并以“运行至完成”的方式执行任务。这种机制虽然实现简单,但在实际应用中存在一个严重问题:线头阻塞(Head-of-line Blocking)。
6.1 长作业阻塞问题与 LLM 推理服务的调度挑战
当一个长请求排在队列前面时,它会阻塞后续的短请求,即使后者所需的计算资源和响应时间远小于前者。结果是,短请求不得不等待长请求完成后才能开始处理,从而显著增加了整体排队延迟。研究表明,在真实工作负载中,排队延迟可能占总延迟的高达 90%。
需要强调的是,这里所说的“短请求”和“长请求”,并不单纯指输入提示词的长度,而是生成第一个 token 所需的时间——即所谓的 First Token Latency(首 token 延迟)。
6.2 解决方案:抢占式调度与多优先级队列
为了解决这一问题,一种可行的方法是引入抢占式调度机制:当中间出现一个高优先级的短请求时,系统可以中断当前正在执行的长请求,将已完成的部分结果缓存起来,保留未完成部分以便稍后继续处理,然后切换去执行短请求。
一旦短请求处理完毕,系统再回到之前被中断的长请求,继续执行其剩余部分。要实现这样的调度机制,系统必须支持多优先级队列的设计。
然而,这种方法本身也存在潜在缺陷:如果高级别队列中堆积了大量长请求,它们可能会被频繁中断并反复进入缓存状态,导致:
- 缓存压力增大
- 长请求的整体完成时间变长
- 系统调度开销上升
6.3 FastServe 的优化方案:多级反馈队列 + 智能 KV 缓存管理
为了解决上述问题,FastServe 提出了一个多级反馈队列(Multi-level Feedback Queue)机制。该机制的核心思想是:
在请求到达系统时,首先预估其生成第一个 token 所需的时间,并根据这一估计值将请求路由到合适的优先级队列中。
这种方式确保了短请求不会被长请求长时间阻塞,从而提升了整体服务质量与用户体验。
此外,FastServe 还结合了高效的 KV 缓存管理机制,允许在 GPU 切换队列之间进行主动的数据迁移和缓存预加载,进一步降低了上下文切换带来的延迟。
通过引入多级反馈队列与智能调度策略,FastServe 成功缓解了传统 LLM 服务系统中的线头阻塞问题,提升了短请求的响应速度,同时又避免了长请求因频繁中断而导致的性能下降。这一方法为构建高性能、低延迟的大模型推理服务平台提供了重要参考。
7. 推理优化的其他方法
在大语言模推理优化领域,有一些方法已经相对成熟,并被广大工程师广泛使用。这些技术涵盖了从模型压缩到推理加速的多个层面。
首先是量化技术,它通过降低模型权重和激活值的精度(例如从 FP16 降至 INT4 或 FP8),在几乎不影响模型性能的前提下显著缩小模型体积并提升推理速度。多种先进的量化方案已陆续被提出:AWQ 利用激活驱动的重要性评分实现激活感知量化,支持低位推理(如 INT3),无需再训练;LLM.int8() 引入带校准机制的 INT8 矩阵乘法,可在不损失准确率的前提下运行 Transformer 模型;SmoothQuant 则通过跨层对齐激活与权重范围,提升后训练量化效果;ZeroQuant 及其后续版本 V2/FP 结合了低比特量化与低秩补偿技术,支持 INT4 和 FP4 的高效推理;LLM-FP4 展示了 FP4 表示方式在保持模型质量的同时大幅提升推理效率的能力;WINT8 是专为 MoE 架构模型设计的 INT8 量化方案,已在生产环境中落地应用;SpQR 将量化与稀疏性结合,实现了近似无损的 LLM 压缩,适用于边缘部署场景;FP8-LM 探索了 FP8 格式在 Transformer 模型中的训练与推理优化,有效减少了内存占用与计算开销;而 NVIDIA 定义的 FP8 格式,也正在成为深度学习系统的重要标准之一。
另一个值得关注的方向是早期退出机制。以 LITE 为例,该方法让模型中间层学会做出预测,并在置信度足够高时提前终止生成流程,从而节省高达 38% 的推理失败成本,尤其适用于实时性要求高的场景。
在注意力机制方面,Flash Attention 是一个里程碑式的优化技术,它通过内存分块策略,在速度和内存使用上都优于传统注意力实现;ROFormer 引入旋转位置嵌入,增强了模型在长距离依赖建模上的能力;StreamLLM 则支持在流式输入过程中动态调整注意力窗口,提升了处理连续输入的能力。
此外,非自回归语言模型也在探索新的生成范式。例如 Diffusion-LM 首次将扩散模型的思想引入文本生成任务,为可控文本生成提供了新思路。
当然,所有这些技术最终都需要高效的工具链来落地。其中,vLLM 是目前最受欢迎的开源 LLM 推理库之一,由加州大学伯克利分校团队开发,专注于提供高吞吐、低延迟的语言模型服务。它起源于 Page Attention 的思想,目前已集成上述提到的几乎所有主流推理优化技术,形成了完整的推理加速解决方案。vLLM 社区活跃、生态完善,已成为当前 LLM 推理优化领域最具影响力的技术平台之一。
【参考资料与关联阅读】
https://arxiv.org/abs/2406.10774
https://arxiv.org/pdf/2211.17192
https://arxiv.org/pdf/2303.06865
https://arxiv.org/pdf/2305.05920
docs.vllm.ai
https://gist.github.com/TrungThanhTran