轻量推理框架
这是一份针对 EchoLLMServing 推理引擎架构及核心算子优化的工程笔记梳理。内容已针对技术深度、架构权衡(Trade-off)以及实现细节进行了重构,,并补充了底层逻辑分析。
EchoLLMServing 推理框架笔记
1. 架构设计与分布式执行逻辑
当前项目采用了多进程架构支持张量并行(Tensor Parallelism),控制流与数据流分离:
- 控制平面:Rank 0 作为主节点(
LLMEngine),负责请求接收、调度(Scheduler)和元数据管理(可结合 Gloo 进行跨进程协调)。 - 数据平面:副卡初始化
ModelRunner并阻塞于共享内存(shm)或类似 ZeroMQ 的异步队列等待指令。 - 执行流:Rank 0 在
generate中调用run,将计算元信息写入 shm,触发所有 Rank 并行执行前向传播。在 LMHead 阶段,各卡计算出局部的 logits 后,通过 All-Reduce 或 Gather 汇总到 Rank 0,由采样器(Sampler)根据策略输出 Token。
架构审视:这种主从架构简化了调度的复杂度,但在大规模并发下,Rank 0 的调度开销可能成为瓶颈。引入 CUDA Graph 是降低调度开销的有效手段,通过捕获算子执行序列,能够显著减少 Kernel Launch Overhead。
2. 核心算子实现与优化 (@Triton)
FlashAttention
- 定位:解决 Prefill 阶段的 Compute Bound 问题。
- 实现逻辑:对输入 Q 的
seq_len按BLOCK_M分块,对外层 Head 分块。在 Triton program 内处理tl.arange(0, head_dim)的向量化计算。 - Trade-off:虽然极大地提升了计算密度,但对 SRAM 容量要求高。
BLOCK_M的大小受限于硬件的 Shared Memory 上限。
PagedAttention (V1 -> V2 演进)
- V1 的问题:最初尝试将 context 中的全部 KV 取出再计算,导致严重的 Memory Bound。显存带宽被非连续的访存彻底打满,吞吐极低。
- V2 的改进:将 context_len 按
BLOCK_M划分,对 KV 矩阵分块并行计算 QK,进行 Online Softmax 增量更新。减少了不必要的显存搬运,将数据尽可能留在 SRAM 中处理。
FlashDecoding (长序列 Decode 优化)
在长上下文 Decode 时,传统的 PagedAttention 仍面临带宽瓶颈。引入 Split-K 机制与双 Kernel 架构优化:
- OOM 与 Reduce 代价分析:最初将 split size 固定为 64,导致每个 block 都会产生临时结果(
m,l,acc)。如果并行度过高且BLOCK_N很小,申请的 Global Memory 空间会急剧膨胀导致 OOM,且最终的 Reduce Kernel 需要遍历巨大的临时数组,抵消了并行带来的收益。 - 解决方案:将
seq_len按照预设的 split 划分为几个 chunk,每个 program 处理一个 split 内的所有 KV Cache。
针对笔记中遗留问题的解答:
- 问题:Split_num 是否可以根据
context_len动态调整?- 解答:必须动态调整。固定的 split_num 会导致短序列的 kernel 切分过细(增加 kernel 启动和 reduce 开销),而长序列切分不足(无法充分利用 SM)。通常的启发式策略是:
split_num = max(1, context_len // optimal_chunk_size),其中optimal_chunk_size根据 GPU 的 SM 数量和 Cache 大小在 Profiling 中动态标定(例如 256 或 512)。
- 解答:必须动态调整。固定的 split_num 会导致短序列的 kernel 切分过细(增加 kernel 启动和 reduce 开销),而长序列切分不足(无法充分利用 SM)。通常的启发式策略是:
- 问题:调度时是否优先调度上下文长度均匀的 Seq 以避免 Kernel 空转?
- 解答:是的。在未实现完全 Unified Attention 的情况下,同 Batch 内序列长度差异过大会导致严重的 Padding 计算浪费或线程束(Warp)分化。通过持续批处理(Continuous Batching)结合细粒度的 Block 管理,可以最大程度缓解这个问题。
Fused Add-RMSNorm 与 Triton 拓展
- 问题分析:使用 PyTorch 原生的
x = x + residual然后做 RMSNorm 时,发现总吞吐下降,通过 Nsight Systems (nsys) 分析证实,内存拷贝与读写开销(Memory Access)是主因。 - Triton 实现权衡:
- 写融合算子,将读、Add、写 Res、Norm 全部在一个 Kernel 内完成。
- 关于分块与 Kernel Launch Overhead:对输入向量的最后一个维度(
hidden_dim)进行处理。如果hidden_dim过大(如 Llama 的 4096),可以将其拆分为几个 Block。并行度高导致的 Launch Overhead 可以通过 CUDA Graph 解决。关键在于利用tl.load时的向量化读写(保证首地址对齐)。
3. 调度、显存管理与特性支持
异步推理与 Continuous Batching
AsyncLLMEngine 通过 asyncio.queue 实现了请求的非阻塞生成。Continuous Batching 的核心价值在于打破了静态 Batch 的生命周期绑定,每次 Step 后立即回收完成状态的 Seq 所占用的 Block 资源,并打入新请求,极大提升了吞吐。
Chunked Prefill 调度规则
- 核心动机:解决长短 Prompt 混杂时的 Time to First Token (TTFT) 延迟毛刺,以及分离阶段导致 Decode 计算资源利用率低的问题。
- Block Manager 状态不一致问题:Chunked Prefill 会导致
num_cached_tokens与实际输入的input_ids长度在单次 forward 中不匹配,必须在 post-process 中进行增量状态维护。
针对笔记中遗留问题的解答:
- 问题:混合 Batch 时,分开做两种 Attn 和合成一种(Unified Attention)有什么区别?
- 解答:
- 分开做(分离 Kernel):工程实现简单。Prefill 的请求走 FlashAttention,Decode 的请求走 FlashDecoding。缺点是 Batch 较小时,两个 Kernel 依次 Launch,可能无法跑满 GPU 的 SM,存在硬件资源的波谷。
- 合成一种(Unified Attention):将 Decode 视为长度为 1 的特殊 Prefill 阶段,使用一套底层机制处理。这要求极高的指针与索引管理技巧(类似 FlashInfer 的设计)。优点是只需 Launch 一个 Kernel,SM 占用率极高;缺点是分支预测和 Warp 分化处理非常复杂。前期建议分开做,后期追求极限性能时再考虑 Unified。
- 解答:
KV Cache INT8 量化
- 工程经验:最初全量反量化(Dequantization)导致严重的显存读写墙,耗时极高。
- 正确路径:将 Per-token 反量化逻辑直接 Fuse 进 FlashAttention/FlashDecoding Kernel 内。在读取 INT8 Cache 到 SRAM 后,立即使用伴随的 Scale 因子还原为 FP16 进行计算。这一操作以极小的计算开销(Compute)换取了 50% 的显存带宽(Bandwidth)与容量收益。
4. 底层架构思考 (Thinking & Trade-offs)
Decode 阶段 复杂度缓解策略
Decode 本质上是 Memory Bound,复杂度随上下文线性增长。常见缓解方案:
- 算法层:Sliding Window Attention (SWA)、KV 压缩/池化(如 H2O)、稀疏注意力。
- 系统层:前缀缓存(RadixTree LRU Prefix Cache),将公共的前缀(如 System Prompt)复用,避免重复计算并节省 KV Cache 空间。
为什么必须用 Triton/CUDA 代替 PyTorch 张量广播?
PyTorch 的张量广播发生在逻辑层和 Global Memory 层面。它会产生大量隐式的中间张量,导致 HBM 被反复读写(Memory Wall)。Triton 和 CUDA 的核心优势在于显存层级控制:将高复杂度的
还有什么值得用 Triton 优化的算子?
- Fused RoPE + QKV 投影:将旋转位置编码直接融合进 QKV 的 Linear 计算之后,避免显存的来回搬运。
- Fused SwiGLU (或 GeLU):激活函数通常是单纯的 element-wise 操作,典型的 Memory Bound,非常适合融合。
- MoE Top-K Routing:如果未来支持 Qwen3-MoE,专家路由的 Top-K 计算、Softmax 及 token 重排,使用 Triton 可以大幅降低开销。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 西郊有密林 祝君出重围!


