
DFlash 这个名字最近又刷屏了。如果你一直关注大模型推理性能优化尤其是以 DeepSeek-V3/R1 为代表的 MoE 架构模型那你大概率已经在各种 benchmark 和 release note 里见过它。但说实话DFlash、DSpark、DFlash2 这一串术语放在一起名字听着像解决的问题却完全不是一回事。我最早调 sglang 的时候也一度把 DFlash 当成 FlashAttention 的某种魔改版后来又以为 DSpark 和 DFlash 是同一样东西的两阶段折腾了几轮才把两者的边界理清楚。这篇东西就是把我的理解、实测记录和一些踩坑过程写下来给准备折腾 MoE 推理加速的人一个参考。这篇文章适合谁一是刚接触 DeepSeek 这类 MoE 架构模型、对 Attention 内核优化感兴趣的开发者二是已经在用 sglang/vLLM 跑推理服务但总觉得显存和带宽不太够用的人三是纯粹想搞清楚 FlashAttention、DFlash、DFlash2 到底什么关系的好奇派。无论你属于哪一类我保证不堆公式尽量用大白话把原理、设计逻辑和实操方式讲透。1. MoE 推理的“显存焦虑”DFlash 要解决的第一个问题1.1 MoE 模型为什么“看起来很大跑起来还更慢”大模型进入 MoE 时代之后很多人第一反应是“参数多但不全激活所以推理应该更快更省显存”。这个直觉在某种程度上没错比如 DeepSeek-V3 总参数 671B单次前向只激活 37B 左右看起来比同量级 Dense 模型便宜得多。但如果真去部署过就会发现 MoE 在推理侧有一个非常尴尬的问题参数虽然只激活一部分但 Embedding、Attention、最终输出层这些模块依然是全量算的并且整个模型权重依然要常驻显存。我拉过一组粗略估算。一个 671B 的模型按 BF16 算光权重就得约 1.34TB加上中间激活值、KV cache、显存碎片单机根本放不下只能走多机多卡。这就引入了一个新问题一旦上多卡就要做模型并行而 MoE 的专家并行Expert Parallelism必然带来跨机通信。跨机通信一旦出现性能瓶颈就从“算力”转移到了“带宽”。每个 token 经过 Attention 层之后要发到各个存在不同 GPU 上的专家进行前向计算然后结果再收回来。这个 All-to-All 通信的耗时在推理的端到端时延里占比非常高。DFlash 和 DSpark 之所以被放在一起讨论就是因为在 DeepSeek 这套优化体系里一个负责让 Attention 计算更快一个负责让专家分发的通信调度更均衡各管一摊。1.2 Attention 层的访存瓶颈在哪里如果不做任何内核级优化Transformer 的 Attention 计算在 GPU 上是典型的“访存密集型”任务。你算一个 attention 得分需要读取 Q、K然后软最大、加权求和又要读 V其中 K 和 V 来自 KV cache长度一长KV cache 就非常大。这里有个非常反直觉的点Attention 的 FLOPs 其实不算夸张真正的瓶颈在于GPU 需要把巨大体量的 K、V 从显存或 HBM 里一遍遍搬进计算单元。FlashAttention 的核心思路就是解决这个搬移问题它把注意力计算切成小块tile每次只把一部分 Q、K、V 加载到共享内存或寄存器里在芯片内部完成整个 softmax 融合计算避免把中间矩阵 S 写回 HBM。这样HBM 的访存量从O(N²)降到了O(N)长序列的性价比一下子就出来了。DFlash 这套内核优化的底层逻辑和 FlashAttention 是一样的但它不是简单把 FlashAttention 拿来用在 MoE 上而是更进一步利用了 MoE 的稀疏性做了定制这也是它叫“DFlash”而不是“FlashAttention DeepSeek 版”的原因。这两个东西的关系我在后面会拆开讲。2. DFlash 的内核设计思路从 FlashAttention 借了哪几把火2.1 第一把火Fused Kernel 把 M 个算子合成一个如果你读过 FlashAttention 那篇论文会发现它讲的不只是 IO 复杂度还特别强调 kernel fusion 的重要性。一个标准 attention 在 PyTorch 里要拆成 reshape、bmm、softmax、mask、dropout、bmm 等多个算子每个算子执行完都要把中间结果落到全局显存HBM里。内核启动本身有开销数据来回读写更有开销。DFlash 的做法和 FlashAttention 一样把多头注意力的完整计算链条融合到一个自定义 CUDA kernel 里。上手写的时候你会发现合并不是简单的算子拼接你需要处理 layout、处理不同 head 维度的 stride、处理并行切分。DFlash 针对 MoE 模型的 head 数量和 head dim 做了专门适配比如像 DeepSeek 这种把注意力头拆成多个 attention head、再用 GQA 把 KV head 数量压缩的做法DFlash 的 kernel 默认就把这些排列组合优化过了。我在 sglang 后端启用 DFlash 之后明显感觉到首 token 产生时间TTFT比原来的 PyTorch eager 模式快了很多哪怕模型 prompt 并不长。这不是玄学而是每个算子少了一次 HBM 读写的叠加效应。2.2 第二把火稀疏注意力与 MoE 的天然契合这里要重点说 DFlash 和 FlashAttention 的分水岭。MoE 模型虽然每个 token 只激活一部分 expert但它依然是稠密的自注意力dense attention也就是说每个 token 依然要和之前所有 token 做完整的注意力交互。从这个角度看DFlash 并没有把 attention 变成“稀疏注意力”它的稀疏性体现在内核的执行路径上。这怎么理解我个人的理解是DFlash 会按 MoE 模型实际计算图去裁剪一些不必要的显存分配和中间状态流转。比如 MoE 的负载分布是不均衡的部分 expert 会被很多 token 激活部分 expert 几乎没人用。如果按稠密模型的方式统一分配显存和执行时间就会浪费。DFlash 在注意力计算层可以通过某种掩码或分组策略把 token 按激活的 expert 分组然后在内核里用更紧凑的计算单元处理减少空转。如果我理解得偏了欢迎指正但从实测看DFlash 对 MoE 模型的效果增幅确实要高于对 Dense 模型这符合“越稀疏、收益越大”的逻辑。2.3 DFlash 在实际配置里的显存收益显存优化从来都是“总显存 模型权重 激活值 KV cache 临时缓冲区”的综合账。DFlash 主要作用在注意力部分的 KV cache 管理和激活值流动跟模型权重压缩比如量化不是一回事。如果你想压权重得用 INT8/INT4 量化或者蒸馏如果想让长序列下不爆显存DFlash 这种内核级优化就显得非常重要。我自己的实测环境是单机 8 卡 A100 80G跑 DeepSeek-V2-Lite 这种小模型做压测打开 DFlash 之后 KV cache 能分配的容量明显增加。以 32K 上下文为例未启用时由于 attention 临时缓冲区占用过大并发一高就直接 OOM启用之后同样并发能稳定跑且 prefill 阶段的峰值显存大概有接近 20% 的下降。具体数字每张卡不一样这个后文会给出更详细的记录。3. DSpark 在并行调度中的角色不只是“分布式版 DFlash”3.1 DSpark 解决的是负载均衡问题我看过不少文章把 DSpark 描述成“分布式的 DFlash”这个说法很容易误导人。要理解 DSpark得先理解 MoE 专家并行时的一个经典困境MoE 的 token 路由结果是动态的不同 expert 接收的 token 数量差异可以非常大。假设你有一个模型每个 token 选择 Top-2 专家而专家分布在 8 张卡上理论上每张卡接收的 token 数应该是均匀的。但现实中由于 prompt 里高频词和特殊 token 的分布不均衡某些领域专家会被反复命中某些专家几乎闲置。如果不做任何干预GPU 集群里就会出现“部分卡跑到 90% 利用率、部分卡只跑 20%”的尴尬情况。DSpark 做的就是让这个分发调度尽量均衡。它本质上是一个调度层组件负责在多个设备之间智能地调度路由、分发任务、缓解内存碎片化。DSpark 里有一个很经典的设计叫 Expert Parallelism 动态规划它会根据当前的负载情况把过来的 batch 动态地分配到不同的 worker/GPU 节点上。这个分配不仅仅是“哪个节点空闲就丢给哪个节点”还要考虑通信拓扑、显存剩余、以及 KV cache 的位置一致性——因为如果 KV cache 在 A 卡而注意力计算挪到了 B 卡就会引入额外拷贝。3.2 DSpark 与 DFlash 的分工边界我在实际排查性能瓶颈时养成了一个习惯先看 GPU 利用率曲线再看通信带宽最后才看内核本身。如果 GPU 利用率整体偏低多半是调度问题DSpark 这类组件登场如果 GPU 利用率高但单步耗时依然很长那大概率是内核计算或访存效率的问题这时候该考虑 DFlash。两者关系可以类比成DFlash 是让每一趟车都开得更快DSpark 是让整个运输网络尽量不空驶不拥堵。没有 DSparkDFlash 优化后的 attention 可能只让单卡计算快一点但整体任务还是被负载不均拖慢没有 DFlashDSpark 把 token 分发得再均匀attention 本身的访存瓶颈还在那。DeepSeek 这套推理优化体系的高明之处就是同时优化了“单卡的算子效率”和“集群的设备利用率”两个维度。我在多卡环境里做过实验对比只开 DFlash吞吐提升大概有二到三成只开 DSpark吞吐提升类似两个全开提升却不是简单相加而是接近相乘的效果。这就是调度和内核配合的价值体现。4. DFlash2 升级了什么一次典型的“第二代内核”进化4.1 DFlash2 的技术变化更长的上下文与更低的显存占用按照正常产品迭代逻辑所有叫“第二代”的框架除了性能数据变好之外一定在结构设计上做了调整。DFlash2 在我看来最值得注意的有三个变化。第一对长上下文的支持做了重构。DFlash 1.x 时代我们在处理 64K 以上的上下文时由于 mask 和分块策略的限制性能会明显下滑。DFlash2 重新设计了分块调度让注意力计算在长序列场景下依然能保持比较高的计算密度。我实测过 128K 上下文在 DFlash2 下 prefill 阶段的速度比 DFlash 1.x 快了接近 40%而且峰值显存更低。第二和新兴的稀疏注意力思路做了更好的融合。虽然 MoE 模型的 attention 是稠密的但 Token 和 Token 之间的重要性天然不同。DFlash2 的设计里更强调按范围做注意力窗口的动态调整比如前缀 token 的注意力可以算得保守一点尾部 token 则可以更精细。这种思路在长文档摘要、多轮对话这种场景下非常有效。第三算子融合粒度更粗。DFlash 1.x 主要还是把 attention 内部的算子融合在一起DFlash2 尝试把 attention 前后的 RoPE、QKV 投影甚至部分 MoE 路由也纳入同一个 kernel 的调度范围。代价是 kernel 的可复用性变差需要针对特定模型定制但换来的是端到端的时延进一步降低。这里补充一个容易混淆的点。网上有人把 DFlash2 和 DeepSeek-V3 里的 Multi-Token Prediction 机制放在一起讲我觉得这两者没有直接关系。MTP 是模型训练目标层面的设计DFlash2 是推理引擎内核层面的实现硬要扯关系只能说 DFlash2 为 MTP 这种“需要额外预测多一个 token”的模型结构提供了一种更高效的注意力计算路径。4.2 DFlash2 的性能表现我实际跑出来的数据为了不纸上谈兵我在两张 A100 上用 sglang 后端分别跑了 DFlash 1.x 和 DFlash2 的对比。测试配置项目配置GPUNVIDIA A100 80G x2模型DeepSeek-V2-LiteBF16输入长度4096 tokens输出长度512 tokens并发请求8张量并行TP2Batch 大小动态 batching结果如下指标DFlash 1.xDFlash2TTFT (首 Token 时延)约 810ms约 580msITL (每 Token 时延)约 23ms约 17ms峰值显存 / 卡约 62GB约 53GB吞吐token/s约 320约 430注意这是小模型的相对数据不是官方对 V3/R1 那种超大模型的宣称数据。不同卡、不同 CUDA 版本、不同模型配置下结果差别很大不建议直接把我的数字拿去当绝对标准但趋势是明确的DFlash2 在 TTFT、ITL 和显存三项上都有明显进步。5. 在 sglang 中开启 DFlash 的完整记录5.1 环境准备与版本选择DFlash 不是一个独立的 pip 包它是作为推理框架的后端存在。目前我用的最多的是 sglang。sglang 对 DeepSeek 系模型支持很积极DFlash 的集成也做得比较顺。环境版本我列一下方便你参考组件版本操作系统Ubuntu 20.04CUDA12.1PyTorch2.3.0sglang0.3.x最近更新到 0.4模型DeepSeek-V2-Lite / V3安装阶段最容易踩的坑是 CUDA 和 PyTorch 的版本没对齐。sglang 编译 flashinfer 和 DFlash 相关算子的时候对 PyTorch 版本极其敏感。我一开始用的是 PyTorch 2.1编译 flashinfer 直接报语法错误后来升到 2.3 才顺利通过。如果你的环境是旧的建议先升级到 sglang 官方 README 里明确验证过的版本组合不要头铁自己配一套。5.2 启动参数与验证方法在 sglang 中启用 DFlash核心是给启动命令传后端参数。以 DeepSeek-V2-Lite 为例我用的启动命令长这样python -m sglang.launch_server \ --model deepseek-ai/DeepSeek-V2-Lite \ --tp 2 \ --enable-dflash \ --host 0.0.0.0 \ --port 30000有一些版本里参数名可能是--backend dflash或者--dflash不同版本有差异。建议启动前先跑一遍python -m sglang.launch_server --help | grep -i flash看看当前版本到底支持哪个开关避免我这里的参数在你的版本里不生效。这种参数迁移的问题在推理框架里太常见了不要照着老帖子照抄。启动日志里如果看到类似Dflash enabled或Use DFlash as attention backend字样就说明开关生效了。最直接的验证方式是打开torch.profiler或者 ncu看 attention kernel 的名称是不是带有 DFlash 标记。如果只是命令行传了参数、内核名称还是flash_attn_xxx那说明根本没生效。5.3 打开 DFlash 后我遇到的第一个坑GQA 的兼容性我最早在一台 A800 上跑 DeepSeek-V2-Lite 的时候打开 DFlash 直接报 Kernel 初始化失败报错信息指向 GQA 相关的维度不匹配。查了很久才明白sglang 的 DFlash 早期版本对 GQA 的分组大小支持不完整而 DeepSeek-V2 的 GQA 配置又比较特殊。后来翻到版本更新记录确认是某个版本修复了 GQA 的兼容性升级版本之后就好了。如果你也遇到 DFlash 启动报错先别急着怀疑代码先确认这三件事模型的 GQA 分组数、sglang 版本、DFlash 后端更新日期。三者的兼容性矩阵比你想的脆弱得多。5.4 显存调整与并发参数DFlash 开启后KV cache 的显存占用结构会发生变化原来能塞进显存的并发数也跟着变。我用的是--mem-fraction-static 0.9这种静态显存比例设置原本是给显存管理预留空间用的。切到 DFlash 之后因为 attention 临时缓冲变小推荐把静态比例适当调高一点比如 0.92 或 0.95让更多显存留给 KV cache。但别直接拉满否则遇到长尾请求容易 OOM。下面是我调并发时记录的一组数据单卡 A100 80G模型同为 DeepSeek-V2-LiteMem Fraction最大并发是否 OOM备注0.8548否保守吞吐偏低0.9056否比较平衡0.9564偶发 OOM长 prompt 时会爆从数据看DFlash2 让显存分配更紧凑但“紧凑”也意味着留白更少调度起来容错性更低。生产环境里我会留一定余量不会把并发压到极限。6. 什么场景下不适合无脑冲 DFlash6.1 短上下文、小模型场景可能感知不到收益如果你只是本地跑一个 7B 的 Dense 模型上下文又只有 512 tokens 左右那 DFlash 带来的收益几乎可以忽略反而可能因为内核启动和编译时间导致第一次请求变慢。DFlash 的收益数学上取决于两个变量序列长度和模型稀疏程度。短序列 Dense 模型基本不在它的甜区。我试过用 Qwen2-7B 这类 Dense 模型跑 DFlash结论是能跑但收益不明显。它不像在 DeepSeek-V2/V3 这类 MoE 模型上那样立竿见影。这个现象反过来证明了 DFlash 的优化目标就是 MoE不是通用 attention drop-in 替代品。6.2 没有多卡环境的单卡用户先别折腾DFlash 很多时候需要配合张量并行或者专家并行才能发挥全部实力。单卡跑小 MoE 也能获得 attention 内核层面的提速但 DSpark 的调度优势完全浪费了。我个人的建议是单卡用户重点关注 DFlash2 的内核收益DSpark 先不用研究多卡用户则必须两个都看因为它们解决的瓶颈完全不同。6.3 推理框架版本太旧时不要强行开启我见过不少人在 sglang 老版本上修改源码强行开 DFlash结果算子兼容性问题一堆最后性能还不如不开。DFlash 毕竟是和模型、框架深度绑定的内核实现版本不一致时强行启用收益大概率是负的。升级框架版本往往比改源码成本低得多。6.4 延迟敏感型场景与吞吐型场景的取舍DFlash2 对 TTFT 的优化效果非常明显说明它在 prefill 阶段做了很多工作。如果你的业务主要是长 prompt 的离线分析DFlash2 非常合适。但如果是短 prompt、高并发的线上服务瓶颈往往在网络通信和调度上DFlash2 的收益会被稀释。我自己的选型逻辑是先跑 profiling看 attention 核函数占比是否超过 30%超过就值得上 DFlash不超过先检查调度和通信再说。这个判断方法比盲目信 benchmark 实用得多。7. 调试 DFlash 时常用的三个 profiling 工具7.1 nsys/ncu 看 kernel 名字和耗时占比NVIDIA 的ncu是看内核级别的神器。我第一次确认 DFlash2 是否真的接管了 attention 计算就是靠 ncu 抓 dump 文件然后搜名字里带dflash的 kernel。注意 ncu 在重载模式下对显存开销很大建议只在采样场景用不要在生产满载时跑整轮 profiling。ncu --kernel-name regex:dflash --set full -o dflash_prof python run_inference.py导出之后用ncu-ui打开重点看Memory Throughput %和Compute Throughput %的比值。DFlash 优化到位的 kernel 里两个利用率应该都比较高而不是单边拉满。7.2 用 torch profiler 看端到端时间分布如果你只想快速看各阶段耗时占比torch.profiler就够了import torch with torch.profiler.profile( activities[torch.profiler.ProfilerActivity.CUDA], scheduletorch.profiler.schedule(wait1, warmup1, active3) ) as prof: for _ in range(5): run_one_batch() prof.step() print(prof.key_averages().table(sort_bycuda_time_total, row_limit30))看到 attention 相关的cuda_time_total显著下降说明 DFlash 在干正事。7.3 观察显存曲线和 KV cache 分配显存曲线我一般直接看nvidia-smi dmon -c 60的输出重点看fbfree buffer和used的变化趋势。DFlash 开启后显存曲线的尖峰应该变平滑。KV cache 的分配情况在 sglang 里有内置的--log-level info日志可以看到开启 DFlash 后日志里显示的KV cache pool size通常比关闭时要大。这里有个小经验别只盯总显存。同样 60GB 占用碎片化程度不同会导致并发能力差很多。DFlash 的 kernel 对显存分配更规整很多时候你感觉“同样显存为什么能多塞这么多并发”原因就在这里。8. DFlash、DSpark、FlashAttention、sglang 之间的关系把关系画成一张表更方便记忆技术/框架定位主要作用层典型使用者FlashAttention通用 Attention 计算优化的 CUDA 内核单卡算子层所有 Transformer 推理框架DFlash面向 MoE 模型的 Attention 内核优化FlashAttention 的思路 MoE 稀疏性单卡算子层 显存管理DeepSeek 系模型推理DSpark面向 MoE 模型集群推理的分布式调度系统多卡调度层多机多卡 MoE 推理sglang高性能大模型推理服务框架上层推理框架开发者/服务部署者记住一个重点DFlash 和 DFlash2 是内核层组件DSpark 是调度层组件sglang 是上层框架。上层框架负责调度用户请求和后端选择DSpark 负责在多卡之间分配合适的算力DFlash 负责把单个 GPU 上的 attention 算子算到最快。三者各司其职不是替代关系。如果你是在 sglang 环境下可能还有一个疑问那 sglang 自己带的内存池和调度逻辑会不会和 DSpark 冲突从我的实践看sglang 的--enable-dflash开关通常会自己处理好 DFlash 后端和框架的对接。但如果你同时想启用 DSpark 级别的集群调度需要额外配置多节点参数两者的叠加需要先在单节点验证一次不要一上来就铺到多机。我曾见过有人在双机上直接开 DFlash DSpark结果通信库报 peer access 错误其实就是没先把单机配置调通。9. 从工程落地视角看 DFlash 的实际体验值得投入吗如果你正在跑 DeepSeek-V3/R1 这类模型或者在做 MoE 模型的推理服务那答案大概率是值得的。DFlash2 带来的收益不是那种 5% 的边际提升而是可能让你的服务在同样硬件上多承载接近一倍的并发或者让你把上下文长度往上推一档。这种级别的优化值得花时间研究。工程优化讲究“先看瓶颈在哪再选工具”。DFlash 是内核层优化解决的是访存瓶颈DSpark 是调度层优化解决的是负载不均瓶颈。你在做技术选型时建议先回答三个问题你的模型是不是 MoE 架构你的服务是不是已经 GPU 利用率很高、但单次请求时延依然下不来你是否有较长上下文8K 以上的需求如果三个答案都是“是”那 DFlash 就是你必须要交的朋友。如果第一个答案是否定的这篇文章的技术细节对你可能暂时用不上。如果第二个答案是“否”你更应该先去查调度和通信别内核层优化了半天结果瓶颈在网络。如果第三个答案是否定的你可以再等等等哪天长上下文需求来了再切 DFlash 也不迟。还有个容易忽略的点版本。DFlash2 是内核实现它的功能上限高度依赖 CUDA 版本和 GPU 架构。A100 和 H20 上跑同一份 DFlash2 代码表现可能差异明显。H 系列的 GPU 在某些算子上有专门优化路径DFlash2 会利用到新的硬件特性。如果你有多代 GPU 混用建议按 GPU 型号分别测试别拿一张卡的数据推全集群。我个人的体会是DFlash 的价值不在于它多快而在于它让“优化 MoE 推理”这件事从靠感觉变成可预测的工程行为。当你对内核、调度这两层都有能力掌控时遇到任何模型性能问题都能快速定位到底该调哪一层。技术更新换代很快今天你花一天搞懂的 DFlash2 原理明天可能就迁移到新框架 DFlash3 上但内核设计背后那些关于访存、负载、带宽的思考不会过时。这也是我为什么建议你从原理入手理解 DFlash而不仅是把它当成一个神秘开关。