EP10-多机DeepSeek优化部署
[FIXME] [EP10] 多机DeepSeek优化部署
直播回看链接:https://www.youtube.com/watch?v=UMf5-K4PX8Q
特别鸣谢:组织者@月球大叔, 主讲人@Du Kuntai, @Cheng Yihua
飞行嘉宾是Perplexity AI的Research Engineer、Punica的作者Chen Lequn
💥 背景与介绍
Lequn: 在大多数系统中,延迟和吞吐是相互冲突的目标。
延迟——用户感知
吞吐——成本
但Deepseek这种MOE模型在大多数场景下利用多机部署中更多的GPU可以达到更高的吞吐和更低的延迟。
为什么要用DP?
通过EP,我们可以将MoE计算分散在128个甚至更多的GPU上。如果单纯地将MLA按EP数进行TP,每个GPU分到的注意力头数量较少,会导致收益递减。这时,我们可以引入数据并行(Data Parallelism, DP)。每个DP组都有一个完整的MLA层副本。每个DP组接受不同的输入并独立执行MLA层计算。
MLA层的DP和TP可以组合,一个DP组可以被分成多个TP Rank。MoE层的EP可以与MLA层的DP/TP组合。EP = DP * TP
。例如,在16台机器上,EP128 DP32 TP4意味着将路由专家分布在128个GPU上,每4个GPU形成一个DP组,总共32个独立的DP组。
有32个DP这么多GPU是怎么做到负载均衡的,或者对于高utilization问题,怎么做到负载均衡呢?
Lequn: 这就是为什么我们前面会有一个scheduler,它相当于知道所有实例的负载情况,可以去做负载均衡。然后注意到其实这个scheduler也不需要跑的特别快,它不是像ingress那种外部服务器的那种负载均衡,因为qps不会太大,1000已经是一个比较大的体量了。
Yihua, Kuntai: LMCache团队开发的一个计算kv cache的小工具,可以在浏览器搜索“KV Cache Size Calculator”找到,但是现在计算Deepeek的kv cache结果是错误的(Deepseek-671B中一个token占用70272 bytes的KV缓存),欢迎去提pr!
不同配置下的性能分析
Lequn: 使用一台H200机器进行单机部署,最多16台H100机器进行多机部署。对于每个部署环境,我们使用TP 1、2、4、8和每卡批处理大小1、2、4、8、16、32、64、128的组合。下图显示了不同配置的吞吐量和输出速度。
横轴表示每个请求的输出速度(词元/秒)。纵轴使用对数刻度显示每台机器的吞吐量(词元/秒)。我们用不同颜色的线标记了每个EP配置的帕累托边界。
关键Insight
- 单机EP8配置:批处理大小为1时输出速度极快(>100词元/秒),但吞吐量低;增大批处理会激活更多专家,加重内存带宽压力,显著降低输出速度。
- 高EP数优势(如EP128):每个GPU负载的专家更少,内存带宽压力小,增大批处理时输出速度更稳定,吞吐量更高(比单机高约5倍)。
- 多机部署对比:
- 更高EP值(如EP128)同时提升吞吐量和输出速度。
- 极大批处理时,单机吞吐可能略优于多机(因NVLink带宽更高或实现限制),但受内存容量限制,多机仍是高吞吐场景的更优选择。
核心结论
- 低延迟场景:单机小批处理(EP8)。
- 高吞吐场景:多机高EP配置(如EP128),平衡速度与吞吐。
有趣的是,在更大的批处理大小(每卡64个请求)上,我们观察到一个新现象:单机部署吞吐量略高于多机部署。部分原因是节点内NVLink的带宽高于节点间InfiniBand。另一部分是由于我们实现的限制。我们将在后面更详细地分析这种现象。
Q&A
Listener: 说了一种非常好的理解方式,多机的性能提高可以把它理解成等效的内存带宽提升。因为在decode的时候出来,是内存受限的,把expert放到更多的GPU上,相当于提高了整个cluster的内存带宽。
Lequn: 核心是MOE在做计算的时候,expert要从显存读到register里面,如果要读的expert数大,即使你提高batch,对于提升throughput是不利的,而EP可以缓解这个问题,在读expert数小的时候,提高batch能有效提高throughput。
Listener: 对于不同的request,可能路由到不同的expert上,如何避免一些expert overload,一些expert underload?
Lequn: 对于overload的expert做replica,调整冗余专家的放置就好了,EPLB。
其实这里Deepseek报告里有说。
System N把斧:cache, batch, shard, replica, overlap…
今天介绍的内容主要是解决decode阶段的难点,不太考虑prefill。
Character.AI,Mooncake也公布了自己对于推理优化的一些技术,感兴趣的可以看我上一篇文章,或搜索”Character AI inference blog”即可。
📌 计算和通信重叠
如上文关于专家并行的介绍,GPU在MoE层通信期间是空闲的。为减少浪费并降低延迟,我们需要找到数据独立的计算任务来填充这个空闲时间。
主要优化在于使用DeepSeek技术报告中提到的 micro batch 来打破数据依赖。如图下部所示,我们将一个Transformer层的计算分为5个阶段:
- 阶段1:InputNorm, QKVProj, AppendKV, BMM
- 阶段2:BMM, Attn, OProj, PostNorm, Gate
- 阶段3:Dispatch Send, Shared Expert
- 阶段4:Dispatch Recv, MoE, Combine Send
- 阶段5:Combine Recv
在前3个密集Transformer层中,我们使用整个 batch。在接下来的58个MoE Transformer层中,我们将 batch 平均分成两个 micro batch。两个 micro batch 交替执行,相差3个阶段。由于这两个 micro batch 之间没有数据依赖,我们可以在Dispatch Send和Combine Send之后切换到另一个 micro batch 的计算。
Q&A
Yihua: 在不同micro batch之间切换如何实现?
Lequn: 两个办法。1. 保存中间结果,相当于你手动维护这个状态机。2. 利用python语法糖,比如yield。
Yihua: 用python yield会影响cuda graph吗?
Lequn: 不影响,会先走一个capture,这是一个静态的执行。
月球大叔:这个overlap是有两个cuda stream吗?
Lequn: 好问题。只需要1个cuda stream就行了,两个micro batch在计算的时候是没有重叠的部分的,在算的时候搞对输入输出就行了。
Deepseek公开了一个trace,可以用Perfetto打开看kernel是怎么运行的
基于上面的trace,Lequn做了一个分析,包括函数、stage之间的latency占比
有意思的是,使用micro batch并不是在所有情况都是好的
在高batch size的时候效果才比较好,可能的原因是在batch小的时候,再做拆分会使每个kernel计算效率降低。
整个layer的latency
关键Insight
- 比较EP8和EP128 Microbatch。EP8总共花费1802微秒,略少于EP128的1896微秒。除了上面提到的Microbatch带来的内核执行时间增加外,主要差异在于用于MoE计算的GroupGEMM,以及两个通信内核Dispatch和Combine。
- EP8的GroupGEMM比EP128的时间减少了一半。这是多机部署的核心优势。
- 不幸的是,通信花费的时间增加了213微秒,这大大抵消了GroupGEMM的优势。在我们通信内核的单独性能测试中,我们发现它们只能达到Infiniband带宽的一半。我们将继续优化通信内核。
- 另一个明显拖后腿的内核是GEMM。Microbatch增加了GEMM 95微秒。
Roofline: GROUPGEMM
Lequn: 我们使用DeepGEMM的GroupGEMM实现进行性能测试。测试点覆盖了EP8、EP16、EP32、EP64、EP128配置与TP1和批处理大小1-128的组合。
上图显示了不同EP配置下GroupGEMM的屋顶线模型。不同的EP对应不同数量的group。图中展示了几乎重叠的性能曲线,表明GroupGEMM性能主要由总词元数(表示为g * m)决定。
星号标记了每个EP配置下对应于每个GPU批处理大小为128的数据点。比较这些星号数据点,我们可以看到随着EP增加(DP也同步增加),每个专家的词元数m也增加。在EP8时,m=128,而在EP128时,m=2048。
随着m增加,算术强度也增加。在大多数配置中,GroupGEMM受内存带宽限制,因此增加m可以提高性能。
Kuntai: 分享一个talk, compute每年增速约1.3倍,但memory为1.2倍。未来会有更多的operater进入到memory bound中。
更多的内容请自行搜索“多机部署DeepSeek实现更低延迟和更高吞吐量”的博文!
🔔 LMCache中为什么要用buffer?
Yihua:
- page size太小的时候,可能打不满传输的带宽,一个可能考虑的解决方案是有一个稍微大一点的transfer buffer, 然后可以做device to device的,就是inter device的一些简单的copy。把东西都整合到一个大的buffer里的时候,再一起发出去。这样底层网络传输的带宽利用率会更高。但是它有一个extra copy的overhead,有一个trade off。
- 在使用CPU进行MemCopy的时候,像vllm这种page size小的,如果对每一个layer launch一个kernel,每一个page的16个token做一次,这个开销是比较大的。如果把这些pages都collect到一起,然后再做一次copy的PCIe transfer可能会更快一些。
- 还有一个办法是,直接把host memory swap到GPU的UBM上面…..(这里没听清楚,不懂)
⚡ Kuntai布置的几个小作业
多模型部署,怎么在production stack里面部署多个不同类型的LLM,比如一个qwen-3B和一个LLama-8B模型。
如何开启Session-base router。根据请求的session-id做路由。
Mooncake kv cache hit rate是多少?
前两个问题比较基础,用过production-stack的人应该都知道,我最近也在它上面做了一些测试和优化,感兴趣或有疑问的小伙伴欢迎来交流讨论!第三个问题,从mooncake仓库里可以看到相关描述“up to 50% cache hit ratio”。