[Docs] unify phase experiment archive naming

This commit is contained in:
Zhiyi Hong 2026-07-31 17:00:22 +08:00
parent 5f24b7d22f
commit 7be3062c51
11 changed files with 1695 additions and 26 deletions

View File

@ -1,5 +1,9 @@
# sskj — 多平台大模型推理性能基准测试项目
> **更新2026-07-31 16:46:05 CST**
>
> 统一 DeepSeek-V4-Pro 推理优化档案命名与导航Phase 1/2 实验页分别更名为 `phase1_exp.html``phase2_exp.html`,代码页保持 `phase1_code.html``phase2_code.html`。主计划中的入口统一为“打开 Phase N 实验档案 / 代码详解”并为实验页与代码页补齐双向链接。Phase 1 状态同步为固定点 11/11、混合 A/B 3/3、阶段总结果 14/14。
>
> **更新2026-07-31 16:27:58 CST**
>
> 完成 Phase 2 Worker 通信代码分发的双节点真机 smoke test。Run `dsv4pro-phase2-stage-smoke-20260731-162326` 依次通过 Head/Worker P2P、两组单机 8-rank AllReduce 和一组双机 16-rank AllReduce全部 `wrong_values=0`。两节点暂存文件 SHA256 一致;结束后 `/tmp` 暂存、通信容器和 GPU 进程均已清理。本次使用 1 MiB、单次迭代仅验证执行链路不作为正式性能数据。

View File

@ -134,6 +134,10 @@
</header>
<main>
<p>
<a href="./推理优化计划.html">返回推理优化主计划</a> ·
<a href="./phase1_exp.html">打开 Phase 1 实验档案</a>
</p>
<div class="callout">
<strong>文档边界:</strong>这是一份独立代码档案,只解释 Phase 1 实现,不承担阶段结论展示。
下文的行号均绑定提交 <code>ca1f2f63375c</code>。代码变更后应先更新基线提交,再重新核对行号。

View File

@ -234,19 +234,23 @@
<div class="meta">
<span>节点174.1.51.5 + 174.1.51.7</span>
<span>拓扑SGLang TP16 / EP2</span>
<span>更新2026-07-30 22:55:37 CST</span>
<span>更新2026-07-31 16:46:05 CST</span>
</div>
</div>
</header>
<main>
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
<a class="back" href="./phase1_code.html">打开 Phase 1 代码详解</a>
<p class="status">
<strong>阶段状态:已完成。</strong>
正式 Run <code>dsv4pro-phase1-full-20260730-220916</code> 在双 Rail
<code>NET/IB + GDRDMA</code> 下完成 9 个固定点和 3 个混合 A/B 结果,
共 12/12 成功,用时 28 分 36 秒;服务、容器与 16 张 GPU 已清理。
共 12/12 成功,用时 28 分 36 秒。补充 Run
<code>dsv4pro-phase1-long-decode-20260730-234236</code> 完成长输出与
长上下文 Decode 2/2。两个 Run 合计 11 个固定点和 3 个混合结果,
14/14 成功;服务、容器与 16 张 GPU 已清理。
</p>
<h2>1. 目标与边界</h2>
@ -287,7 +291,7 @@ dsv4pro_pro6000d_2node_sglang_tp16_quick_map/</code></pre>
</tr>
<tr>
<td><code>quick_map_scenarios.tsv</code></td>
<td>个固定工作负载点</td>
<td>十一个固定工作负载点</td>
</tr>
<tr>
<td><code>quick_map_results.py</code></td>
@ -392,13 +396,16 @@ bash run_quick_map.sh stop</code></pre>
<tr><td><code>decode_throughput_1k_to_1k_c16</code></td><td>1K</td><td>1K</td><td>16</td><td>Decode 吞吐</td></tr>
<tr><td><code>decode_throughput_1k_to_1k_c32</code></td><td>1K</td><td>1K</td><td>32</td><td>Decode 吞吐</td></tr>
<tr><td><code>decode_throughput_1k_to_1k_c64</code></td><td>1K</td><td>1K</td><td>64</td><td>Decode 高并发</td></tr>
<tr><td><code>long_output_decode_1k_to_4k_c16</code></td><td>1K</td><td>4K</td><td>16</td><td>持续 Decode 与 KV 增长</td></tr>
<tr><td><code>long_context_decode_128k_to_1k_c1</code></td><td>128K</td><td>1K</td><td>1</td><td>长上下文上的 Decode 成本</td></tr>
<tr><td><code>balanced_32k_to_1k_c8</code></td><td>32K</td><td>1K</td><td>8</td><td>综合压力</td></tr>
</tbody>
</table>
<p>
快速 Run 使用一次重复和一波测量请求,即 <code>num_prompts=C</code>
32K/128K Prefill 不做昂贵的同形状 Warm-up短 Prefill 与 Decode 使用一个
Warm-up并在正式计时前清空 Prefix Cache。固定矩阵不做 SLO 截断或自适应并发搜索。
32K/128K Prefill 与 128K 长上下文 Decode 不做昂贵的同形状 Warm-up
短 Prefill、普通 Decode 与 1K → 4K 长输出 Decode 使用一个 Warm-up
并在正式计时前清空 Prefix Cache。固定矩阵不做 SLO 截断或自适应并发搜索。
</p>
<h2>5. SGLang Benchmark 与 Prefix Cache</h2>
@ -534,13 +541,14 @@ wait "${background_pid}"</code></pre>
<tbody>
<tr><td>Shell 语法</td><td class="pass">通过</td><td><code>bash -n run_quick_map.sh</code></td></tr>
<tr><td>Python 单测</td><td class="pass">3/3 通过</td><td>场景唯一性、百分位回退、失败结果汇总</td></tr>
<tr><td>完整 Dry-run</td><td class="pass">通过</td><td>服务、个固定点、混合 A/B、清理均展开成功</td></tr>
<tr><td>完整 Dry-run</td><td class="pass">通过</td><td>服务、十一个固定点、混合 A/B、清理均展开成功</td></tr>
<tr><td>真实旧 Bench JSON 解析</td><td class="pass">通过</td><td>成功解析 P50/P95/P99 与吞吐字段</td></tr>
<tr><td>项目精简</td><td class="pass">通过</td><td>实验目录顶层仅保留一个 Shell 入口</td></tr>
<tr><td>双 Rail 传输门禁</td><td class="pass">通过</td><td>Head 与 Worker 均识别 <code>mlx5_0/mlx5_3</code>,跨节点 Channel 使用 <code>NET/IB/*/GDRDMA</code></td></tr>
<tr><td>四点 Sanity</td><td class="pass">4/4 通过</td><td>1K/32K Prefill 与 C1/C32 Decode 均恢复到合理量级</td></tr>
<tr><td>冷 Prefix 口径</td><td class="pass">通过</td><td>正式测量请求的 Head 日志显示 <code>#cached-token: 0</code></td></tr>
<tr><td>完整真机 Run</td><td class="pass">12/12 通过</td><td>固定矩阵 9/9混合 A/B 3/3运行期失败 0</td></tr>
<tr><td>长 Decode 补测</td><td class="pass">2/2 通过</td><td>1K → 4K C16 与 128K → 1K C1 均生成完整目标 OSL</td></tr>
<tr><td>资源清理</td><td class="pass">通过</td><td>两节点相关容器与计算进程为 016 张 GPU 显存占用为 0</td></tr>
</tbody>
</table>
@ -560,7 +568,9 @@ wait "${background_pid}"</code></pre>
<tr><td>Run ID</td><td><code>dsv4pro-phase1-full-20260730-220916</code></td><td><code>COMPLETED</code></td></tr>
<tr><td>运行时间</td><td>28 分 36 秒</td><td>22:09:47 至 22:38:22 CST</td></tr>
<tr><td>网络路径</td><td>双 Rail <code>NET/IB + GDRDMA</code></td><td><code>mlx5_0</code><code>mlx5_3</code></td></tr>
<tr><td>结果完整性</td><td>12/12 成功</td><td>固定点 9/9混合 A/B 3/3</td></tr>
<tr><td>正式 Run 完整性</td><td>12/12 成功</td><td>固定点 9/9混合 A/B 3/3</td></tr>
<tr><td>补充 Run</td><td><code>dsv4pro-phase1-long-decode-20260730-234236</code></td><td>固定点 2/2约 12 分钟含服务启动与清理</td></tr>
<tr><td>阶段合计</td><td>14/14 成功</td><td>固定点 11/11混合 A/B 3/3</td></tr>
</tbody>
</table>
@ -596,7 +606,30 @@ wait "${background_pid}"</code></pre>
说明 Prefill 与 Decode 同时存在时干扰很强。
</p>
<h3>9.4 混合 Prefill/Decode A/B</h3>
<h3>9.4 长 Decode 补测</h3>
<table>
<thead>
<tr><th>场景</th><th>Output TPS</th><th>TTFT P95</th><th>TPOT P95</th><th>E2E P95</th></tr>
</thead>
<tbody>
<tr><td>1K → 4KC=16</td><td>310.02 tok/s</td><td>6.241 s</td><td>50.33 ms</td><td>211.364 s</td></tr>
<tr><td>128K → 1KC=1</td><td>12.43 tok/s</td><td>49.326 s</td><td>32.24 ms</td><td>82.312 s</td></tr>
</tbody>
</table>
<p>
<code>1K → 4KC=16</code> 相比 <code>1K → 1KC=16</code>
Output TPS 增加 4.99%TPOT P95 只增加 0.62%。较长 Decode 没有出现
稳态吞吐塌陷;吞吐略升是固定启动和 Prefill 成本被更多输出 token 摊薄。
</p>
<p>
<code>128K → 1KC=1</code> 的 TTFT 只比 <code>128K → 1</code>
纯 Prefill 高 2.03%,而 TPOT P95 只比 <code>1K → 1KC=1</code>
高 2.47%。因此这次长上下文请求的主要新增成本在 Prefill而不是每个 Decode
token。表中的 12.43 Output TPS 是把 49 秒 Prefill 也计入总时长的端到端值,
不能把它误读为纯 Decode 速率。
</p>
<h3>9.5 混合 Prefill/Decode A/B</h3>
<table>
<thead>
<tr><th>指标</th><th>A仅 Decode</th><th>B注入 128K Prefill</th><th>变化</th></tr>
@ -609,16 +642,23 @@ wait "${background_pid}"</code></pre>
</tbody>
</table>
<p class="decision">
Phase 2 在上述长 Prefill、并发 Prefill和混合 A/B 之外,还会采集普通 Decode、
长输出 Decode 与长上下文 Decode。目标是区分计算、显存带宽、调度排队、
跨机通信、KV 增长和节点不均衡。
Phase 2 重放五个固定代表点:
<code>128K → 1C=1</code><code>32K → 1C=16</code>
<code>1K → 1KC=32</code><code>1K → 4KC=16</code>
<code>128K → 1KC=1</code>,再执行有无 128K 注入的混合 A/B。
目标是区分计算、显存带宽、调度排队、跨机通信和节点不均衡。
</p>
<p>
完整产物:
<a href="./results/dsv4pro-phase1-full-20260730-220916/report.md">报告</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/summary.csv">逐点汇总</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/aggregate.csv">聚合表</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/run_manifest.json">运行清单</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/run_manifest.json">运行清单</a>
长 Decode 补测的
<a href="./results/dsv4pro-phase1-long-decode-20260730-234236/report.md">报告</a>
<a href="./results/dsv4pro-phase1-long-decode-20260730-234236/summary.csv">逐点汇总</a>
<a href="./results/dsv4pro-phase1-long-decode-20260730-234236/run_manifest.json">运行清单</a>
</p>
<h2>10. 经验教训</h2>
@ -635,7 +675,15 @@ wait "${background_pid}"</code></pre>
不作为本阶段最终结果。
</p>
<h2>11. 运行命令</h2>
<h2>11. 实验复现命令</h2>
<p class="decision">
本节记录的是本阶段<strong>实际执行过</strong>的命令。长命令同时由程序原样保存到
<code>results/&lt;RUN_ID&gt;/server/*_server_cmd.txt</code> 和每个 Case 的
<code>bench_cmd.txt</code>;这些落盘文件是最终证据,正文中的换行仅用于阅读。
</p>
<h3>11.1 实际执行:完整 Phase 1</h3>
<p><strong>执行位置:</strong><code>174.1.51.5</code>;脚本通过 SSH 启动 <code>.7</code> Worker。</p>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
tmux new-session -d -s dsv4pro-phase1-full \
@ -643,11 +691,162 @@ tmux new-session -d -s dsv4pro-phase1-full \
2&gt;&amp;1 | tee /data/hzy/dsv4pro_phase1_full_20260730-220916.log"
tmux attach -t dsv4pro-phase1-full</code></pre>
<p>
<code>all</code> 的真实顺序是:
<code>Worker 启动 → Head 启动 → /health → NET/IB 门禁 → fixed → mixed → stop → summarize</code>
</p>
<h3>11.2 实际执行Worker 服务</h3>
<details>
<summary>展开 174.1.51.7 的完整 docker run</summary>
<pre><code class="language-bash">docker run -d \
--name dsv4pro_pro6000d_2node_sglang_tp16_quick_map_worker \
--gpus all \
--network host \
--ipc host \
--shm-size 20g \
--ulimit memlock=-1 \
--ulimit stack=67108864 \
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
-v /data/hzy/sglang_cache/dsv4_pro_tp16:/root/.cache \
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
-e NCCL_SOCKET_IFNAME=eth0 \
-e 'NCCL_IB_HCA==mlx5_0:1,mlx5_3:1' \
-e NCCL_CROSS_NIC=1 \
-e NCCL_DEBUG=INFO \
-e SGLANG_SHARED_EXPERT_TP1=1 \
--device /dev/infiniband/rdma_cm \
--device /dev/infiniband/uverbs0 \
--device /dev/infiniband/uverbs3 \
--entrypoint python3 \
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
-m sglang.launch_server \
--model-path /data/hf_models/DeepSeek-V4-Pro \
--tp-size 16 \
--ep-size 2 \
--nnodes 2 \
--node-rank 1 \
--dist-init-addr 10.101.0.11:20002 \
--trust-remote-code \
--host 0.0.0.0 \
--port 30002 \
--mem-fraction-static 0.9 \
--cuda-graph-max-bs-decode 64 \
--max-running-requests 256</code></pre>
</details>
<h3>11.3 实际执行Head 服务</h3>
<details>
<summary>展开 174.1.51.5 的完整 docker run</summary>
<pre><code class="language-bash">docker run -d \
--name dsv4pro_pro6000d_2node_sglang_tp16_quick_map_head \
--gpus all \
--network host \
--ipc host \
--shm-size 20g \
--ulimit memlock=-1 \
--ulimit stack=67108864 \
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
-v /data/hzy/sglang_cache/dsv4_pro_tp16:/root/.cache \
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
-e NCCL_SOCKET_IFNAME=eth0 \
-e 'NCCL_IB_HCA==mlx5_0:1,mlx5_3:1' \
-e NCCL_CROSS_NIC=1 \
-e NCCL_DEBUG=INFO \
-e SGLANG_SHARED_EXPERT_TP1=1 \
--device /dev/infiniband/rdma_cm \
--device /dev/infiniband/uverbs0 \
--device /dev/infiniband/uverbs3 \
--entrypoint python3 \
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
-m sglang.launch_server \
--model-path /data/hf_models/DeepSeek-V4-Pro \
--tp-size 16 \
--ep-size 2 \
--nnodes 2 \
--node-rank 0 \
--dist-init-addr 10.101.0.11:20002 \
--trust-remote-code \
--host 0.0.0.0 \
--port 30002 \
--mem-fraction-static 0.9 \
--cuda-graph-max-bs-decode 64 \
--max-running-requests 256</code></pre>
</details>
<p>
<code>NCCL_IB_HCA==...</code> 的两个等号不是笔误:第一个是环境变量赋值分隔符,
第二个是 NCCL HCA 列表的“精确匹配”前缀。
</p>
<h3>11.4 实际执行:代表 Benchmark</h3>
<p>以下是正式 Run 的 <code>128K → 1, C=1</code> 冷 Prefix 命令:</p>
<details>
<summary>展开完整 sglang.benchmark.serving 命令</summary>
<pre><code class="language-bash">timeout --signal=TERM --kill-after=30s 7200s \
docker run --rm \
--network host \
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
-v /data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json:/data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json:ro \
-v /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1:/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1 \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
--entrypoint python3 \
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
-m sglang.benchmark.serving \
--backend sglang \
--host 10.101.0.11 \
--port 30002 \
--dataset-name random \
--dataset-path /data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json \
--random-input-len 131072 \
--random-output-len 1 \
--random-range-ratio 1.0 \
--num-prompts 1 \
--max-concurrency 1 \
--request-rate 10000 \
--output-file /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1/bench.jsonl \
--output-details \
--disable-tqdm \
--warmup-requests 0 \
--seed 42 \
--flush-cache</code></pre>
</details>
<p>
其余固定点使用同一命令模板,只替换 ISL、OSL、并发、请求数、Warm-up、Seed
和输出目录。每个点的最终展开命令保存在自己的 <code>bench_cmd.txt</code>
混合 A/B 的并行启动顺序和两条请求命令见第 6 节及相应 Case 目录。
</p>
<h3>11.5 实际执行:长 Decode 补测</h3>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
export CASE_IDS="long_output_decode_1k_to_4k_c16,long_context_decode_128k_to_1k_c1"
export RUN_ID="dsv4pro-phase1-long-decode-20260730-234236"
trap 'bash run_quick_map.sh stop' EXIT INT TERM
bash run_quick_map.sh start
bash run_quick_map.sh fixed</code></pre>
<h3>11.6 停止、清理与检查</h3>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
bash run_quick_map.sh stop
curl -fsS http://10.101.0.11:30002/health || true
docker ps --filter name=dsv4pro_pro6000d_2node_sglang_tp16_quick_map
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv</code></pre>
<p>
下一阶段:
<a class="back" href="./phase2_dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution.html">
Phase 2硬件与资源竞争归因
<a class="back" href="./phase2_exp.html">
打开 Phase 2 实验档案
</a>
</p>
<p><a class="back" href="./推理优化计划.html">返回推理优化主计划</a></p>

View File

@ -113,6 +113,10 @@
</header>
<main>
<p>
<a href="./推理优化计划.html">返回推理优化主计划</a> ·
<a href="./phase2_exp.html">打开 Phase 2 实验档案</a>
</p>
<div class="callout">
<strong>文档边界:</strong>本文只解释提交 <code>39fc2ba565a3</code> 的 Phase 2
代码和文件调用关系。Phase 1 负责模型服务与请求Phase 2 负责通信基线、

View File

@ -135,7 +135,7 @@
<div class="meta">
<span>节点174.1.51.5 + 174.1.51.7</span>
<span>拓扑SGLang TP16 / EP2</span>
<span>更新2026-07-31 15:25:00 CST</span>
<span>更新2026-07-31 16:46:05 CST</span>
</div>
</div>
</header>
@ -762,7 +762,7 @@ tmux new-session -d -s dsv4pro-phase2 \
<li>完整归因见 <a href="./results/dsv4pro-phase2-20260731-130125/analysis.md"><code>analysis.md</code></a>,原始结构化摘要和两端 NCCL 证据已一并归档。</li>
</ul>
<p><a class="back" href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">返回 Phase 1 实施记录</a></p>
<p><a class="back" href="./phase1_exp.html">返回 Phase 1 实验档案</a></p>
<p><a class="back" href="./推理优化计划.html">返回推理优化主计划</a></p>
</main>
</body>

View File

@ -0,0 +1,12 @@
# DeepSeek-V4-Pro Pro6000D Two-Node SGLang Quick Map
Profiler: disabled. Speculative decoding: disabled.
## Aggregate results
| Case | Suite / role | Stage | ISL | OSL | C | Reps | Total TPS | CV | Output TPS | TTFT P95 | TPOT P95 | E2E P95 | Status |
|---|---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---|
| long_context_decode_128k_to_1k_c1 | fixed / - | long_context_decode | 131072 | 1024 | 1 | 1/1 | 1604.09 | -% | 12.43 | 49325.72 ms | 32.24 ms | 82312.21 ms | COMPLETED |
| long_output_decode_1k_to_4k_c16 | fixed / - | long_output_decode | 1024 | 4096 | 16 | 1/1 | 387.52 | -% | 310.02 | 6241.01 ms | 50.33 ms | 211363.56 ms | COMPLETED |
The fixed map is descriptive and never stops on SLO. With one repetition, CV is intentionally unavailable.

View File

@ -0,0 +1,42 @@
{
"schema_version": 1,
"workflow_stage": "quick_performance_map",
"run_id": "dsv4pro-phase1-long-decode-20260730-234236",
"status": "COMPLETED",
"started_at": "2026-07-30T23:48:13+08:00",
"updated_at": "2026-07-30T23:54:25+08:00",
"suites": [
"fixed"
],
"engine": "sglang",
"model_name": "DeepSeek-V4-Pro",
"model_path": "/data/hf_models/DeepSeek-V4-Pro",
"docker_image": "lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45",
"head_node": "10.101.0.11",
"worker_node": "10.101.0.13",
"head_ip": "10.101.0.11",
"sglang_port": 30002,
"dist_init_port": 20002,
"tp_size": 16,
"ep_size": 2,
"nnodes": 2,
"mem_fraction_static": 0.9,
"cuda_graph_max_bs_decode": 64,
"max_running_requests": 256,
"nccl_socket_ifname": "eth0",
"nccl_ib_hca": "=mlx5_0:1,mlx5_3:1",
"nccl_cross_nic": "1",
"enable_rdma": true,
"require_nccl_ib": true,
"rdma_device_paths": "/dev/infiniband/rdma_cm,/dev/infiniband/uverbs0,/dev/infiniband/uverbs3",
"git_commit": "06b017483cb1cfc6aace3c60e94576fb667ec9bc",
"git_dirty": false,
"scenario_file": "/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/quick_map_scenarios.tsv",
"case_ids": "long_output_decode_1k_to_4k_c16,long_context_decode_128k_to_1k_c1",
"notes": [
"The fixed quick map does not stop on SLO.",
"Profiler is disabled; these results are eligible for performance comparison.",
"Speculative decoding is not enabled."
],
"ended_at": "2026-07-30T23:54:25+08:00"
}

View File

@ -0,0 +1,3 @@
run_id,suite,case_id,role,stage,repetition,isl,osl,concurrency,num_prompts,warmup_requests,status,error_type,exit_code,started_at,ended_at,elapsed_s,completed,failed,duration_s,actual_concurrency,peak_concurrent_requests,total_input_tokens,total_output_tokens,request_throughput,input_token_throughput,output_token_throughput,total_token_throughput,peak_output_token_throughput,e2e_mean_ms,e2e_p50_ms,e2e_p95_ms,e2e_p99_ms,ttft_mean_ms,ttft_p50_ms,ttft_p95_ms,ttft_p99_ms,tpot_mean_ms,tpot_p50_ms,tpot_p95_ms,tpot_p99_ms,itl_mean_ms,itl_p50_ms,itl_p95_ms,itl_p99_ms,bench_file,bench_log
dsv4pro-phase1-long-decode-20260730-234236,fixed,long_context_decode_128k_to_1k_c1,,long_context_decode,1,131072,1024,1,1,0,COMPLETED,,0,2026-07-30T23:52:24+0800,2026-07-30T23:54:19+0800,115.0,1,0,82.34937581099803,0.9995487118435447,,131072,1024,0.012143382875118852,1591.6574802075781,12.434824064121704,1604.0923042716997,,82312.21251300303,82312.21251300303,82312.21251300303,82312.21251300303,49325.72139299009,49325.72139299009,49325.72139299009,49325.72139299009,32.244859354851364,32.244859354851364,32.244859354851364,32.244859354851364,32.2448224535841,32.23047900246456,32.468517863890156,32.707680857274674,/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_context_decode_128k_to_1k_c1/rep1/bench.jsonl,/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_context_decode_128k_to_1k_c1/rep1/bench.log
dsv4pro-phase1-long-decode-20260730-234236,fixed,long_output_decode_1k_to_4k_c16,,long_output_decode,1,1024,4096,16,16,1,COMPLETED,,0,2026-07-30T23:48:13+0800,2026-07-30T23:52:19+0800,246.0,16,0,211.3935806370573,15.996984262440824,,16384,65536,0.07568820184502423,77.50471868930481,310.01887475721924,387.52359344652405,,211353.73641450133,211353.21495501557,211363.55966723931,211364.98273107863,5862.642711690569,5970.386928500375,6241.009955512709,6241.542907894473,50.18097526320165,50.15488793663095,50.32745551037411,50.57979576238554,50.18097181628469,50.06324249552563,50.968476399430074,52.59070861677173,/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_output_decode_1k_to_4k_c16/rep1/bench.jsonl,/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_output_decode_1k_to_4k_c16/rep1/bench.log
1 run_id suite case_id role stage repetition isl osl concurrency num_prompts warmup_requests status error_type exit_code started_at ended_at elapsed_s completed failed duration_s actual_concurrency peak_concurrent_requests total_input_tokens total_output_tokens request_throughput input_token_throughput output_token_throughput total_token_throughput peak_output_token_throughput e2e_mean_ms e2e_p50_ms e2e_p95_ms e2e_p99_ms ttft_mean_ms ttft_p50_ms ttft_p95_ms ttft_p99_ms tpot_mean_ms tpot_p50_ms tpot_p95_ms tpot_p99_ms itl_mean_ms itl_p50_ms itl_p95_ms itl_p99_ms bench_file bench_log
2 dsv4pro-phase1-long-decode-20260730-234236 fixed long_context_decode_128k_to_1k_c1 long_context_decode 1 131072 1024 1 1 0 COMPLETED 0 2026-07-30T23:52:24+0800 2026-07-30T23:54:19+0800 115.0 1 0 82.34937581099803 0.9995487118435447 131072 1024 0.012143382875118852 1591.6574802075781 12.434824064121704 1604.0923042716997 82312.21251300303 82312.21251300303 82312.21251300303 82312.21251300303 49325.72139299009 49325.72139299009 49325.72139299009 49325.72139299009 32.244859354851364 32.244859354851364 32.244859354851364 32.244859354851364 32.2448224535841 32.23047900246456 32.468517863890156 32.707680857274674 /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_context_decode_128k_to_1k_c1/rep1/bench.jsonl /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_context_decode_128k_to_1k_c1/rep1/bench.log
3 dsv4pro-phase1-long-decode-20260730-234236 fixed long_output_decode_1k_to_4k_c16 long_output_decode 1 1024 4096 16 16 1 COMPLETED 0 2026-07-30T23:48:13+0800 2026-07-30T23:52:19+0800 246.0 16 0 211.3935806370573 15.996984262440824 16384 65536 0.07568820184502423 77.50471868930481 310.01887475721924 387.52359344652405 211353.73641450133 211353.21495501557 211363.55966723931 211364.98273107863 5862.642711690569 5970.386928500375 6241.009955512709 6241.542907894473 50.18097526320165 50.15488793663095 50.32745551037411 50.57979576238554 50.18097181628469 50.06324249552563 50.968476399430074 52.59070861677173 /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_output_decode_1k_to_4k_c16/rep1/bench.jsonl /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-long-decode-20260730-234236/cases/long_output_decode_1k_to_4k_c16/rep1/bench.log

View File

@ -415,7 +415,7 @@
<article id="document-content">
<h1>6000D 双机 DeepSeek-V4-Pro 推理优化计划</h1>
<blockquote>
<p>适用环境:<code>174.1.51.5 + 174.1.51.7</code>,每台 8 张 RTX PRO 6000 Blackwell Server Edition<br>当前部署DeepSeek-V4-Pro16 张 GPU 组成一个完整实例<br>当前约束:模型暂时只能使用全部 16 张 GPU无法额外复制一套模型进行 PD 分离<br>计划版本2026-07-31 15:25:00 CST</p>
<p>适用环境:<code>174.1.51.5 + 174.1.51.7</code>,每台 8 张 RTX PRO 6000 Blackwell Server Edition<br>当前部署DeepSeek-V4-Pro16 张 GPU 组成一个完整实例<br>当前约束:模型暂时只能使用全部 16 张 GPU无法额外复制一套模型进行 PD 分离<br>计划版本2026-07-31 16:46:05 CST</p>
</blockquote>
<h2>当前执行状态与阶段档案</h2>
<table>
@ -434,14 +434,17 @@
</tr>
<tr>
<td>DeepSeek-V4-Pro / 双机 Pro6000D / SGLang TP16 快速性能地图</td>
<td>已完成;双 Rail NET/IB + GDRDMA固定点 9/9、混合 A/B 3/3总用时 28 分 36 秒</td>
<td><a href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">打开实施记录</a></td>
<td>已完成;双 Rail NET/IB + GDRDMA固定点 11/11、混合 A/B 3/3阶段结果 14/14</td>
<td>
<a href="./phase1_exp.html">打开 Phase 1 实验档案</a><br>
<a href="./phase1_code.html">打开 Phase 1 代码详解</a>
</td>
</tr>
<tr>
<td>DeepSeek-V4-Pro / 双机 Pro6000D / SGLang 硬件与资源竞争归因</td>
<td>最终采集代码已完成;首轮 8/8 成功,精确窗口、双节点 DCGM、PCIe/NCCL 基线和逐指标报告等待最终复跑</td>
<td>最终采集代码已完成;首轮 8/8 成功,Worker 分发修复与双节点通信 smoke test 已通过,等待最终正式复跑</td>
<td>
<a href="./phase2_dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution.html">打开 Phase 2 档案</a><br>
<a href="./phase2_exp.html">打开 Phase 2 实验档案</a><br>
<a href="./phase2_code.html">打开 Phase 2 代码详解</a>
</td>
</tr>
@ -604,7 +607,7 @@ Nsight Compute 或专项 Microbenchmark
</ul>
<h2>5. Phase 1建立阶段化性能地图</h2>
<p>本阶段当前实现与结果维护在
<a href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">Phase 1双机 SGLang TP16 快速性能地图实施记录</a>
<a href="./phase1_exp.html">Phase 1双机 SGLang TP16 快速性能地图实验档案</a>
代码采用单一 Shell 入口 <code>run_quick_map.sh</code>,不修改或调用旧的全天全量脚本。</p>
<p>
最终 Run <code>dsv4pro-phase1-full-20260730-220916</code> 已完成:
@ -735,12 +738,14 @@ TPOT P95 增加 66.55%。Phase 2 将围绕这两个现象采集硬件时间序
<h2>6. Phase 2同步采集轻量硬件指标</h2>
<p>
本阶段的设计、代码改动与结果同步维护在
<a href="./phase2_dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution.html">
Phase 2硬件与资源竞争归因档案</a>。本阶段重放长 Prefill、并发 Prefill、
<a href="./phase2_exp.html">
Phase 2硬件与资源竞争归因实验档案</a>。本阶段重放长 Prefill、并发 Prefill、
普通 Decode、长输出 Decode、长上下文 Decode以及
<code>1K → 1K, C=32</code> 的混合 A/B。首轮 Run
<code>dsv4pro-phase2-20260731-130125</code> 在 26 分 26 秒内完成 8/8 个结果;
最终采集代码固定到提交 <code>30664faa41f8</code>,完成最终复跑和汇报后才进入 Phase 3。
最终采集代码在提交 <code>39fc2ba565a3</code> 修复 Worker 分发,并由
Run <code>dsv4pro-phase2-stage-smoke-20260731-162326</code> 完成双节点通信功能验证;
完成最终正式复跑和汇报后才进入 Phase 3。
</p>
<h3>6.1 首轮归因结果</h3>
<ul>

View File

@ -0,0 +1,737 @@
# 6000D 双机 DeepSeek-V4-Pro 推理优化计划
> 适用环境:`174.1.51.5 + 174.1.51.7`,每台 8 张 RTX PRO 6000 Blackwell Server Edition
> 当前部署DeepSeek-V4-Pro16 张 GPU 组成一个完整实例
> 当前约束:模型暂时只能使用全部 16 张 GPU无法额外复制一套模型进行 PD 分离
> 计划版本2026-07-31 12:26:00 CST
## 阶段档案与实验命令规范
每个 Phase HTML 的正文只保留最终成功实验、有效结果和结论;失败尝试压缩到
末尾的经验教训。阶段完成时删除“暂不能下结论”等过渡内容。
每个 Phase HTML 必须包含“实验复现命令”,并明确区分:
| 标签 | 含义 | 必须记录的内容 |
|---|---|---|
| 实际执行命令 | 该次有效 Run 真正运行过 | 执行节点、工作目录、tmux/入口、完整 Docker 服务命令、Benchmark/Profile/监控命令、停止清理命令、Run ID 与命令证据路径 |
| 复现命令 | 根据实际 Run 整理,可重新执行 | 与实际参数等价;允许为可读性换行,但不得省略影响结果的参数 |
| 计划或示例命令 | 尚未在当前阶段运行 | 必须显式标注“未执行”,真机完成后替换为实际命令,不能作为结果证据 |
长命令必须同时原样保存到结果目录的 `*_cmd.txt` 或 Manifest。HTML 负责教学、
解释和索引,落盘命令文件负责精确审计。
## 0. 双机通信前置知识与当前修正
完整术语、设备映射、日志判读和 2026-07-30 网络事故复盘见:
- [6000D 双机通信与 NCCL 术语入门](./6000D双机通信与NCCL术语入门.html)
本项目部署时只使用两条节点间计算网:
| 物理端口 | Linux netdev/IP 入口 | RDMA Verbs/HCA 入口 | 交换路径 |
|---|---|---|---|
| 400G Rail 1 | `eth0` | `mlx5_0` | switch 1 |
| 400G Rail 2 | `eth3` | `mlx5_3` | switch 2 |
`eth0``mlx5_0` 不是同一个软件设备。它们是同一条 400G 物理 Ethernet
端口的两种入口:前者服务 IP/TCP Socket后者服务 RoCE/RDMA Verbs。
400G 指物理链路的标称线速,不专属于 TCP 或 RDMA400 Gbit/s 约等于
50 GB/s 单向理论上限,不能直接当作 NCCL 或模型端到端可达到的吞吐。
当前唯一启动入口只允许 `eth0/eth3``mlx5_0/mlx5_3`,在两端预检并
透传 `rdma_cm/uverbs0/uverbs3`,并要求 NCCL INFO 证明两条 HCA 的
`NET/IB + GDRDMA` 已启用,否则不开始 benchmark。修正后的 Phase 1
正式矩阵 12/12、长 Decode 补测 2/2 均成功。
## 1. 目标与原则
### 1.1 最终目标
在不做 PD 分离的前提下,定位 DeepSeek-V4-Pro 在双机 6000D 上的端到端瓶颈,并提高:
- 满足 TTFT、TPOT 等 SLO 时的最大吞吐。
- 长上下文 Prefill 性能。
- Decode 输出吞吐和单请求 TPOT。
- 混合流量下的稳定性与 P95/P99 时延。
- 16 张 GPU、PCIe 和双 Rail 计算网的有效利用率。
### 1.2 核心原则
1. 先找关键路径,再调参数。
2. 先用端到端指标确认问题,再用 Timeline 找到阶段,最后才用 Kernel Profiler。
3. Prefill、Decode 和混合干扰必须分别测试。
4. 一次只改变一个变量,每项优化都要保留可复现的 A/B 结果。
5. 单算子更快不代表服务吞吐更高,最终结论必须回到真实请求和 SLO。
6. Profiling Run 只用于定位,不能与无 Profiler 的正式性能结果直接比较。
## 2. 当前最值得验证的瓶颈假设
| 编号 | 假设 | 为什么值得优先检查 |
|---|---|---|
| H1 | TP16 每层跨机通信暴露过多 | 两台机器没有跨机 NVLinkTP Collective 需要经过 RoCE |
| H2 | NSA Indexer 或 Sparse Attention Kernel 效率不足 | DSV4-Pro 的稀疏注意力路径复杂Indexer 可能抵消稀疏收益 |
| H3 | MoE Grouped GEMM 或路由负载不均 | Decode 小 Batch 容易 Memory-bound热门专家可能制造慢 Rank |
| H4 | 长 Prefill 干扰在线 Decode | 统一实例中 Prefill 与 Decode 竞争计算、显存带宽和调度预算 |
| H5 | CPU Scheduler、Metadata 或 Kernel Launch 产生 GPU 空洞 | 小 Batch Decode 对 CPU 和 Launch 开销特别敏感 |
| H6 | KV Cache 容量、碎片或 Preemption 限制并发 | 大模型权重占用高,剩余 HBM 决定上下文与并发容量 |
| H7 | 当前并行拓扑并非最优 | 使用 16 张卡不等于只能采用单一 TP16 拓扑 |
## 3. Profiling 总体流程
```text
端到端性能地图
服务内部指标与硬件计数器
Nsight Systems 时间线
确定 1-3 个主要瓶颈
Nsight Compute 或专项 Microbenchmark
提出优化并做单变量 A/B
回到完整 Benchmark 和 SLO 验证
```
不要直接对完整服务运行长时间 Nsight Compute。它的开销很高也会生成巨大的报告。应先用 Nsight Systems 找到占关键路径的 Kernel再构造小型复现。
## 4. Phase 0冻结可复现环境
正式测试前,每个 Run 必须保存以下信息:
- 两台机器的 GPU、Driver、CUDA、NCCL 版本。
- vLLM 或 SGLang 的镜像名、镜像 ID、Git Commit 和 Python 包版本。
- 模型目录、权重文件校验信息和模型配置。
- 完整 Docker Run 与服务启动命令。
- 完整 Benchmark 命令。
- TP、DP、PP、EP 拓扑。
- Attention、NSA、Indexer、MoE、GEMM 和通信 Backend。
- `NCCL_SOCKET_IFNAME``NCCL_IB_HCA``NCCL_CROSS_NIC` 等通信变量。
- GPU Memory Fraction、Context Limit、Active Request Limit、KV Cache Dtype。
- CUDA Graph、Chunked Prefill、Prefix Cache 和投机解码状态。
- 运行前后的 `nvidia-smi`、容器列表和网络状态。
建议每次运行生成:
```text
results/<RUN_ID>/
run_manifest.txt
server_cmd.txt
bench_cmd.txt
summary.csv
requests.jsonl
server/
hardware/
profiles/
notes.md
```
### 基线约束
- 初始基线不启用 MTP、EAGLE、DSpark 等投机解码。
- 初始基线使用唯一随机 Prompt避免 Prefix Cache 影响。
- 服务启动完成后做固定 Warm-up。
- 每个测试点至少重复 3 次。
- 正式结果使用无 Profiler 运行。
- Profiling 只捕获预热后的少量 Engine Step。
## 5. Phase 1建立阶段化性能地图
详细结果见 [Phase 1 实验档案](./phase1_exp.html),实现说明见
[Phase 1 代码详解](./phase1_code.html)。
### 5.1 第一轮最小矩阵
| 场景 | ISL | OSL | 并发 | 主要目标 |
|---|---:|---:|---:|---|
| P1 短 Prefill 延迟底线 | 1K | 1 | 1 | 固定开销与最小 TTFT |
| P2 中长 Prefill | 32K | 1 | 1 | NSA、Indexer、Attention |
| P3 长 Prefill | 128K | 1 | 1 | 长上下文计算和显存压力 |
| P4 Prefill 吞吐 | 32K | 1 | 逐步加并发 | Chunked Prefill 与输入 TPS |
| D1 Decode 延迟底线 | 1K | 1K | 1 | 单请求 TPOT |
| D2 Decode 吞吐 | 1K | 1K | 16/32/64 | MoE、Batch 与通信 |
| M1 混合负载 | Decode C=32 时注入 128K Prefill | | | Prefill 对在线 Decode 的干扰 |
长度应以当前已验证的服务容量为上限。如果 128K 不可用,先降到 64K但必须在 Manifest 中记录原因。
### 5.2 并发搜索
沿用 Add-16 加退化回退策略:
```text
C = 1延迟基线
C = 16, 32, 48, 64, ...
```
停止条件需要区分:
- `OOM`
- `ENGINE_CRASH`
- `TTFT_SLO_EXCEEDED`
- `TPOT_SLO_EXCEEDED`
- `TPS_SATURATED`
- `TPS_REGRESSION`
- `MAX_CONCURRENCY_REACHED`
不能把“最大成功并发”“最高 TPS 并发”和“满足 SLO 的最大并发”混为同一个值。
### 5.3 每个 Case 必须记录
#### 请求层
- 实际成功、失败和超时请求数。
- 实际输入、输出和总 token 数。
- Request Throughput。
- Input、Output 和 Total TPS。
- P50/P95/P99 TTFT。
- P50/P95/P99 TPOT。
- P50/P95/P99 ITL。
- P50/P95/P99 E2E。
- Queue Time 与 Service Time若框架支持。
#### Scheduler 层
- Running、Waiting Request 数。
- 每轮 Batch Sequence 数。
- 每轮 Prefill、Decode Token 数。
- Chunked Prefill 次数和 Chunk 大小。
- Forward Step 时间。
- Scheduler/Metadata 准备时间。
- Preemption、Recompute、Retract 次数。
- Prefix Cache Hit Tokens。
#### 显存层
- 权重占用。
- KV Cache 总量、已用量和峰值。
- CUDA Graph 占用。
- Workspace 与临时 Tensor 峰值。
- Reserved/Allocated 差异与碎片。
## 6. Phase 2同步采集硬件指标
详细实现与运行命令见
[Phase 2 实验档案](./phase2_exp.html),实现说明见
[Phase 2 代码详解](./phase2_code.html)。
首轮 8/8 benchmark 已完成最终采集代码、Worker 分发修复和双节点通信
smoke test 已通过,等待最终正式复跑。
Phase 2 只提供一个用户入口:
```bash
cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution
RUN_ID=dsv4pro-phase2-$(date +%Y%m%d-%H%M%S)
tmux new-session -d -s dsv4pro-phase2 \
"RUN_ID=${RUN_ID} bash run_hardware_contention_attribution.sh all \
2>&1 | tee /data/hzy/${RUN_ID}.log"
```
`all` 会内部完成 TP16 服务启停、两节点采集、五个固定 Case、混合 A/B、
结果汇总和异常清理。不要手工并行执行 Phase 1 的 `start/stop`
### 6.1 GPU
测试期间持续记录:
```bash
nvidia-smi \
--query-gpu=index,timestamp,utilization.gpu,utilization.memory,\
memory.used,memory.total,power.draw,temperature.gpu,clocks.sm,clocks.mem,pstate \
--format=csv,noheader,nounits
```
重点观察:
- SM Utilization。
- HBM Utilization。
- 显存占用。
- GPU Clock、Memory Clock。
- Power 与温度降频。
- PCIe RX/TX。
如果有 DCGM增加
- Tensor Core Active。
- DRAM Active。
- SM Active。
- PCIe Throughput。
- GPU Stall 与 XID。
### 6.2 CPU
记录服务主进程与 Worker 线程:
```bash
pidstat -t -p <PID> 1
mpstat -P ALL 1
numastat -p <PID>
```
需要发现:
- 单个 Scheduler Thread 是否满核。
- Tokenizer、HTTP Frontend 或 Python 线程是否阻塞。
- Worker 是否跨 NUMA 访问。
- CPU 空洞是否对应 GPU 空洞。
### 6.3 网络
Phase 1 正式 Run 已确认容器内可见 RDMA 设备NCCL 同时识别
`mlx5_0/mlx5_3`,跨节点 Channel 使用 `NET/IB + GDRDMA`。Phase 2
保留相同 fail-closed 门禁,并同步采集两条 Rail 的流量和错误计数。
当前拓扑中需要分别观察两条 Compute Rail确认
- 两条 Rail 是否同时有流量。
- 带宽是否均衡。
- 是否有丢包、重传、PFC Pause 或错误计数。
- 慢 Rank 是否固定绑定某个 NIC 或 NUMA 节点。
基础监控可以使用:
```bash
sar -n DEV 1
ethtool -S eth0
ethtool -S eth3
```
通信调试 Run 可以临时开启:
```bash
NCCL_DEBUG=INFO
NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH,TUNING
```
该日志开销较高,不应在正式性能结果中长期启用。
## 7. Phase 3Nsight Systems 时间线
### 7.1 捕获策略
- 只捕获预热后的 5 到 10 个 Engine Step。
- Prefill、Decode 和混合干扰分别生成报告。
- 两台机器分别保存原始报告。
- 优先保留所有 Rank文件过大时至少保留代表 Rank 和跨机通信相关 Rank。
- 报告必须和对应 Benchmark Case ID 绑定。
### 7.2 vLLM
当前版本支持时,使用 CUDA Profiler 动态 Capture
```bash
export VLLM_WORKER_MULTIPROC_METHOD=spawn
nsys profile \
--trace=cuda,nvtx,nccl \
--trace-fork-before-exec=true \
--cuda-graph-trace=node \
--capture-range=cudaProfilerApi \
--capture-range-end=repeat \
-o /data/profile/dsv4_tp16 \
vllm serve ... \
--profiler-config.profiler cuda
```
压测端使用支持 Profile Trigger 的 Bench
```bash
vllm bench serve ... --profile
```
### 7.3 SGLang
服务启动前设置:
```bash
export SGLANG_TORCH_PROFILER_DIR=/data/profile/sglang
```
Profiling 专用 Run 可增加:
```text
--enable-layerwise-nvtx-marker
```
捕获预热后的 10 个 Step
```bash
curl -X POST http://127.0.0.1:30000/start_profile \
-H 'Content-Type: application/json' \
-d '{
"output_dir": "/data/profile/sglang",
"start_step": 5,
"num_steps": 10,
"activities": ["CPU", "GPU"]
}'
```
多机 Trace 自动合并要求两台机器能访问同一个共享输出目录。没有共享目录时分别保存,再在本地汇总。
### 7.4 时间线必须回答的问题
1. Prefill 和 Decode 各自的 Top Kernel 是什么?
2. NCCL 在关键路径上的暴露时间是多少?
3. 通信与计算重叠了多少?
4. 每层之间是否存在 CPU 或同步空洞?
5. CUDA Graph 是否覆盖常见 Decode Batch
6. 16 个 Rank 是否同时结束?
7. 是否存在固定慢 Rank
8. MoE Expert Token 是否严重不均衡?
9. NSA Indexer 的成本占 Sparse Attention 总成本多少?
10. 长 Prefill 到来时Decode Kernel 为什么被延迟?
### 7.5 报告分析
```bash
nsys stats <REPORT>.nsys-rep
```
重点查看:
- CUDA GPU Kernel Summary。
- NCCL Summary。
- NCCL GPU Time Utilization。
- Communication/Compute Overlap。
- NCCL Straggler。
- CUDA API Summary。
- OS Runtime 和 CPU Thread Timeline。
## 8. 证据到优化方向的映射
| 观察到的证据 | 更可能的根因 | 下一项 A/B |
|---|---|---|
| Decode 中 NCCL 占比高,且通信未被计算覆盖 | TP16 通信受限 | TP8+PP2、NCCL 拓扑与算法 |
| C=1 很慢,并发增加后 TPS 明显改善 | MoE/权重读取 Memory-bound | Batch、MoE Backend、MTP |
| GPU 利用率呈锯齿Kernel 间有明显空洞 | CPU Scheduler 或 Launch 开销 | CUDA Graph、异步调度 |
| 一个或少数 Rank 长期最慢 | Expert、NIC 或 NUMA 不均衡 | EPLB、Affinity、Rank Mapping |
| NSA Indexer 时间接近或超过 Attention | 稀疏索引收益不足 | Indexer Backend、Top-K、融合 |
| 长 ISL 的 Attention 时间异常增长 | Prefill Kernel 或 Chunking 问题 | Prefill Backend、Chunk Size |
| KV Cache 长期接近满并发生重算 | HBM 容量不足 | FP8 KV、并发和 Context 上限 |
| 注入长 Prefill 后 Decode TPOT 暴涨 | Prefill/Decode 相互干扰 | Chunked Prefill 与 Scheduler |
| GPU 利用率低但 CPU 单核满载 | Host 端瓶颈 | Frontend、Tokenizer、Scheduler |
| 两条 Rail 流量明显不均 | NIC Mapping 或 NCCL 拓扑 | HCA、CROSS_NIC、NUMA Affinity |
## 9. Phase 4优先级最高的拓扑实验
### 9.1 ATP16 基线
当前方案用于建立所有后续实验的对照。
风险是每层 TP Collective 都可能跨越两台机器Decode 小消息通信尤其容易被延迟支配。
### 9.2 BTP8 + PP2
逻辑上:
```text
Node 5: Pipeline Stage 0, TP8
Node 7: Pipeline Stage 1, TP8
```
理想情况下,每卡权重占用与 TP16 接近:
```text
TP16:
每卡权重约为 W / 16
TP8 + PP2:
每个 Stage 保存 W / 2
Stage 内由 8 卡切分
每卡权重约为 (W / 2) / 8 = W / 16
```
潜在收益:
- 每层 TP Collective 留在单机。
- 跨机主要传输 Pipeline Stage 边界激活。
- 避免每层都进行跨机 AllReduce。
潜在代价:
- Pipeline Bubble。
- 低并发延迟可能变差。
- KV Cache、Hybrid Cache 和 DSV4-Pro 模型实现可能暂不支持 PP。
- 两个 Stage 的计算量可能不均衡。
测试顺序:
1. 先做加载与单请求 Smoke Test。
2. 对比 C=1 Decode 延迟。
3. 对比 C=16/32/64 吞吐。
4. 观察跨机网络流量是否显著下降。
5. 观察两个 Pipeline Stage 是否负载均衡。
### 9.3 CAttention TP8/DP2 + MoE EP16
目标是:
- Attention 在节点内使用 TP8。
- 两个 Attention DP Group 并行处理请求。
- MoE Expert 在 16 张卡上分布。
这接近“Attention DP + MoE EP”的思路。Expert 权重通常占模型大头,因此即使 Attention 权重复制两份,也有机会放入显存。
必须先验证:
- 当前 vLLM/SGLang 版本是否支持 DSV4-Pro 的该拓扑。
- Expert 权重、非 Expert 权重和 KV Cache 的实际显存占用。
- All-to-All 是否比当前 TP16 AllReduce 更划算。
- Expert 负载是否均衡。
## 10. Phase 5通信专项
### 10.1 不只测 1 GiB 大消息
之前的 1 GiB `all_reduce_perf` 主要说明大消息带宽。Decode 中的 Collective 往往更小,可能由延迟主导。
需要覆盖真实消息尺度:
```bash
all_reduce_perf -b 8K -e 64M -f 2 -g 8
all_gather_perf -b 8K -e 64M -f 2 -g 8
reduce_scatter_perf -b 8K -e 64M -f 2 -g 8
```
若启用 EP还要测试 All-to-All。
### 10.2 通信优化顺序
1. 确认两条 Rail 都在工作。
2. 确认 Rank、GPU、NIC 和 NUMA Affinity。
3. 对照实际模型消息大小。
4. 查看 NCCL 自动选择的 Algorithm、Protocol 和 Channel。
5. 只有自动选择明显不合理时,才 A/B `Ring/Tree``Simple/LL128` 等设置。
6. 观察模型端到端结果,而不只看 nccl-tests 峰值。
## 11. Phase 6Kernel 专项
从 Nsight Systems 中选累计占关键路径最高的 1 到 3 个 Kernel再使用 Nsight Compute。
DSV4-Pro 的优先怀疑对象:
- NSA Indexer/Top-K。
- Sparse MLA/Attention Prefill。
- Sparse MLA/Attention Decode。
- MoE Gate、Dispatch、Grouped GEMM、Combine。
- FP8 Quant/Dequant 与 Scale Packing。
- RMSNorm、Rope、KV Cache Store 等碎片化小算子。
- NCCL Collective Kernel。
需要分析:
- SM 和 Tensor Core 利用率。
- DRAM 吞吐与 L2 Hit Rate。
- Occupancy。
- Register 与 Shared Memory 压力。
- Warp Stall 原因。
- Kernel Shape 与 Batch/Token 数。
- 小 Kernel Launch 次数。
优化优先顺序:
1. 切换已有高性能 Backend。
2. 调整 Backend 的 Shape/Workspace/Tile 配置。
3. 消除无用 Copy、Cast 和临时 Tensor。
4. 融合相邻的 Memory-bound 小算子。
5. 现有 Backend 不覆盖关键 Shape 时,再开发新 Kernel 或提交 PR。
## 12. Phase 7Scheduler 与统一实例干扰
### 12.1 混合干扰实验
先建立稳定 Decode 背景流量:
```text
ISL=1K
OSL=1K
C=32
```
运行稳定后,周期性注入一个长 Prefill
```text
ISL=128K
OSL=1
C=1
```
比较注入前后:
- Decode P50/P95/P99 TPOT。
- Decode Output TPS。
- 长请求 TTFT。
- 每轮 Prefill Chunk。
- Scheduler Queue。
- GPU Timeline。
### 12.2 可调方向
- Chunked Prefill Size。
- Max Prefill Tokens。
- Max Batched Tokens。
- Max Running Requests/Max Num Seqs。
- Prefill 与 Decode 调度优先级。
- CUDA Graph Batch Coverage。
- 双 Batch Overlap 或框架已有的通算重叠能力。
调优目标不是单独最大化 Prefill TPS而是减少长 Prefill 对 Decode SLO 的破坏。
## 13. Phase 8显存与缓存
当前初始值应保持固定,只在发现明确证据后调整:
- GPU Memory Fraction。
- Max Context Length。
- Active Request Limit。
- KV Cache Dtype。
- Page/Block Size。
- CUDA Graph Capture Size。
若 KV Cache 是瓶颈,优先顺序:
1. 确认权重和 Workspace 的真实占用。
2. 检查 Allocated/Reserved 差值与碎片。
3. 使用 FP8 KV Cache前提是当前 Kernel 支持且精度可接受。
4. 根据业务上限设置 Context Length不为不会出现的极端长度预留容量。
5. 设置合理的 Active Request Limit避免运行时 OOM。
6. 再考虑 CPU/L3 KV Offload。
Prefix Cache 单独做第二阶段测试:
| 命中率 | 用途 |
|---:|---|
| 0% | 纯计算基线 |
| 20% | 低复用业务 |
| 50% | 中等公共前缀 |
| 80% | Agent/Coding 高复用 |
Mooncake 或三级缓存只有在 Prefix 可复用时才有明显价值。随机独立 Prompt 不适合评价它。
## 14. Phase 9MTP 与模型级优化
当 TP16 Baseline、并行拓扑、通信、Backend 和 Scheduler 已稳定后,再测试:
- 原生 MTP。
- DSpark。
- EAGLE。
- KV Cache 量化。
- 更低比特权重量化。
- Sparse Attention 算法或 Indexer 优化。
投机解码至少记录:
- Accept Rate。
- Mean Accept Length。
- Target Forward TPS。
- Draft/MTP 开销。
- CPU 调度气泡。
- 不同并发下的净收益。
不能只看 Accept Length也不能只看 C=1。
## 15. 里程碑与交付物
### M1可信 Baseline
完成条件:
- 七组最小矩阵均有 3 次重复。
- 同一 Case 的关键 TPS 变异系数尽量不超过 3%。
- 所有环境、命令和日志可追溯。
交付:
- Baseline Summary。
- SLO Frontier。
- GPU/CPU/Network Timeline。
### M2瓶颈报告
完成条件:
- Prefill、Decode、混合三类 Profile 完成。
- 找出累计贡献最高的 1 到 3 个瓶颈。
- 每个判断都有 Trace、计数器或日志证据。
交付:
- `.nsys-rep` 或 Torch Trace。
- Kernel/NCCL Summary。
- Bottleneck Evidence Table。
### M3并行拓扑 A/B
完成条件:
- TP16 保留基线。
- TP8+PP2 完成可行性与性能验证。
- Attention DP + MoE EP 完成支持性和显存评估。
交付:
- 每种拓扑的显存、通信、TTFT、TPOT 和 TPS 对比。
- 推荐拓扑与不推荐拓扑的证据。
### M4首轮优化闭环
完成条件:
- 至少一项优化通过完整 Benchmark。
- 结果在无 Profiler 环境下可复现。
- 正确性无回归。
- 满足 SLO 的吞吐有明确改善。
期望目标:
- 首轮争取获得至少 10% 的 SLO 内吞吐提升,或显著降低 P95/P99 长尾。
- 若无法提升,也必须形成排除结论,说明瓶颈为什么不在该方向。
## 16. 实验纪律
每次实验都必须回答:
1. 改了什么?
2. 为什么认为它会影响当前瓶颈?
3. 除该变量外,还有什么发生了变化?
4. 端到端指标如何变化?
5. Profile 证据如何变化?
6. 是否引入精度、稳定性或显存风险?
7. 是否值得保留?
禁止以下做法:
- 同时修改多个参数后只报告最终 TPS。
- 用 Profiling Run 和普通 Run 直接比较性能。
- 只看平均值,不看 P95/P99。
- 用配置 ISL/OSL 估算 TPS而不核对实际 token 数。
- 用 1 GiB NCCL 带宽代表 Decode 小消息性能。
- 因单个 Kernel 更快就宣称端到端优化成功。
- OOM 后不重启服务继续测试。
## 17. 首轮执行建议
建议直接按以下顺序推进:
1. 固化当前 TP16 服务命令和 Manifest。
2. 跑 P1、P2、P3、D1、D2、M1。
3. 同步采集 GPU、CPU 和双 Rail 数据。
4. 对 P3、D2、M1 各捕获 5 到 10 个 Engine Step。
5. 输出 NCCL、NSA/Attention、MoE、CPU Gap 四项时间占比。
6. 根据最大暴露时间选择第一个优化方向。
7. 优先做 TP16 与 TP8+PP2 的可行性和性能对比。
8. 回到完整 Benchmark 验证 SLO 内吞吐。
最重要的判定标准是:
> 优化暴露在关键路径上的时间,而不是只优化看起来最慢的单个算子。
## 18. 参考资料
- [vLLM Profiling](https://docs.vllm.ai/en/stable/contributing/profiling/)
- [SGLang Benchmark and Profiling](https://github.com/sgl-project/sglang/blob/main/docs/developer_guide/benchmark_and_profiling.md)
- [SGLang Server Arguments](https://github.com/sgl-project/sglang/blob/main/docs/advanced_features/server_arguments.md)
- [NVIDIA Nsight Systems User Guide](https://docs.nvidia.com/nsight-systems/UserGuide/index.html)
- [NVIDIA Nsight Systems Analysis Guide](https://docs.nvidia.com/nsight-systems/AnalysisGuide/index.html)
- [腾讯混元 Hy3 Preview AI Infra 精读笔记](../hy3_infra_article/Hy3_Preview_AI_Infra_精读笔记.md)

View File

@ -0,0 +1,659 @@
# Hy3 Preview AI Infra 推理优化精读笔记
> 原文:[腾讯混元 AI Infra 如何优化 Hy3 Preview一次大模型推理性能提升的技术拆解](https://zhuanlan.zhihu.com/p/2053138680768943935)
> 作者:混元 AI Infra 推理团队
> 发布时间2026-06-26
> 整理时间2026-07-29
> 用途:推理优化学习、实验设计与工程路线参考
这是一份基于原文及配图整理的技术学习笔记,不是逐字转载。重点是解释每项优化在解决什么瓶颈、为什么有效、依赖什么条件,以及如何映射到我们当前的 vLLM、SGLang、DeepSeek-V4-Flash 和 Kimi-K3 实验。
## 1. 一页结论
这篇文章最值得学习的并不是某一个算子,而是它展示了一套完整的推理优化方法:
1. 先用真实业务数据和明确 SLO 定义目标,而不是只看固定长度随机请求。
2. 将 Prefill 和 Decode 分开分析,因为二者的瓶颈、并行策略和优化目标不同。
3. 从算子、融合、并行、缓存、调度、量化和稀疏算法六个层级逐层消除瓶颈。
4. 不是寻找一个对所有场景都最好的配置,而是围绕业务分布寻找吞吐、时延、容量之间的 Pareto 前沿。
5. 单算子加速不等于端到端等比例加速,必须回到真实流量和 SLO 重新测量。
文章的最终测试口径很有参考价值:
- 5000 条真实请求。
- 最大输入约 192K平均输入约 68K。
- 最大输出约 64K平均输出约 0.9K。
- 理论 Prefix Cache 命中率约 80%。
- 硬件为 96 GB Hopper 架构 GPU结果图标注为 H20。
- SLO 为 TTFT 不超过 4 秒、TPOP 不超过 50 毫秒。
- 总体测试精度标注为 W8A8C8。
图中报告的最终单卡吞吐约为:
- 输入287.8 万 token/min/GPU约 47,967 token/s/GPU。
- 输出8.6 万 token/min/GPU约 1,433 token/s/GPU。
![最终单卡输入与输出 TPM](assets/01_overall_results.jpg)
这里需要特别注意:输入吞吐远高于输出吞吐并不奇怪。文章的真实流量平均输入约 68K、平均输出约 0.9K,输入 token 数本来就比输出多很多;同时 Prefix Cache 命中也会改变 Prefill 的实际计算量。不能只用这两个柱子的比例推断 Prefill 和 Decode 的硬件速度。
## 2. 模型与问题背景
Hy3 Preview 是一个 GQA + MoE 模型。官方仓库给出的主要规格包括:
- 总参数量约 295B单 token 激活参数约 21B。
- 另有约 3.8B 的 MTP 层参数。
- 80 层主模型。
- 192 个专家,每个 token 激活 8 个专家。
- 64 个 Attention Head、8 个 KV HeadHead Dim 为 128。
- 原生上下文上限 256K。
官方模型仓库:[Tencent-Hunyuan/Hy3-preview](https://github.com/Tencent-Hunyuan/Hy3-preview)
它在 Hopper 96 GB GPU 上主要面对四类矛盾:
| 矛盾 | 表现 |
|---|---|
| 长上下文与 TTFT | Prefill 计算量大,混合长度请求造成长尾 |
| MoE 与通信 | Expert Dispatch/Combine、TP AllReduce 和跨节点流量较重 |
| 权重与 KV Cache | 权重挤压 HBM限制长上下文和并发容量 |
| MTP 与异步调度 | 每轮实际接受 token 数不固定CPU 无法按传统方式提前准备 |
文章的优化可以整理成六层:
| 层级 | 代表技术 | 主要目标 |
|---|---|---|
| 算子 | 动态 Attention、Router GEMM、FusedMoE | 提高单个热点算子的效率 |
| 融合 | Rope/Norm/Quant/KV、AllReduce/Norm/Add、Sampler、GEMM/RS | 减少 Kernel Launch、HBM 往返和通信等待 |
| 并行 | Prefill TPSP、Decode Attention-DP + MoE-EP | 为不同阶段选择合适的数据切分 |
| 缓存 | GPU、CPU、KVStore 三级缓存 | 扩大 Prefix Cache 容量并支持跨实例复用 |
| 调度 | MTP 异步流水 | 隐藏 CPU 调度开销 |
| 模型压缩 | W4A8、Attention FP8、Stem 稀疏注意力 | 降低权重、访存和长上下文计算成本 |
## 3. 算子优化
### 3.1 Attention动态切分和负载均衡
#### 问题
线上 Batch 中常同时存在长、短请求。静态 Split-KV 必须预先固定切分粒度:
- 切得太少,长序列不能充分占满 SM。
- 切得太多,短序列会承担额外调度、归约和 Kernel 开销。
- 长短请求混合时,不同 CTA 的工作量不均,最慢 CTA 决定整次 Kernel 的结束时间。
#### 方案
文章采用统一 Tile 粒度加贪心装桶:
1. 将所有请求拆成统一大小的 Attention Tile。
2. 把不同请求产生的 Tile 汇总成一条任务流。
3. 根据全局 Tile 数量,为每个 CTA 分配相同或接近的任务预算。
4. 每轮推理前生成任务映射表Attention Kernel 按表领取任务。
5. 最后由 Combine Kernel 合并 Split-KV 的局部结果。
配图中的例子把长度为 1024、5120、2048 的三个请求按 512 token 拆成 2、10、4 个 Tile再给 4 个 CTA 各分配 4 个 Tile。长请求可以跨 CTA 执行,不再让某个 CTA 单独拖住整批请求。
![动态 Attention 调度](assets/02_attention_dynamic_schedule.jpg)
#### 收益
- 单 Batch 长文本场景,单算子最高约 2.95 倍加速。
- 混合长度 Batch 场景约 1.59 到 1.76 倍加速。
#### 对我们的启发
我们当前固定 ISL/OSL Grid 适合测容量边界,但不能验证这种负载均衡优化。要增加一个混合长度测试:
- 同一 Batch 同时放入 1K、4K、16K、64K、128K 请求。
- 保持总 token 数近似相同,对比固定长度 Batch。
- 观察 P95/P99 TTFT、GPU SM Occupancy、Attention Kernel 尾部空转时间。
### 3.2 Router GEMM用两路 BF16 重构 FP32
#### 问题
MoE Router 和稀疏 Attention 的打分对精度敏感,可能需要 FP32 权重。直接执行 BF16 激活乘 FP32 权重会遇到:
- Tensor Core 路径利用不足。
- 激活转成 FP32/TF32 会增加类型转换。
- 小 M Shape 下CUDA Core 路径尤其低效。
#### 方案
离线把 FP32 权重拆成高位 BF16 与低位 BF16 残差:
```text
W ≈ W_high + scale * W_low
scale = 1 / 256
```
推理时执行两路 BF16 GEMM
```text
Y = X * W_high^T + scale * (X * W_low^T)
```
两路计算被放进同一个 Kernel
- X 只从 HBM 读取一次。
- 两路结果分别在寄存器中累加。
- Epilogue 中完成修正。
- 最终只写回一次结果。
![双 BF16 重构 FP32 Router GEMM](assets/03_router_gemm.jpg)
#### 收益
在 N=192、K=4096、M=2 到 4096 的测试范围内,相比 FP32 cuBLAS 路径约快 2.86 到 3.22 倍。
#### 对我们的启发
这不是简单的 `dtype` 开关而是数值表示、Kernel 实现与模型精度共同设计。它提醒我们:
- Router 往往是小矩阵,不能用大 GEMM 的经验判断性能。
- 看 GPU 利用率时,要单独检查 Router、Indexer 和 Expert GEMM 的 Shape。
- 对 DeepSeek/Kimi 的稀疏路由,需要区分“精度敏感的小算子”和“吞吐主导的大算子”。
### 3.3 FusedMoE重排完整专家执行链
文章不是只替换 Grouped GEMM而是重构了整个 MoE 数据通路:
1. 在共享内存中分块统计路由结果,并为每个专家预留连续输出区间。
2. Gate-Up GEMM 直接按路由索引读取原始输入,省略显式 Gather。
3. 取消部分 Warp Specialization以提高 SM 驻留密度。
4. 激活量化结果按专家连续写入,供 Down GEMM 顺序读取。
5. 末端直接完成 Top-K 加权聚合,减少中间 HBM 往返。
6. 用 PDL 串联阶段,降低频繁 Kernel Launch 形成的空隙。
报告的单算子收益:
- TP=8、EP=1相比 vLLM CUTLASS、vLLM Triton 和 SGLang 路径约快 1.5 到 1.6 倍。
- TP=1、EP=8约快 1.2 到 1.5 倍。
开源实现:[Tencent/hpc-ops](https://github.com/Tencent/hpc-ops)
这里有一个很重要的实验原则:同一个 MoE Kernel 在 TP8/EP1 与 TP1/EP8 下的收益不同,因为每卡 Expert 数、每个 Expert 收到的 token 数、通信方式和矩阵 Shape 都变了。比较 MoE Backend 时必须固定完整的 TP/DP/EP 拓扑。
## 4. 算子融合
### 4.1 Rope + Norm + Hadamard + Quant + Store KV
QKV Projection 之后通常存在一串算术强度很低的 Element-wise 操作。若每一步都是独立 Kernel就会反复
- 从 HBM 读数据。
- 写回中间结果。
- 发起新的 Kernel。
文章把 Rope、RMSNorm、Hadamard、量化和 KV Cache 写入融合为一个 Kernel。中间值尽量停留在寄存器中最后直接以低比特格式写入 KV Cache。
报告的融合算子加速约 5 倍。它体现的是典型原则:
> 对访存受限的小算子,减少一次 HBM 往返往往比减少几次算术操作更重要。
### 4.2 AllReduce + Norm + Add
TP 路径通常按以下顺序执行:
```text
AllReduce -> Residual Add -> RMSNorm
```
拆开执行会产生通信等待和中间 Tensor 读写。文章把它融合为:
```text
RMSNorm(AllReduce(x) + residual, weight)
```
提供两类实现:
- Prefill 高吞吐路径:利用 NVSwitch 多播,面向较大的 token Batch。
- Decode 低延迟路径:使用 Lamport P2P并用 PDL 让两个 Kernel 重叠。
覆盖约 8K 到 32K token 的场景,相比 NCCL 和 FlashInfer 同类路径最高约快 1.68 倍。
这说明通信优化不能只看 NCCL Bandwidth。对于小消息和 DecodeKernel Launch、同步点与后处理往往和网络带宽同样重要。
### 4.3 Sampler 融合
常规采样可能包含重复惩罚、温度缩放、Top-K、Top-P、Softmax 和随机采样等十余个 Kernel。文章将其压缩成两个核心 CUDA Kernel并根据简单温度采样或完整采样选择专用路径。
关键设计:
- 全局词表尽量只读取一次。
- 重复惩罚掩码留在 GPU 内处理。
- 单请求可拆给多个 CTA。
- Max Top-K 不超过 64 时使用局部堆归并。
- Top-K 与 Softmax 的 max/sum 归约融合。
下图直观展示了融合前后的 profiler 时间线:融合前有大量碎片化 Kernel融合后主体工作集中到少数长 Kernel。
![Sampler 融合前](assets/04_sampler_before.jpg)
![Sampler 融合后](assets/05_sampler_after.jpg)
文章报告相较 vLLM 与 FlashInfer 的采样路径分别约有 5.5 倍和 2.5 倍单算子提升。端到端收益仍取决于输出长度、Batch 和模型主体计算占比。
### 4.4 GEMM + ReduceScatter 细粒度重叠
传统执行顺序是完整 GEMM 结束后再开始 ReduceScatter。文章将 SM 分成两类角色:
- 计算 SM执行 GEMM。
- 通信 SM搬运已经完成的输出 Tile。
计算 SM 每生成一个 Tile就写入本地 Buffer 并通知通信 SM通信不必等待整个矩阵完成。
此外GEMM 内部又划分为三级 Warp 流水:
```text
Load Warp -> MMA Warp -> Epilogue Warp
```
![GEMM 三级 Warp 流水](assets/06_gemm_comm_fusion.jpg)
在 M 为 8K、16K、32K、64K 的四组 Shape 上,通信覆盖率约从 76.5% 增长到 84.8%,端到端相较串行路径约快 1.68 到 1.81 倍。
这个方向对多机 TP/EP 特别重要。我们以后跑 NCCL Test 只能知道通信上限,真正的模型吞吐还取决于能否把通信藏在计算后面。
## 5. Prefill 与 Decode 的并行策略
### 5.1 PrefillTPSP
文章认为 Hy3 Preview 的纯 TP8 Prefill 有三个问题:
1. Norm、Router 等 token-wise 算子在各 TP Rank 重复计算。
2. 频繁 AllReduce 交换完整激活。
3. MoE Grouped GEMM 沿 Hidden 维切得过窄Shape 不利于 Tensor Core。
因此,它没有让整层始终使用同一种并行方式,而是在不同模块切换布局。配图给出的一层时间线包含:
- Attention 使用 TP8。
- Routed Expert 使用 TP4 + SP2。
- Shared Expert 沿 token/sequence 维使用 SP8。
- AG + QKV 和 RS + O Projection 做通信计算融合。
- Shared Expert 与通信使用多 Stream 重叠。
- AllGather 通信采用 FP8图中说明可比 BF16 减少约 50% 通信带宽。
![TPSP 单层执行时间线](assets/07_prefill_tpsp.jpg)
端到端 Prefill TTFT
| 输入长度 | 优化前 | 优化后 | 降幅 |
|---|---:|---:|---:|
| 16K | 764 ms | 536 ms | 29.9% |
| 32K | 1885 ms | 1424 ms | 24.5% |
#### 对我们的启发
“TP 越小通信越少所以一定更快”是不完整的。TP 改变的不只是通信量,还会改变:
- 每卡权重与 KV Cache 容量。
- GEMM 的 M/N/K Shape。
- 是否存在重复 token-wise 计算。
- Batch 在 DP Rank 之间的分散程度。
- 是否能使用特定融合算子。
因此TP2/DP4、TP4/DP2、TP8/DP1 必须端到端实测,不能只用通信直觉排序。
### 5.2 DecodeAttention DP + MoE EP
Decode 阶段通常 Batch 较小,单 token GEMM 更偏 Memory-bound。文章采用 Attention DP 与 MoE EP 的混合并行:
- Attention 权重在 DP Rank 上复制,让请求可以分开执行。
- Expert 权重按 EP Rank 分布,减少每卡权重占用。
- 多节点请求汇聚到 Expert 后形成更大的 Grouped GEMM Batch。
- 使用异步 EPLB根据真实专家负载重排权重。
- Shared Expert 计算与 Dispatch/Combine 尽量重叠。
- 长序列 Attention 使用 DPTP 混合方式缓解 DP Rank 负载不均。
报告的端到端吞吐提升约为 15.7% 到 44.7%。
这和我们之前 Custom DP 的现象能够对应:
- 短上下文、高并发时,独立实例容易各自形成稳定 BatchCustom DP 可能反超。
- 低并发时,请求被分散后每个实例 Batch 太小GPU 利用率下降。
- 长上下文时Prefill 和 KV Cache 压力成为主导,简单 Round Robin 无法替代全局调度与混合并行。
## 6. GPU、CPU、KVStore 三级缓存
文章把 Prefix Cache 扩展成三级:
| 层级 | 介质 | 特点 | 复用范围 |
|---|---|---|---|
| L1 | GPU HBM | 延迟最低、容量最小 | GPU 进程 |
| L2 | CPU DRAM | 容量更大、回载较快 | 实例内部 |
| L3 | 本地盘或共享 KVStore | 容量最大、延迟最高 | 本机或跨实例 |
完整请求流程:
1. Scheduler 先查 L1 GPU Prefix Cache。
2. 对未命中部分查询 L2/L3。
3. 命中的完整 KV Block 按需加载回 GPU。
4. 跳过已经命中的 Prefix Prefill。
5. 新生成的完整 KV Block 异步下沉到 L2/L3。
6. L3 连续读取失败时降级到 CPU-only避免外部存储故障拖垮服务。
配图中的 L3 Backend 可以是:
- HoverDB 本地磁盘:本机持久化缓存。
- NitroFS 共享存储:支持跨实例复用。
![三级 KV Cache 架构](assets/08_multilevel_cache.jpg)
#### 与 Mooncake 的关系
这正是 Mooncake/HiCache 一类系统的价值所在。即使不开 PD 分离,多级缓存仍能在以下场景产生价值:
- 多轮 Agent 对话存在长公共前缀。
- Coding 请求反复携带同一仓库上下文。
- 实例扩缩容、迁移或重启后仍希望复用 Prefix。
- GPU HBM 不足,希望把冷 KV 下沉到 CPU、SSD 或远端存储。
但如果测试流量全部是独立随机 token几乎没有共享前缀L2/L3 缓存只会增加查找和搬运开销。因此必须显式设计 0%、20%、50%、80% 命中率的测试组。
## 7. MTP 与异步调度
### 7.1 传统异步调度为什么失效
普通 Decode 每轮稳定生成一个 tokenCPU 可以在 GPU 执行第 N 轮时提前准备第 N+1 轮。
MTP 会一次草拟多个 token但实际接受长度是动态的。下一轮的
- Sequence Length。
- Position ID。
- KV Cache Block 映射。
- 输入 token 布局。
都依赖本轮验证结果。若 CPU 必须等待 GPU 把接受长度拷回,就会重新出现同步气泡。
### 7.2 文章的方案
CPU 暂时不等待真实接受长度,而是:
1. 按最大可能接受长度插入 Placeholder。
2. 提前准备并 Launch 下一轮。
3. 真实接受长度继续保留在 GPU。
4. 下一轮正式计算前,再由 GPU 修正 Position、KV 映射等关键状态。
这样 CPU 可以提前一整轮,而不是只和很短的 MTP Layer Forward 重叠。
![MTP 与异步调度流水](assets/09_mtp_async_schedule.jpg)
报告结果:
- 每轮减少约 5 到 10 ms 的 CPU 气泡。
- 端到端性能提升约 10% 到 20%。
#### 对我们的启发
投机解码测试不能只记录 Accept Length。至少要同时记录
- Target Model Decode TPS。
- Draft/MTP 接受长度和接受率。
- 每轮 CPU 调度时间。
- GPU 间隙和 Kernel Launch 间隔。
- 不同 Batch 下的收益。
小 Batch 时 CPU 气泡占比高MTP 调度优化可能很重要;大 Batch 时 Target Forward 本身更重,收益比例可能下降。
## 8. W4A8、Attention FP8 与精度恢复
文章的压缩链路是:
1. SmoothQuant 风格的激活平滑,抑制少数通道的离群值。
2. Attention 的 Query/Key 在量化前做 Hadamard 正交旋转,把离群值打散。
3. 使用 GPTQ 做逐层权重重建,根据二阶信息补偿低比特权重误差。
4. 做轻量级 QAT仅更新量化相关参数使模型适应任务分布。
![Hy3 W4A8 量化流程](assets/10_quantization.jpg)
报告称:
- 多领域评测与 BF16 基线的差距控制在约 1% 以内。
- 端到端吞吐提升超过 28%。
需要区分两种口径:
- 文章开头的总体线上结果标注为 W8A8C8。
- 量化章节进一步讨论的是 W4A8 + Attention FP8 路线。
二者不能当成同一套权重和同一组最终吞吐数据。
开源工具:[Tencent/AngelSlim](https://github.com/tencent/AngelSlim)
#### 对我们的路线判断
这部分不适合当前最先做,因为它可能涉及 Calibration、GPTQ 重建和 QAT。优先级应该低于
- 正确部署和基线测量。
- TP/DP/EP 与 Scheduler 调优。
- Prefix Cache 和多级缓存。
- Backend 与已有 Kernel 的选择。
当系统参数已稳定,并且确实被权重容量或 HBM 带宽限制时,再进入量化训练与精度评估。
## 9. Stem 稀疏注意力
Stem 的目标是在长上下文 Prefill 中,只计算最有价值的一部分 Attention Block。
### 9.1 Token Position Decay
普通 Uniform Top-K 对不同 Query 位置使用相同预算。Stem 认为:
- 序列头部 token 会参与更多后续因果聚合,误差可能逐层传播。
- 序列尾部 token 的影响范围较小,可以更激进地稀疏。
因此 Top-K 预算从头部的 `k_start` 逐渐衰减到尾部:
```text
k_end = mu * k_start
```
在总计算预算近似不变时,把更多预算留给影响更大的早期位置。
### 9.2 Output-Aware Metric
仅按 `QK^T` 选 token只衡量注意力路由概率没有衡量 Value 实际携带的信息强度。Stem 加入 Value 向量模长:
```text
M(i, j) = QK^T + beta * max(0, log(||V_j||_2))
```
然后基于该分数做 Top-K并交给 Block Sparse Flash Attention 计算。
![Stem 稀疏注意力](assets/11_stem_sparse_attention.jpg)
### 9.3 性能与精度
文章给出的长上下文 Prefill 加速:
| 长度 | FA3 BF16 | FA3 FP8 | Stem |
|---|---:|---:|---:|
| 16K | 1.27x | 1.45x | 1.50x |
| 32K | 1.36x | 1.73x | 1.96x |
| 64K | 1.42x | 2.02x | 2.68x |
| 128K | 1.47x | 2.29x | 3.62x |
![稀疏 Attention Prefill 加速](assets/12_sparse_speedup.jpg)
效果随长度增长而放大,符合稠密 Attention 计算复杂度快速增长的直觉。
配图还比较了 BF16 与“FP8-W8A8 + Stem”的多个任务分数。后者在不同任务上有小幅升降例如 LongBench v2 和 SWE-bench Verified 约下降 2 个绝对分Terminal-Bench 基本持平ClawEval 略有提升。不能只看平均值,需要为实际业务单独设精度门槛。
![量化与 Stem 的任务精度对比](assets/13_sparse_accuracy.jpg)
#### 对我们的意义
你以前做过稀疏注意力基模工作,这一块很适合作为中后期深入方向,但需要把“算法”和“系统”同时验证:
- 稀疏索引本身的计算是否抵消节省。
- Indexer 在 Prefill/Decode 的 Shape 是否覆盖。
- Block Pattern 能否被现有 Kernel 高效执行。
- 稀疏 KV 的布局是否引入额外 Gather。
- 128K 以上是否仍保持精度。
- Chunked Prefill 是否改变选块逻辑或数值结果。
## 10. 如何正确理解文章中的加速数字
### 10.1 不要把所有加速比相乘
例如 Attention 2.95x、融合算子 5x、FusedMoE 1.6x 并不意味着端到端能快几十倍。Amdahl 定律决定了:
```text
总体收益 = 1 / (未优化部分 + 优化部分 / 加速比)
```
而且不同优化可能覆盖同一段时间,收益会重叠。
### 10.2 固定长度 Grid 与真实数据各有用途
| 方法 | 适合回答的问题 | 不适合回答的问题 |
|---|---|---|
| 固定 ISL/OSL/C Grid | 容量边界、Shape 性能、OOM 点、参数敏感度 | 真实 P95/P99、缓存收益、混合长度长尾 |
| 真实 Trace | 线上吞吐、SLO 达标率、Prefix Cache、调度效果 | 精确定位某个 Shape 的 Kernel 问题 |
正确做法不是二选一,而是:
1. 用 Grid 画出系统性能和容量地图。
2. 用真实 Trace 验证业务加权结果。
3. 对真实 Trace 暴露出的热点 Shape 再回到 Microbenchmark 和 Profiler。
### 10.3 文章没有完全披露的变量
做横向对比时还需要确认:
- 总 GPU 数与节点数。
- vLLM/SGLang 的具体版本和 Baseline 参数。
- Cache 命中是按请求、token 还是 block 计算。
- 输入 TPM 是否统计逻辑输入 token还是实际执行 Prefill 的 token。
- MTP 接受率和平均接受长度。
- 量化精度数据与总体 W8A8C8 吞吐是否来自同一配置。
- 各单算子收益对应的 Batch、并行拓扑和频率锁定条件。
因此,这篇文章非常适合作为优化地图,但不能直接把数字当成我们的性能目标。
## 11. 映射到我们当前的工程路线
### 阶段 A建立可信 Baseline
- 固定代码、镜像、模型权重和驱动版本。
- 保留 TP2/DP4、TP4/DP2、TP8/DP1 的 Shape Grid。
- 同时记录 TTFT、TPOT、ITL、E2E、请求吞吐、输入/输出/总 TPS。
- 记录实际成功请求的 Prompt/Output token避免只用配置长度估算 TPS。
- 增加 GPU、HBM、PCIe/NVLink/RDMA、CPU 利用率和服务日志。
### 阶段 B调度、缓存与并行
- 比较原生 DP 与 Custom DP。
- 对短上下文高并发和长上下文分别选择路由策略。
- 测试 Prefix Cache 命中率 0%、20%、50%、80%。
- 测试 GPU-only、GPU+CPU、GPU+CPU+Mooncake/KVStore。
- 对 MoE 分别测试 TP 主导、EP 主导和 Attention DP + MoE EP。
### 阶段 CProfiler 驱动的 Kernel 优化
- 用 Nsight Systems 找 GPU 空洞、CPU 调度气泡和通信等待。
- 用 Nsight Compute 找热点 Kernel 的访存、Occupancy 和 Tensor Core 利用率。
- 先尝试已有 BackendFlashInfer、FlashMLA、DeepGEMM、CUTLASS、Marlin、HPC-Ops。
- 只有现有 Backend 不覆盖关键 Shape 时,才值得自己写算子或提交 PR。
### 阶段 D模型相关优化
- MTP/DSpark/EAGLE。
- W4A8、Attention FP8、KV Cache 量化。
- Stem/DSA 等稀疏 Attention。
- 精度回归、Calibration 和必要的轻量训练。
## 12. 建议补充的实验矩阵
### 12.1 真实流量 Baseline
先构建一个与文章接近但适合当前模型的 Trace
- 输入长度按 1K、4K、16K、64K、128K、192K 分桶。
- 输出长度以 1K 左右为中心,同时保留短输出和长输出尾部。
- 混合长度请求一起进入服务。
- 每次测试至少数百到数千请求,保证 P95/P99 有意义。
### 12.2 Prefix Cache
| 变量 | 建议取值 |
|---|---|
| 前缀命中率 | 0%、20%、50%、80% |
| 前缀长度 | 4K、16K、64K、128K |
| Cache 层级 | GPU、GPU+CPU、GPU+CPU+L3 |
| 实例范围 | 单实例、跨实例 |
### 12.3 并行策略
| 阶段 | 候选策略 | 重点指标 |
|---|---|---|
| Prefill | TP8、TP/SP 混合、Chunked Prefill | TTFT、输入 TPS、通信时间 |
| Decode | TP、DP、Attention DP + MoE EP | TPOT、输出 TPS、负载均衡 |
| 多机 | TP/EP、DP/EP、PD 分离 | 网络流量、跨机长尾、容错 |
### 12.4 SLO 吞吐边界
不要只找 Total TPS 最大点。每个 Shape 应同时输出:
- 最大成功并发。
- Total TPS 峰值并发。
- 满足 TTFT SLO 的最大并发。
- 满足 TPOT SLO 的最大并发。
- 同时满足全部 SLO 的最大并发。
这些并发可能不是同一个点。
## 13. 阅读文章时需要记住的十个问题
1. 当前瓶颈属于 Prefill 还是 Decode
2. 是 Compute-bound、Memory-bound、Communication-bound还是 CPU-bound
3. 优化改变了计算量,还是只改变了数据搬运和重叠?
4. 收益对应什么 Batch、Shape、TP/DP/EP
5. 单算子收益在端到端占比是多少?
6. 是否依赖 NVLink/NVSwitch、RDMA、特定 GPU 架构?
7. 是否需要新权重、Calibration、QAT 或模型结构支持?
8. 是否改变数值结果或精度?
9. 对低并发、长上下文和混合长度是否仍成立?
10. 最终是否提高了满足 SLO 的吞吐,而不只是无约束峰值 TPS
## 14. 术语速查
| 术语 | 含义 |
|---|---|
| CTA | CUDA Thread BlockKernel 调度到 SM 的基本工作单元 |
| Tile | 对矩阵或序列任务做的固定粒度切块 |
| Split-KV | 将长 Attention 的 KV 维拆给多个 CTA再合并局部结果 |
| PDL | Programmatic Dependent Launch用于减少依赖 Kernel 之间的启动气泡 |
| SP | Sequence Parallel沿 token/sequence 维切分 |
| TPSP | Tensor Parallel 与 Sequence Parallel 的混合布局 |
| EPLB | Expert Parallel Load Balancing专家并行负载均衡 |
| TPOP | Time Per Output Token与 TPOT 接近,衡量连续吐字速度 |
| Prefix Cache | 复用相同前缀已经生成的 KV Cache |
| W8A8C8 | 权重、激活和缓存均使用 8-bit 的总体精度标记,具体格式需看实现 |
| W4A8 | 4-bit 权重、8-bit 激活 |
| MTP | Multi-Token Prediction一轮提出或预测多个后续 token |
| OAM | Output-Aware Metric用 Value 强度修正稀疏 Attention 选块分数 |
## 15. 延伸资料
- [原始知乎文章](https://zhuanlan.zhihu.com/p/2053138680768943935)
- [Hy3 Preview 官方仓库](https://github.com/Tencent-Hunyuan/Hy3-preview)
- [HPC-Ops](https://github.com/Tencent/hpc-ops)
- [AngelSlim](https://github.com/tencent/AngelSlim)
- [腾讯混元 AI Infra 新开源HPC-Ops 推理核心算子全面升级](https://developer.cloud.tencent.com/article/2688857)
- [Mooncake](https://github.com/kvcache-ai/Mooncake)
## 16. Mentor 结论
这篇文章可以作为我们后续推理优化工作的总地图,但学习顺序不要反过来。
当前最值得优先复刻的是:
1. 真实流量 + SLO 的 Benchmark 方法。
2. Prefill/Decode 分阶段分析。
3. TP/DP/EP 与混合长度负载的系统实验。
4. Prefix Cache 和多级缓存。
5. Profiler 驱动的 Backend 与融合优化。
量化、MTP 和稀疏 Attention 很有价值,但更依赖模型结构、精度评估和训练支持。等 Baseline、调度、并行与缓存做扎实之后再进入这些方向收益会更容易被正确测量也更容易形成有说服力的技术成果。