GPUStack Day 0 支持 Kimi-K3:8×B300 双引擎实测
科技
科技 > 传媒 > 正文

GPUStack Day 0 支持 Kimi-K3:8×B300 双引擎实测

Kimi-K3 是一款面向复杂推理与长上下文任务的 MoE 模型。本次测试使用 单机 8×NVIDIA B300,通过 GPUStack 分别接入 vLLM 与 SGLang,并启用 Kimi-K3-DSpark 草稿模型进行投机解码。

先给出最值得关注的结论:

(1)64K 输入、10 路并发:vLLM 端到端耗时 100.5 秒,SGLang 为 150.8 秒,vLLM 领先约 1.5×。

(2)200K 输入、10 路并发:SGLang 端到端耗时 225.3 秒,vLLM 为 295.2 秒,SGLang 反超约 1.31×。

(3)vLLM 从 64K 增长到 200K 后,稳定解码吞吐下降 3.29×;SGLang 仅下降 1.25×。

(4)性能交叉的主因是本轮 SGLang 开启了 --dcp-size=8,而 vLLM 的 decode_context_parallel_size=1。

(5)两侧 Prefill 能力接近,主要差异来自 Decode 阶段。

(6)Random 数据集使 DSPARK 投机解码的接受率极低,因此本轮结果不能直接代表真实生产流量。

重要说明: 这不是严格对等的 A/B 测试。两套引擎在 DCP、显存分配、算子后端、接受判据等方面存在差异。本文更适合用来理解瓶颈与选型方向,不应被视为最终性能排名。

1. 测试环境

本次测试环境如下:

2. 准备模型权重

使用 ModelScope 下载 Kimi-K3 主模型与 DSpark 草稿模型。

2.1 下载 Kimi-K3 主模型(MXFP4)

2.2 下载 vLLM 使用的 DSpark 草稿模型

2.3 下载 SGLang 使用的 DSpark 草稿模型

3. 拉取推理镜像

3.1 拉取 vLLM 镜像

3.2 拉取 SGLang 镜像

镜像准备完成后,在 GPUStack 的推理后端中分别为 vLLM 和 SGLang 添加自定义版本。

4. 在 GPUStack 中部署 vLLM

在 GPUStack 中创建 vLLM 自定义后端,选择 Kimi-K3 模型、8 张 B300,并配置所需环境变量。

图 1 GPUStack 中的 vLLM 模型配置

vLLM 自定义后端及模型配置如下:

图 2 vLLM 自定义后端与模型配置

4.1 vLLM 环境变量

4.2 vLLM 启动参数

--gpu-memory-utilization 0.95 在本轮测试中导致 KV 池超额提交,并触发运行期 OOM 重试。后文会给出更稳妥的调整建议。

5. 在 GPUStack 中部署 SGLang

同样创建 SGLang 自定义后端,选择 8 张 B300,并录入模型与启动参数。

图 3 GPUStack 中的 SGLang 模型配置

SGLang 自定义后端及模型配置如下:

图 4 SGLang 自定义后端与模型配置

5.1 SGLang 启动参数

服务启动后,可在 GPUStack 中查看实时日志、吞吐与 KV Cache 状态。

图 5 GPUStack 中的服务日志与运行状态

6. 64K / 3K、10 路并发对比

首先使用 64K 输入、3K 输出、10 路并发进行测试。

6.1 vLLM 测试结果

图 6 vLLM 64K / 3K、10 路并发测试结果

6.2 SGLang 测试结果

图 7 SGLang 64K / 3K、10 路并发测试结果

测试期间的推理日志如下:

图 8 vLLM 推理日志

图 9 SGLang 推理日志

7. 不同输入长度下的测试

除 64K 外,本轮还测试了多种输入长度,用于观察上下文增长对 TTFT、TPOT、ITL 与端到端耗时的影响。

7.1 vLLM

图 10 vLLM 不同输入长度测试结果

7.2 SGLang

图 11 SGLang 不同输入长度测试结果

8. 核心结果:64K 与 200K 之间发生性能交叉

从客户端可信指标与引擎日志综合看,两套引擎在 64K 和 200K 之间发生了明显的性能交叉:

一句话解释: vLLM 在较短上下文中没有 DCP 通信开销,因此更快;上下文增长后,SGLang 的 DCP=8 将每张卡需要读取的 KV 数据量大幅分摊,长上下文 Decode 反而更有优势。

但读完两边源码后,这个差异不是“vLLM 忘了开”那么简单:vLLM 在代码里明确禁止 DCP 与 DSPARK 投机解码共存,想要 DCP 就必须放弃投机;SGLang 允许二者共存,代价是投机退化为非自适应模式。也就是说,这是一道架构层的取舍题,而不是配置疏漏。

9. 为什么会发生性能交叉

9.1 Decode 扩展性是决定性因素

取 10 路请求全部进入解码后的稳定段:

图 12 64K 与 200K 稳定段解码聚合吞吐

解码聚合吞吐:10 路并发稳定段均值,单位 tok/s;数据来源为引擎自报日志。

vLLM(DCP=1)

(1)64K:稳定段约 439~537 tok/s,峰值 537 tok/s。

(2)折算单请求约 53.7 tok/s,即约 18.6 ms/token。

(3)200K:稳定段约 157.8~166.6 tok/s,均值约 163 tok/s。

(4)折算单请求约 16.3 tok/s,即约 61.3 ms/token。

(5)上下文增长约 3.1×,解码性能下降 3.29×,呈近似线性退化。

SGLang(DCP=8)

(1)64K:稳定段约 313~418 tok/s,均值约 335 tok/s。

(2)折算单请求约 33.5 tok/s,即约 29.9 ms/token。

(3)200K:稳定段约 252~288 tok/s,均值约 268 tok/s。

(4)折算单请求约 26.8 tok/s,即约 37.3 ms/token。

(5)上下文增长约 3.1×,解码性能仅下降 1.25×。

在 64K 场景中,DCP 每一步增加的通信成本还没有被 KV 读取收益抵消,所以 vLLM 更快;到了 200K,KV 读带宽成为主要瓶颈,SGLang 的 DCP 优势开始显现。

9.2 Prefill 能力基本打平

两侧的 chunked-prefill / max-num-batched-tokens 都设置为 32768,稳定 Prefill 聚合吞吐约为 19K~23K tok/s。这说明在 8×B300 上,Kimi-K3 的 MoE + MLA 预填充能力已经比较接近,整体差距主要出现在 Decode 阶段。

需要注意的是,vLLM 的 64K Prefill 数据受到两类运行时事件干扰:

(1)8 张卡共出现 32 次软 OOM 重试;

(2)4 个算子在推理期发生 JIT 编译。

这些事件都集中在 64K 测试初期,因此 vLLM 该轮的 Prefill 聚合值和首批 TTFT 偏悲观。200K 测试中未复现,SGLang 日志中也没有对应的 OOM / JIT 记录。

9.3 SGLang 日志揭示了长上下文 Prefill 衰减

SGLang 会逐 chunk 输出 input throughput。在 200K 场景中,同一请求的 6 个 chunk 吞吐依次约为:

切换到下一个请求后,吞吐又回到约 22K tok/s。这直接反映了随着已缓存上下文变长,注意力计算成本逐步上升。

vLLM 的 Avg prompt tput 是 10 秒窗口聚合值,粒度更粗,因此不会直接呈现这一结构。对比两侧日志时,需要注意可观测口径并不完全一致。

10. DSPARK 投机解码:本轮基本没有带来收益

本轮压测使用 Random 数据集。随机 token 序列本身几乎不可预测,因此草稿模型很难连续命中。

两套引擎都使用 Kimi-K3-DSpark,并设置 7 个投机 token;但在 200K 场景下,双方平均接受长度都接近 1:

(1)vLLM:约 1.05~1.17;

(2)SGLang:约 1.05~1.17。

这意味着系统为草稿模型付出了额外算力,却几乎没有获得投机解码收益。vLLM 200K 轮中,Drafted throughput 约 990 tok/s,而 Accepted throughput 仅约 21 tok/s,约 98% 的草稿计算被丢弃。

不同场景下的接受长度与接受率如下:

结论: 本轮测到的是“投机解码纯负担”场景,不能直接外推到真实生产流量。下一轮应改用 ShareGPT、真实业务请求或其他自然语言数据集。

10.1 两套引擎的接受判据也不同

两边草稿权重同源,但验证策略不同:

(1)vLLM 显式设置 draft_sample_method=probabilistic 和 rejection_sample_method=block,采用更宽松的概率式分块验证。

(2)SGLang 的 DSPARK 路径采用严格的 greedy 逐位匹配;块内第一个 token 不匹配,后续 token 会全部作废。

因此,在相同草稿模型下,两套引擎仍可能出现明显不同的平均接受长度。若要严格验证投机解码收益,需要统一数据集、采样参数、gamma 与接受判据。

11. 客户端指标:先排除不可信的吞吐列

四组客户端测试的可信指标汇总如下:

截图中的部分吞吐字段无法与总 token 数、并发和耗时相互校验。例如:

(1)vLLM 200K 显示输出吞吐 1111.61 tok/s;

(2)但按 10 × 3000 ÷ 295.21 计算,物理上限约为 101.6 tok/s;

(3)64K 测试也存在接近 11× 的固定偏差。

因此,本文只采用 TTFT、TPOT、ITL、延迟与总耗时;吞吐统一以引擎日志为准。

11.1 TTFT 口径也需要统一

启用 --reasoning-parser kimi_k3 后,思维链输出位于 delta.reasoning,而不是 delta.content。如果压测工具只从 delta.content 计算首 token,两套引擎的 TTFT 都会被系统性抬高。

对于推理模型,更合理的口径是:

即收到第一个 reasoning 或 content 增量时就停止 TTFT 计时。

此外,vLLM 该轮还出现长 Prefill 挤占 Decode 调度预算的现象:当 long_prefill_token_threshold=0 时,队首长请求可能一次吃满 32768 token 预算,导致其他已经进入运行态的请求拿不到一个 token 的 Decode 调度,从而形成较平的高 TTFT。

12. 显存分配与 KV Cache 容量

每张 B300 可见显存为 267.69 GiB,两侧模型权重占用接近,但剩余显存分配策略差异很大。

12.1 vLLM

KV 池容量为 2,687,776 token,1M 上下文下理论最大并发约为 2.69×。

但当前配置存在超额提交。vLLM 日志提示:若要真正落入 0.95 显存预算,KV Cache 应降至 38.48 GiB,而本轮实际分配了 43.76 GiB。这也是运行期 OOM 重试的主要来源。

可选择以下一种方式修正:

方案一:降低显存利用率

方案二:显式限制 KV Cache

12.2 SGLang

全注意力池容量为 916,672 token,草稿池容量为 7,333,376 token。

当前配置声明 context-length=1000000,但全注意力池只有 916,672 token,比目标上下文少约 8.3%。也就是说,单条满 1M 请求无法完整放入 KV 池。

同时,max_running_requests 从 48 被压缩到 39,瓶颈来自 Mamba 状态缓存,而非普通 KV Cache。对于 1M 长上下文高并发场景,vLLM 当前的容量余量更大。

13. 冷启动开销

两套引擎总冷启动时间都在 10 分钟左右,但耗时分布不同:

(1)vLLM 的主要耗时来自 profile、KV 分配与 warmup,约 409.6 秒;

(2)SGLang 的主要耗时来自 CUDA Graph 捕获,约 5.5 分钟。

如果服务需要频繁扩缩容,应把冷启动时间纳入容量规划,并尽量通过常驻实例、预热或分批扩容降低影响。

14. 配置并不完全对等

本轮两套技术栈的主要差异如下:

15. 日志中值得处理的告警

15.1 两边共有:FP8 KV Cache 缺少 scaling factor

两侧日志均提示 FP8 KV Cache 未提供 scaling factor,并回退到 1.0。该配置可能影响精度,而本轮没有进行输出质量对齐。

在对性能数字下最终结论前,应先使用固定评测集验证精度,确认 FP8 KV Cache 没有造成不可接受的质量损失。

15.2 两边共有:slow tokenizer

两套引擎都出现 Using a slow tokenizer 告警。对于 64K / 200K 长输入,Tokenizer 耗时会直接进入 TTFT,应优先更换或启用 fast tokenizer。

15.3 SGLang:设备名识别异常

日志中一处将设备识别为 NVIDIA L20D,但其他位置又正确识别为 B300 / GB300、SM100 / SM103。DCP 通信后端会根据设备名查表选择,建议确认 a2a 是否由错误条目触发。

同时,日志提示:

这可能在 Blackwell 平台上带来精度问题,需要进一步验证。

15.4 vLLM:显存利用率设置过高

--gpu-memory-utilization 0.95 使 KV Cache 超额提交。运行期每张卡需要额外约 4.52 GB,但当时只剩约 2.67 GB,于是触发多轮分配器清缓存与重试。

建议将显存利用率降至 0.92,或使用日志给出的 --kv-cache-memory 值显式限制。

15.5 vLLM:部分“已启用”融合可能未实际生效

日志显示 norm_quant、act_quant 与 allreduce_rms 已启用,但这些属于 Inductor 编译期 pass;Kimi-K3 自动开启 VLLM_USE_BREAKABLE_CUDAGRAPH 后,编译模式可能被强制设为 NONE,因此对应融合未必真正应用。

此外,本轮还关闭了 FlashInfer autotune。vLLM 的结果可能低于其最佳状态,复测时应重新检查实际生效的编译与算子配置。

16. 代码层根因:DCP 与投机解码在两个引擎中的取舍

这是整份分析里最硬的一条结论,而且它不是调参能绕开的。

前面 200K 场景的性能反转,表面看起来像是“vLLM 忘了开 DCP”。但源码显示并非如此:vLLM 结构性地不允许 DCP 和 DSPARK 同时开启。当 decode_context_parallel_size > 1 遇到 K3DSparkModel 时会直接抛出异常(config/speculative.py:978-986)。

因此,在 vLLM 上若要获得长上下文的 KV 分片收益,就必须整条放弃投机解码。

SGLang 可以同时开启 DCP 与 DSPARK,但同样需要付出代价:dcp_size > 1 会强制设置 SGLANG_RAGGED_VERIFY_MODE=static(overrides.py:356-362)。此时 DSPARK 的 SPS 成本表、STS 温度校准、置信度 planner 和块接受率估计器等自适应机制均不再激活,每个请求都会验证完整的 8-token 窗口,无法进行自适应裁剪。

这也解释了为什么 SGLang 能在 200K 场景中保住 DCP 的长上下文收益,但投机接受长度始终偏低。

16.1 其余几个只有读代码才能发现的点

16.1.1 SGLang 的 KDA 融合验证内核大概率没有运行

启动参数解析得到 linear_attn_verify_backend=nv_cutedsl,但 dispatcher 在赋值时直接将 verify_kernel 指向 triton_kernel(kda_backend.py:97-98)。融合 CuTe 路径只会在运行期通过 _can_run_dspark_cutedsl_mtp() 的严格形状约束后尝试。

日志最终显示 verify=TritonKDAKernel,说明实际回退到了 Triton,验证侧仍有一部分性能潜力没有释放。

16.1.2 SGLang“第二个 KV 池”的身份

日志中的 7,333,376 = 916,672 × 8,正好是 dcp_size 的倍数。它并不是第二种架构池,而是草稿 worker 的 MHA KV 池按 DCP 虚拟 token 空间寻址的结果。

草稿模型使用 trtllm_mha 稠密注意力,因此 K/V 分开记账,各占约 4.37 GB。

16.1.3 vLLM 的 1536 attention block 是由 Mamba 对齐要求产生的

混合模型要求 attention 页的字节数不小于 Mamba 页,因此 block 从 32 被提高到 1536,再通过约 0.70% 的 Mamba 页 padding 使两类页的字节数对齐(platforms/interface.py:890-940)。

代价是约 0.7% 的字节浪费和更粗的前缀缓存粒度。这个 1536 边界又会反馈给 align 模式的分块切分,间接参与前文提到的 Decode 饿死问题。

16.1.4 vLLM 2.69× 并发上限的来源

限制并不是 KV 总量不足,而是 scheduler_reserve_full_isl=True 默认要求在请求准入时就按完整最大序列长度预留 KV。

如果将 --max-model-len 从 1M 调整为实际业务长度,并发能力会立即提高。

17. 选型建议

17.1 输入不超过 64K,吞吐优先

优先选择 vLLM。

本轮 64K 场景中,vLLM 解码吞吐约领先 1.5~1.6×,端到端耗时领先约 1.5×。在较短上下文中,避免 DCP 通信开销更有利。

17.2 200K 及以上长文服务

优先考虑 SGLang + DCP。

本轮 200K 场景中,SGLang 解码约领先 1.64×,并且随上下文增长的性能退化明显更缓。不过上线前必须先将 KV 池提高到不低于目标 context-length。

它也是当前两侧中唯一可以同时保留 DCP 与投机解码的一方,但开启 DCP 后,DSPARK 会退化为非自适应验证模式。

17.3 长上下文 + 高并发

当前配置下更倾向 vLLM,但需要复测。

vLLM 在 1M 上下文下理论 KV 容量约为 2.69 路,而 SGLang 当前全注意力池不足 1M,且 Mamba 状态缓存将最大运行请求数压缩到 39。

如果 SGLang 调整 KV / Mamba 内存比例后,再与“关闭投机、开启 DCP”的 vLLM 进行对照,结论可能发生变化。

18. 下一轮如何做严格复测

新版源码分析显示,vLLM 无法同时开启 DCP 与 DSPARK,因此不能简单地“给 vLLM 也开 DCP”来进行二方对齐。更合理的方式是增加 SGLang 关闭 DCP 的第三组,并同时优化两侧各自可修复的问题。

19. 总结

这次 8×B300 单机测试最有价值的地方,不是简单得出“谁更快”,而是清楚展示了上下文长度如何改变推理瓶颈:

(1)64K 阶段,通信开销更敏感,vLLM 更有优势;

(2)200K 阶段,KV 读带宽成为核心瓶颈,SGLang + DCP 反超;

(3)Prefill 不是主要差距来源,Decode 扩展性才是关键;

(4)投机解码在 Random 数据集下几乎完全失效;

(5)显存分配、TTFT 口径和客户端吞吐统计都会显著影响结论。

因此,生产选型不能只看单一 TPS。上下文长度、并发规模、KV 容量、DCP 配置、真实请求分布与输出质量,必须放在同一套评测体系中综合判断。

About GPUStack

GPUStack 是由 GPUStack.ai 所推出的开源项目。GPUStack.ai 成立于2022年,是 AI Infra 解决方案提供商,目前已完成5300万元种子轮融资。创始团队成员均来自业界应用广泛的 Kubernetes 管理平台 Rancher 的核心团队。其中,联合创始人及 CTO 梁胜博士是前 SUSE 全球工程及创新总裁,加入 SUSE 之前,梁胜博士于2014年9月创立全球著名的容器管理平台公司 Rancher Labs 并担任 CEO。