@Kay2289123: 分享一份最近整理的AI基础设施入门阅读清单——建议收藏并慢慢阅读……
摘要
这篇文章分享了一份精选的AI基础设施入门阅读清单,重点关注大型模型推理主题,如vLLM、连续批处理和性能基准测试。
查看缓存全文
缓存时间: 2026/09/12 02:44
分享一份最近整理的 AI Infra 入门阅读清单——建议收藏并慢慢阅读。AI 现在已经能生成大量优质内容,让概念阐释、论文翻译、资料整理变得更容易。但在这个时代,能够静下心来细读一篇好文章、琢磨不懂的部分——我认为这反而更加稀有和宝贵。这次整理的材料主要聚焦大模型推理。从 KV 缓存与内存管理,到请求调度与推理加速,再到如何判断性能是否真的提升。从事工程实践的同学可以顺藤摸瓜深入源码和实验;平时关注 AI 产业、算力和存储的同学,可以先打牢原理基础。阅读文章时,挑选那些值得反复精读的。
- 若只选一篇,我会选 Inside vLLM。Inside vLLM: Anatomy of a High-Throughput LLM Inference System, by Aleksa Gordić
https://aleksagordic.com/blog/vllm
它将许多分散概念串联成一个真实系统:请求如何进入、如何排队、KV 缓存如何分配、如何执行、最终如何返回结果。PagedAttention、连续批处理、分块预填充、前缀缓存、推测解码、P/D 分离、多 GPU、性能测试——所有这些都能在这条主线上找到位置。这篇非常适合作为主线,后续材料用来填补细节空白。 - 要真正理解连续批处理,先读 Anyscale 这篇。Continuous Batching blog
https://anyscale.com/blog/continuous-batching-llm-inference…
LLM 用户的响应长度各异——有些已完成生成,有些仍在进行。已释放的槽位能否立即承接下一个请求?本文通过静态批处理与连续批处理的对比图表,非常直观地解释了这个问题。读完你会明白,GPU 效率也取决于请求的调度方式。建议重点关注图表和机制部分。 - NVIDIA 的两篇文章:一篇构建全景认知,一篇教你如何评估性能。
Mastering LLM Techniques: Inference Optimization
https://developer.nvidia.com/blog/mastering-llm-techniques-inference-optimization…
非常适合建立基础认知:预填充(处理输入)与解码(逐步生成输出)的区别、模型权重与 KV 缓存各自占用什么空间、量化、批处理和多 GPU 并行分别解决什么问题。
LLM Inference Benchmarking: Fundamental Concepts
https://developer.nvidia.com/blog/llm-benchmarking-fundamental-concepts/…
同样强烈推荐。用户等待第一个 token 的时长、后续生成速度、系统每秒可服务多少请求——这些是不同问题。TTFT、ITL、TPOT 等指标需结合测试方法论来看。 - 想要更深入数学细节,可读 kipply 的 Transformer Inference Arithmetic。
Transformer Inference Arithmetic
这是一篇 2022 年的文章。它引导你从计算量、内存容量、带宽和通信开销出发,估算推理快慢的原因,以及 batch size 变化时会发生什么。硬件和模型示例已过时,可作为选读。
Inside vLLM:高吞吐量 LLM 推理系统解析
- Aleksa Gordić
来源:https://www.aleksagordic.com/blog/vllm
在本文中,我将逐步介绍构成现代高吞吐量 LLM 推理系统的所有核心组件和高级特性。我将重点拆解 vLLM[1] 的工作原理。
本文是系列文章的第一篇。它从宏观入手,然后层层深入细节(采用倒金字塔方式),让你在不淹没于琐碎细节的情况下,对完整系统形成准确的高层心智模型。后续文章将深入探讨特定子系统。
本文分为五个部分:
- LLM 引擎与引擎核心 (https://www.aleksagordic.com/blog/vllm#cpt1):vLLM 基础(调度、分页注意力、连续批处理等)
- 高级特性 (https://www.aleksagordic.com/blog/vllm#cpt2):分块预填充、前缀缓存、引导式与推测解码、分离式 P/D
- 扩展 (https://www.aleksagordic.com/blog/vllm#cpt3):从单 GPU 到多 GPU 执行
- 服务层 (https://www.aleksagordic.com/blog/vllm#cpt4):分布式/并发 Web 架构
- 基准测试与自动调优 (https://www.aleksagordic.com/blog/vllm#cpt5):测量延迟与吞吐量
📝 说明
- 分析基于提交 42172ad (https://github.com/vllm-project/vllm/tree/42172ad)(2025 年 8 月 9 日)。
- 目标读者:任何对最先进 LLM 引擎工作原理感兴趣的人,以及有兴趣为 vLLM、SGLang 等项目做贡献的人。
- 我将聚焦于 V1 引擎 (https://docs.vllm.ai/en/latest/usage/v1_guide.html)。我也研究过 V0(现已弃用 (https://github.com/vllm-project/vllm/issues/18571)),这对于理解项目演进很有价值,许多概念仍然适用。
- 关于 LLM 引擎/引擎核心的第一部分可能有点难懂/枯燥——但博客的其余部分有很多例子和可视化。:)
LLM 引擎与引擎核心
LLM 引擎是 vLLM 的基本构建块。它本身就能实现高吞吐量推理——但仅限于离线场景。目前还无法通过 Web 为其提供客户服务。我们将使用以下离线推理代码片段作为示例(改编自 basic.py (https://github.com/vllm-project/vllm/blob/main/examples/offline_inference/basic/basic.py))。
from vllm import LLM, SamplingParams
prompts = [
"Hello, my name is",
"The president of the United States is",
]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
def main():
llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0")
outputs = llm.generate(prompts, sampling_params)
if __name__ == "__main__":
main()
📝 环境变量:
- VLLM_USE_V1=“1” # 我们使用引擎 V1
- VLLM_ENABLE_V1_MULTIPROCESSING=“0” # 我们在单进程中运行
此配置是:
- 离线(无 Web/分布式系统架构)
- 同步(所有执行在单个阻塞进程中发生)
- 单 GPU(无数据/模型/流水线/专家并行;DP/TP/PP/EP = 1)
- 使用标准 transformer[2](支持混合模型如 Jamba 需要更复杂的混合 KV 缓存内存分配器)
从这里开始,我们将逐步构建一个在线、异步、多 GPU、多节点的推理系统——但仍然服务于标准 transformer。在此示例中,我们做两件事:
- 实例化引擎
- 调用其
generate方法,从给定提示中进行采样
让我们先分析构造函数。
LLM 引擎构造函数
引擎的主要组件包括:
- vLLM 配置(包含配置模型、缓存、并行等的所有旋钮)
- 处理器(通过验证、分词和处理,将原始输入转换为
EngineCoreRequests) - 引擎核心客户端(在我们的示例中,使用
InprocClient,它基本等同于EngineCore;我们将逐步构建到DPLBAsyncMPClient,以实现规模化服务) - 输出处理器(将原始
EngineCoreOutputs转换为用户看到的RequestOutput)
📝 注意:随着 V0 引擎弃用,类名和细节可能会变化。我将强调核心思想而非确切签名。我会抽象掉部分但非全部细节。
引擎核心本身由几个子组件构成:
- 模型执行器(驱动模型的前向传播,目前我们处理的是
UniProcExecutor,它在单个 GPU 上有单个Worker进程)。我们将逐步构建到支持多 GPU 的MultiProcExecutor。 - 结构化输出管理器(用于引导解码——稍后介绍)。
- 调度器(决定哪些请求进入下一个引擎步骤)——它进一步包含:
- 策略设置——可以是 FCFS(先来先服务)或 priority(高优先级请求先处理)
waiting和running队列- KV 缓存管理器——分页注意力[3] 的核心
KV 缓存管理器维护一个 free_block_queue——一个可用 KV 缓存块池(通常数十万块,取决于显存大小和块大小)。在分页注意力中,这些块作为将 token 映射到其计算出的 KV 缓存块的索引结构。
LLM 引擎构造函数
本节描述的核心组件及其关系
标准 transformer 层(非 MLA[4])的块大小计算如下:
2(键/值)* block_size(默认=16)* num_kv_heads * head_size * dtype_num_bytes(例如 bf16 为 2)
在模型执行器构造期间,会创建一个 Worker 对象,并执行三个关键过程。(稍后,使用 MultiProcExecutor 时,这些相同过程将在不同 GPU 上的每个 worker 进程中独立运行。)
-
初始化设备:
- 为 worker 分配一个 CUDA 设备(例如 “cuda:0”)并检查模型数据类型是否受支持(例如 bf16)
- 验证在给定的
gpu_memory_utilization(例如 0.8 → 总显存的 80%)下是否有足够显存 - 设置分布式设置(DP / TP / PP / EP 等)
- 实例化
model_runner(包含采样器、KV 缓存以及前向传播缓冲区,如input_ids、positions等) - 实例化
InputBatch对象(包含 CPU 端的前向传播缓冲区、用于 KV 缓存索引的块表、采样元数据等)
-
加载模型:
- 实例化模型架构
- 加载模型权重
- 调用
model.eval()(PyTorch 的推理模式) - 可选:对模型调用
torch.compile()
-
初始化 KV 缓存:
- 获取每层的 KV 缓存规格。历史上这总是
FullAttentionSpec(同构 transformer),但对于混合模型(滑动窗口、类似 Jamba 的 Transformer/SSM),它变得更复杂(参见 Jenga[5]) - 运行一次虚拟/分析前向传播,并捕获 GPU 内存快照,以计算在可用显存中可容纳多少 KV 缓存块
- 分配、重塑并将 KV 缓存张量绑定到注意力层
- 准备注意力元数据(例如将后端设置为 FlashAttention),稍后在前向传播期间由内核使用
- 除非提供
--enforce-eager,否则对每个预热批大小进行虚拟运行并捕获 CUDA 图。CUDA 图将整个 GPU 工作序列记录到一个 DAG 中。稍后在前向传播期间,我们启动/重放预烘焙的图,以减少内核启动开销,从而提高延迟。
- 获取每层的 KV 缓存规格。历史上这总是
我在这里抽象了许多底层细节——但这些是我现在要介绍的核心部分,因为后续章节会反复引用它们。
现在引擎已初始化,让我们继续讨论 generate 函数。
Generate 函数
第一步是验证并将请求输入引擎。对于每个提示,我们:
- 创建一个唯一的请求 ID 并捕获其到达时间
- 调用输入预处理器对提示进行分词,并返回一个包含
prompt、prompt_token_ids和type(文本、token、嵌入等)的字典 - 将此信息打包到
EngineCoreRequest中,添加优先级、采样参数和其他元数据 - 将请求传递给引擎核心,引擎核心将其包装在
Request对象中并将其状态设置为WAITING。此请求随后被添加到调度器的waiting队列(如果是 FCFS 则追加,如果是优先级则堆推送)
此时,引擎已被输入,执行可以开始。在同步引擎示例中,这些初始提示是我们将处理的全部——没有在运行期间注入新请求的机制。相比之下,异步引擎支持这一点(即连续批处理[6]):在每个步骤之后,新旧请求都会被考虑。由于前向传播将批次展平为单个序列,并且自定义内核高效处理它,即使在同步引擎中也基本支持连续批处理。
接下来,只要有要处理的请求,引擎就反复调用其 step() 函数。每个步骤有三个阶段:
- 调度:选择在此步骤中运行哪些请求(解码,和/或(分块)预填充)
- 前向传播:运行模型并采样 token
- 后处理:将采样的 token ID 附加到每个
Request,反 token 化,并检查停止条件。如果请求完成,则进行清理(例如将其 KV 缓存块返回到free_block_queue)并提前返回输出
📝 停止条件是:
- 请求超过其长度限制(
max_model_length或其自身的max_tokens) - 采样的 token 是 EOS ID(除非启用了
ignore_eos——这在我们想强制生成特定数量输出 token 的基准测试时很有用) - 采样的 token 与采样参数中指定的任何
stop_token_ids匹配 - 输出中存在停止字符串——我们在首次出现停止字符串处截断输出,并在引擎中中止请求(注意
stop_token_ids会出现在输出中,但停止字符串不会)。
引擎循环
引擎循环
在流式传输模式下,我们将在生成时发送中间 token,但我们现在忽略这一点。接下来,我们将更详细地检查调度。
调度器
推理引擎处理两种主要类型的工作负载:
- 预填充请求——对所有提示 token 进行前向传播。这些通常是计算受限的(阈值取决于硬件和提示长度)。最后,我们从最终 token 位置的概率分布中采样单个 token。
- 解码请求——仅对最近一个 token 进行前向传播。所有早期的 KV 向量都已缓存。这些是内存带宽受限的,因为我们仍然需要加载所有 LLM 权重(和 KV 缓存)才能计算一个 token。
在基准测试部分 (https://www.aleksagordic.com/blog/vllm#cpt5),我们将分析所谓的 GPU 性能 roofline 模型。那将更详细地探讨预填充/解码性能概况。
V1 调度器可以在同一步骤中混合两种类型的请求,这得益于更智能的设计选择。相比之下,V0 引擎一次只能处理预填充或解码。
调度器优先处理解码请求——即那些已在 running 队列中的请求。对于每个这样的请求,它:
- 计算要生成的新 token 数量(不总是 1,由于推测解码和异步调度——稍后详述)。
- 调用 KV 缓存管理器的
allocate_slots函数(详见下文)。 - 通过减去步骤 1 中的 token 数量来更新 token 预算。
之后,它处理来自 waiting 队列的预填充请求,它:
- 检索已计算的块数(如果禁用前缀缓存则返回 0——我们稍后会介绍)。
- 调用 KV 缓存管理器的
allocate_slots函数。 - 将请求从 waiting 弹出并移至 running,将其状态设置为
RUNNING。 - 更新 token 预算。
现在让我们看看 allocate_slots 的功能,它:
- 计算块数——确定必须分配多少个新的 KV 缓存块(
n)。每个块默认存储 16 个 token。例如,如果一个预填充请求有 17 个新 token,我们需要ceil(17/16) = 2个块。 - 检查可用性——如果管理器池中没有足够的块,则提前退出。取决于它是解码还是预填充请求…
相似文章
@antiAIvo: 我已经学习了Transformer架构的基础知识、推理和训练流程。接下来,我将开始新一轮的深入探讨…
作者介绍了一门关于大模型推理优化的学习路径,涵盖KV缓存、连续批处理、分页注意力机制等核心议题,并对vLLM与SGLang展开对比分析,突出了该领域在AI部署中的前沿技术地位。
AI推理工程指南(阅读时间约17分钟)
本指南解释了AI推理工程这一学科,涵盖了预填充和解码阶段的划分、从封闭模型到开放模型的转变,以及针对延迟、吞吐量和成本的优化技术。
@akshay_pachaar: https://x.com/akshay_pachaar/status/2094765529231929361
本文是一篇实践指南,介绍如何为代理工作运行本地AI模型,重点讨论硬件权衡,并引入Magnitude,一个开源推理服务器,它简化配置以优化性能。
@TheAhmadOsman: 如何学习这一切?第一步:从服务引擎视角开始 - vLLM:PagedAttention、连续批处理...
一份关于学习AI推理引擎内部机制的详细指南,涵盖vLLM和SGLang等服务引擎、使用Triton和CUTLASS的低层GPU内核编程,以及一系列旨在培养实践能力的小型项目。
@suraj_sharma14:如果你想精通推理工程,这个资源是一座金矿:https://github.com/elizabetht/10…
一个GitHub仓库,提供结构化的100天LLM推理工程学习计划,涵盖从CUDA内核到自动扩展的主题,并配有可运行的脚本。