[Feat] add Phase 1 sanity gate
This commit is contained in:
parent
0d3dd86519
commit
75182c6ede
@ -1,5 +1,9 @@
|
|||||||
# sskj — 多平台大模型推理性能基准测试项目
|
# sskj — 多平台大模型推理性能基准测试项目
|
||||||
|
|
||||||
|
> **更新(2026-07-30 18:40:53 CST)**
|
||||||
|
>
|
||||||
|
> 为 DeepSeek-V4-Pro 双机 TP16 quick-map 的唯一入口新增 `CASE_IDS` 场景过滤和未知 Case 预检,可在完整九点实验前先跑 1K/32K Prefill 与 C1/C32 Decode 四点 Sanity;运行清单会记录实际过滤条件。同步精简阶段档案:正文只保留最终成功 Run 与有效结论,历史失败压缩到末尾经验教训。
|
||||||
|
>
|
||||||
> **更新(2026-07-30 17:54:30 CST)**
|
> **更新(2026-07-30 17:54:30 CST)**
|
||||||
>
|
>
|
||||||
> 为 DeepSeek-V4-Pro 双机 TP16 quick-map 加入 RDMA fail-closed 启动保护。唯一 Shell 入口现在只允许计算网 `eth0/eth3` 与其 RDMA HCA `mlx5_0/mlx5_3`,在两端预检并透传 `rdma_cm/uverbs0/uverbs3`,服务健康后必须从两端 NCCL INFO 日志证明 `NET/IB` 和两条 HCA 均已启用,否则 benchmark 不会开始。运行清单新增 RDMA 开关、强制校验和设备路径;语法、结果解析器单测、完整 dry-run 及非法网卡/HCA 负例均已通过,真机 NET/IB 验证与 Phase 1 重跑尚未执行。
|
> 为 DeepSeek-V4-Pro 双机 TP16 quick-map 加入 RDMA fail-closed 启动保护。唯一 Shell 入口现在只允许计算网 `eth0/eth3` 与其 RDMA HCA `mlx5_0/mlx5_3`,在两端预检并透传 `rdma_cm/uverbs0/uverbs3`,服务健康后必须从两端 NCCL INFO 日志证明 `NET/IB` 和两条 HCA 均已启用,否则 benchmark 不会开始。运行清单新增 RDMA 开关、强制校验和设备路径;语法、结果解析器单测、完整 dry-run 及非法网卡/HCA 负例均已通过,真机 NET/IB 验证与 Phase 1 重跑尚未执行。
|
||||||
|
|||||||
@ -237,21 +237,9 @@
|
|||||||
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
||||||
|
|
||||||
<p class="status">
|
<p class="status">
|
||||||
<strong>阶段状态:网络错误已定位,RDMA fail-closed 代码已完成,等待真机验证和重跑。</strong>
|
<strong>阶段状态:等待真实双 Rail RDMA 重跑。</strong>
|
||||||
第一次真机 Run
|
启动器与静态验证已完成;历史无效 Run 不进入最终分析。下一次仅在
|
||||||
<code>dsv4pro-pro6000d-2node-sglang-quick-20260730-140026</code>
|
NCCL 日志通过 <code>NET/IB</code> 门禁后执行 Sanity 和完整 quick map。
|
||||||
已验证双机服务可用,但因发现请求量和 Prefix Cache 口径问题而主动停止。
|
|
||||||
精简后的第二次 Run
|
|
||||||
<code>dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625</code>
|
|
||||||
完成 3 个 Prefill 固定点后曾观察到约 <code>65 token/s</code>。
|
|
||||||
后续控制组确认 quick-map 容器看不到 RDMA,NCCL 回退
|
|
||||||
<code>NET/Socket</code>,同时脚本把 Socket 接口设成了低速非计算网。
|
|
||||||
因此这 3 个结果不能作为模型、算子或 TP16 的性能基线,Phase 2 暂停,
|
|
||||||
当前入口已固定计算网 <code>eth0/eth3</code> 与
|
|
||||||
<code>mlx5_0/mlx5_3</code>,完成设备透传、日志强校验和 dry-run;
|
|
||||||
只有真机日志证明 <code>NET/IB</code> 后才会重跑 Phase 1。
|
|
||||||
Manifest 终态为 <code>ABORTED_EARLY_FOR_PHASE2</code>;
|
|
||||||
两节点容器和 16 张 GPU 已清理。
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>1. 目标与边界</h2>
|
<h2>1. 目标与边界</h2>
|
||||||
@ -265,7 +253,7 @@
|
|||||||
<li>只测试 SGLang,不测试 vLLM。</li>
|
<li>只测试 SGLang,不测试 vLLM。</li>
|
||||||
<li>使用双机 16 卡完整实例,不做 PD 分离。</li>
|
<li>使用双机 16 卡完整实例,不做 PD 分离。</li>
|
||||||
<li>不启用 MTP、EAGLE、DSpark 或其他投机解码。</li>
|
<li>不启用 MTP、EAGLE、DSpark 或其他投机解码。</li>
|
||||||
<li>本轮不启用 Profiler;受错误 Socket 网络影响的三个旧 Case 仅保留为事故证据,不再作为端到端基线。</li>
|
<li>本轮不启用 Profiler;正文只记录最终有效 Run,失败尝试仅在末尾总结经验。</li>
|
||||||
<li>不修改或调用旧的全天全量 Benchmark 脚本。</li>
|
<li>不修改或调用旧的全天全量 Benchmark 脚本。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -542,211 +530,41 @@ wait "${background_pid}"</code></pre>
|
|||||||
<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>真实旧 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>项目精简</td><td class="pass">通过</td><td>实验目录顶层仅保留一个 Shell 入口</td></tr>
|
||||||
<tr><td>双机容器启动</td><td class="pass">通过</td><td>第二次 Run 于 14:42:04 通过 Health Check,启动约 5 分 30 秒</td></tr>
|
<tr><td>RDMA fail-closed</td><td class="pass">静态验证通过</td><td>非法网卡、缺失设备或未出现 <code>NET/IB</code> 时禁止 benchmark</td></tr>
|
||||||
<tr><td>Prefix Cache 隔离</td><td class="pass">通过</td><td>Warm-up 后 <code>POST /flush_cache</code> 返回 200,正式请求仍为 <code>#cached-token: 0</code></td></tr>
|
|
||||||
<tr><td>中止清理</td><td class="pass">通过</td><td>头、Worker 节点均无相关容器和 Bench 进程,16 张 GPU 显存回到 0 MiB</td></tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>9. 真机结果</h2>
|
<h2>9. 最终真机结果</h2>
|
||||||
<p>
|
<p class="pending">
|
||||||
最终采用 Run
|
正确双 Rail RDMA 下的正式结果尚未生成。本节只接受通过传输门禁、
|
||||||
<code>dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625</code>。
|
冷缓存口径和结果校验的最终 Run;完成后将写入四点 Sanity、九个固定点、
|
||||||
以下三条均为一条请求、<code>C=1</code>、<code>OSL=1</code>,且正式测量前
|
混合 A/B 与阶段结论。
|
||||||
Prefix Cache 已清空。
|
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr><th>门禁或实验</th><th>状态</th><th>最终证据</th></tr>
|
||||||
<th>Case</th>
|
|
||||||
<th>ISL</th>
|
|
||||||
<th>输入 TPS</th>
|
|
||||||
<th>TTFT</th>
|
|
||||||
<th>E2E</th>
|
|
||||||
<th>状态</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td><code>short_prefill_latency_1k_c1</code></td><td>1K</td><td>64.44 tok/s</td><td>15.88 s</td><td>15.88 s</td><td class="pass">COMPLETED</td></tr>
|
<tr><td>双 Rail <code>NET/IB</code></td><td class="pending">待真机验证</td><td>头、Worker 日志均需识别 <code>mlx5_0/mlx5_3</code></td></tr>
|
||||||
<tr><td><code>mid_prefill_latency_32k_c1</code></td><td>32K</td><td>64.96 tok/s</td><td>504.44 s</td><td>504.44 s</td><td class="pass">COMPLETED</td></tr>
|
<tr><td>四点 Sanity</td><td class="pending">待执行</td><td>1K/32K Prefill 与 C1/C32 Decode</td></tr>
|
||||||
<tr><td><code>long_prefill_latency_128k_c1</code></td><td>128K</td><td>65.20 tok/s</td><td>2010.38 s</td><td>2010.38 s</td><td class="pass">COMPLETED</td></tr>
|
<tr><td>九个固定点</td><td class="pending">待执行</td><td>最终 <code>summary.csv</code> 与 <code>report.md</code></td></tr>
|
||||||
<tr><td><code>mid_prefill_throughput_32k_c16</code></td><td>32K × 16</td><td>未形成最终结果</td><td>未形成最终结果</td><td>未形成最终结果</td><td class="pending">ABORTED</td></tr>
|
<tr><td>混合 A/B</td><td class="pending">待执行</td><td>Control 与 Treatment 的 Decode 指标变化</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p class="status">
|
<h2>10. 经验教训</h2>
|
||||||
<strong>这些数值已被降级为“错误网络路径复现”。</strong>
|
|
||||||
它们可以证明低速 Socket 路径近似随输入长度线性增长,但不能用于判断
|
|
||||||
DeepSeek-V4-Pro、SGLang Kernel、GPU 算力或正确 TP16 数据面的性能。
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>9.1 对错误网络 Run 可以下的结论</h3>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>三个长度的输入吞吐只相差约 1.2%,稳定在 <code>64.44–65.20 token/s</code>。</li>
|
<li>启动参数不等于实际传输路径;开始性能测试前必须由 NCCL 日志证明 <code>NET/IB</code>。</li>
|
||||||
<li>32K TTFT 约为 1K 的 31.76 倍;128K TTFT 约为 32K 的 3.99 倍,几乎按 token 数线性增长。</li>
|
<li>Warm-up、固定随机种子和跨 Case Prefix Cache 会改变 TTFT,冷缓存与热缓存必须分开报告。</li>
|
||||||
<li>这不是偶发卡顿:服务端每约 125 秒完成一个 8192-token Chunk,GPU 在该期间持续忙碌。</li>
|
<li>传输门禁或测量口径不成立时,性能结果不能用于归因模型、调度器或 Kernel。</li>
|
||||||
<li><code>32K, C=16</code> 的观察窗口内,日志持续显示 <code>#new-seq: 1</code>,队列从 15 降到 14;至少当前路径没有立即把 16 条 Prefill 合成大批次。</li>
|
<li>完整实验前先跑少量 Sanity 点,能更早发现环境和口径问题。</li>
|
||||||
<li>没有出现 OOM、CUDA、EngineDead 或请求失败,但 NCCL 没有使用预期 RDMA 数据面。</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>9.2 现在还不能下的结论</h3>
|
|
||||||
<ul>
|
|
||||||
<li>仅凭 GPU Utilization=100% 不能判断是算力、显存带宽还是通信瓶颈。</li>
|
|
||||||
<li>不能用本轮断言 SGLang Bug、DSV4/NSA Kernel、MoE Backend 或 Scheduler 存在性能问题。</li>
|
|
||||||
<li>Decode、Balanced 和混合 A/B 未执行,Phase 1 不提供这些场景的基线。</li>
|
|
||||||
<li>每个 Shape 只有一次重复,不能用于稳定性或 CV 结论。</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>9.3 为什么提前结束</h3>
|
|
||||||
<p>
|
<p>
|
||||||
当时根据约 65 token/s 的稳定信号提前停止第 4 个 Case,并写入
|
历史排查细节保存在
|
||||||
<code>EARLY_STOP_FOR_PHASE2</code>。后续网络控制组表明这个停止动作仍然避免了
|
|
||||||
无效算力消耗,但调查方向需要修正:不是立即进入 Kernel/硬件归因,而是先校正
|
|
||||||
NCCL 数据面并重跑快速地图。
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>9.4 为什么旧脚本的 TTFT 短很多:缓存与网络两个因素</h3>
|
|
||||||
<p>
|
|
||||||
2026-07-30 对旧目录
|
|
||||||
<code>/data/qqt/sskj/experiments/pro6000/dsv4_pro6000_sglang_tp16</code>
|
|
||||||
做了逐项审计,并追加原网络配置冷请求控制组。最终结论是:
|
|
||||||
<strong>旧结果与 quick-map 既不是同一缓存口径,也不是同一 Socket 网络路径。</strong>
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>审计项</th><th>旧脚本</th><th>quick-map</th><th>判断</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>服务端配置</td>
|
|
||||||
<td>同一镜像,TP16 / EP2,8K Chunk,FlashInfer MXFP4 MoE</td>
|
|
||||||
<td>模型参数相同,但 NCCL/IP 接口不同</td>
|
|
||||||
<td>不能排除网络启动参数回归</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>正式请求数</td>
|
|
||||||
<td><code>C=1</code> 仍强制至少 10 条</td>
|
|
||||||
<td>延迟点只发 1 条</td>
|
|
||||||
<td>旧均值混合了多条请求的缓存状态</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Warm-up</td>
|
|
||||||
<td>每个 Shape 固定 16 条同 Prompt Warm-up</td>
|
|
||||||
<td>Warm-up 后清 Prefix Cache;32K/128K 延迟点不做额外 Warm-up</td>
|
|
||||||
<td>旧正式测量会继承 Warm-up 的 Prompt 前缀</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Cache 清理</td>
|
|
||||||
<td>从不传 <code>--flush-cache</code></td>
|
|
||||||
<td>正式测量前传 <code>--flush-cache</code></td>
|
|
||||||
<td>旧脚本跨 Case、跨长度保留 Radix/Prefix Cache</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Shape 顺序</td>
|
|
||||||
<td>固定 Seed=42,按 1K→4K→8K→16K→32K→64K→128K 递增</td>
|
|
||||||
<td>每个延迟点按冷缓存解释</td>
|
|
||||||
<td>旧请求会复用上一档相同 Prompt 的短前缀</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p>
|
|
||||||
旧时间线还有一条直接证据:17:40 的失败 Run 已完整执行过
|
|
||||||
<code>1K / 128 / C=1</code>,18:01 的正式 Run 没有重启服务便再次执行同一批
|
|
||||||
Seed=42 请求。因此旧文件中的 1K TTFT 约 0.455 秒,本身就是热缓存结果。
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>最小复现</th><th>Mean TTFT</th><th>P95 TTFT</th><th>解释</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>quick-map 错误网络,1K→1 冷缓存</td><td>15.88–16.04 s</td><td>15.88–16.04 s</td><td>低速非计算网 Socket 路径</td></tr>
|
|
||||||
<tr><td>旧原始网络,1K→1 冷缓存</td><td>1.458 s</td><td>1.458 s</td><td><code>eth0</code> 400G 物理端口上的 Socket 路径</td></tr>
|
|
||||||
<tr><td>旧原始网络,32K→1 冷缓存</td><td>38.062 s</td><td>38.062 s</td><td>比 quick-map 的 504.44 s 快约 13.25 倍</td></tr>
|
|
||||||
<tr><td>quick-map,1K→128 冷缓存</td><td>15.79 s</td><td>15.79 s</td><td>排除 OSL=1 特殊慢路径,但仍受错误网络影响</td></tr>
|
|
||||||
<tr><td>2026-07-28 旧产物</td><td>0.455 s</td><td>0.513 s</td><td>服务已被前一次 Run 和后续递增长度预热</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p>
|
|
||||||
旧 32K 文件的第一条请求约 0.67 秒,其余 9 条平均约 35.51 秒;
|
|
||||||
旧 128K 文件的第一条约 0.96 秒,其余 9 条平均约 144.83 秒。
|
|
||||||
后 9 条也已经分别继承上一档 16K、64K 前缀。旧报告仍按完整 ISL 统计
|
|
||||||
Input TPS,因此会把只计算新增后缀的耗时除进完整 token 数,进一步放大吞吐。
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
缓存审计原始产物保存在:
|
|
||||||
</p>
|
|
||||||
<pre><code>/data/hzy/dsv4_script_audit_20260730/</code></pre>
|
|
||||||
<p>
|
|
||||||
本地归档:
|
|
||||||
<a href="./results/script-audit-20260730/report.md">TTFT 脚本口径审计报告</a>
|
<a href="./results/script-audit-20260730/report.md">TTFT 脚本口径审计报告</a>
|
||||||
及同目录原始 JSON/log。
|
与
|
||||||
</p>
|
<a href="./results/network-path-audit-20260730/report.md">TP16 网络路径审计报告</a>,
|
||||||
|
不作为本阶段最终结果。
|
||||||
<h3>9.5 NCCL 数据面控制组</h3>
|
|
||||||
<p>
|
|
||||||
原脚本控制组使用其默认 <code>NCCL_SOCKET_IFNAME=eth0</code>,不注入
|
|
||||||
<code>NCCL_IB_HCA</code> 与 <code>NCCL_CROSS_NIC</code>。NCCL INFO 和容器设备
|
|
||||||
检查给出了直接证据:
|
|
||||||
</p>
|
|
||||||
<pre><code>NET/IB : No device found
|
|
||||||
NET/Socket : Using [0]eth0:10.101.0.11
|
|
||||||
Using network Socket
|
|
||||||
|
|
||||||
docker exec ... ls /dev/infiniband
|
|
||||||
# No such file or directory</code></pre>
|
|
||||||
<p>
|
|
||||||
宿主机本身存在 <code>uverbs0/uverbs3/rdma_cm</code>,且
|
|
||||||
<code>mlx5_0→eth0</code>、<code>mlx5_3→eth3</code> 均 Up。这说明问题位于
|
|
||||||
容器设备透传和脚本网卡选择,不是物理计算网缺失。quick-map 与原脚本使用相同
|
|
||||||
Docker 设备边界,却把 Socket 接口设成了低速非计算网,造成 10–13 倍退化。
|
|
||||||
在 <code>NET/Socket</code> 路径上,<code>NCCL_CROSS_NIC=1</code> 不参与选择。
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
修正后的唯一入口已加入三层门禁:两端字符设备预检、Docker 精确
|
|
||||||
<code>--device</code> 透传、健康后 NCCL INFO 传输后端验证。静态检查、
|
|
||||||
结果解析器单测和完整 dry-run 已通过;非法 <code>eth1</code> 或
|
|
||||||
<code>mlx5_1</code> 会在加载模型前被拒绝。该结论仍属于代码验证,
|
|
||||||
真机 <code>NET/IB</code> 成功证据要等下一次启动后补入。
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
网络控制组原始产物:
|
|
||||||
</p>
|
|
||||||
<pre><code>/data/hzy/dsv4_oldscript_cold_control_20260730/</code></pre>
|
|
||||||
<p>
|
|
||||||
本地归档:
|
|
||||||
<a href="./results/network-path-audit-20260730/report.md">TP16 网络路径审计报告</a>
|
|
||||||
及同目录 NCCL/benchmark 原始日志。
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>检查点</th>
|
|
||||||
<th>状态</th>
|
|
||||||
<th>结果或结论</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>服务健康</td><td class="pass">通过</td><td>端口可用且无 OOM/Engine 异常,但 NCCL 数据面未按预期进入 RDMA</td></tr>
|
|
||||||
<tr><td>第一次固定点 Run</td><td class="pending">主动停止</td><td>发现 32K 单请求约需十余分钟;原协议的 4 次同形状请求会使整轮再次接近半天</td></tr>
|
|
||||||
<tr><td>RDMA fail-closed 启动器</td><td class="pass">静态验证通过</td><td>仅允许两条计算网 Rail;设备透传、日志强校验与运行清单已加入</td></tr>
|
|
||||||
<tr><td>精简后九个固定点</td><td class="pending">旧 Run 作废,待重跑</td><td>先完成真机 NET/IB 验证,再生成新的性能基线</td></tr>
|
|
||||||
<tr><td>混合干扰 A/B</td><td class="pending">未执行</td><td>待 Prefill 根因明确后再决定是否重放</td></tr>
|
|
||||||
<tr><td>阶段耗时</td><td class="pass">约 65 分钟</td><td>14:36:26 启动,15:41:52 完成进程与容器清理</td></tr>
|
|
||||||
<tr><td>是否进入下一阶段</td><td class="pending">否</td><td>Phase 2 暂停;先证明双 Rail NET/IB 并重跑 Phase 1</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h2>10. 结果位置</h2>
|
|
||||||
<p>服务器原始结果:</p>
|
|
||||||
<pre><code>/data/hzy/sskj/experiments/pro6000/
|
|
||||||
dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/
|
|
||||||
dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625/</code></pre>
|
|
||||||
<p>
|
|
||||||
本地已归档
|
|
||||||
<a href="./results/phase1-dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625/report.md">report.md</a>、
|
|
||||||
<a href="./results/phase1-dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625/aggregate.csv">aggregate.csv</a>
|
|
||||||
和同目录下的 Manifest、Summary、主日志。
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>11. 运行命令</h2>
|
<h2>11. 运行命令</h2>
|
||||||
|
|||||||
@ -415,7 +415,7 @@
|
|||||||
<article id="document-content">
|
<article id="document-content">
|
||||||
<h1>6000D 双机 DeepSeek-V4-Pro 推理优化计划</h1>
|
<h1>6000D 双机 DeepSeek-V4-Pro 推理优化计划</h1>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>适用环境:<code>174.1.51.5 + 174.1.51.7</code>,每台 8 张 RTX PRO 6000 Blackwell Server Edition<br>当前部署:DeepSeek-V4-Pro,16 张 GPU 组成一个完整实例<br>当前约束:模型暂时只能使用全部 16 张 GPU,无法额外复制一套模型进行 PD 分离<br>计划版本:2026-07-30 17:54 CST</p>
|
<p>适用环境:<code>174.1.51.5 + 174.1.51.7</code>,每台 8 张 RTX PRO 6000 Blackwell Server Edition<br>当前部署:DeepSeek-V4-Pro,16 张 GPU 组成一个完整实例<br>当前约束:模型暂时只能使用全部 16 张 GPU,无法额外复制一套模型进行 PD 分离<br>计划版本:2026-07-30 18:40 CST</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<h2>当前执行状态与阶段档案</h2>
|
<h2>当前执行状态与阶段档案</h2>
|
||||||
<table>
|
<table>
|
||||||
@ -443,6 +443,10 @@
|
|||||||
<td><a href="./phase2_dsv4pro_pro6000d_2node_sglang_prefill_hardware_attribution.html">打开 Phase 2 档案</a></td>
|
<td><a href="./phase2_dsv4pro_pro6000d_2node_sglang_prefill_hardware_attribution.html">打开 Phase 2 档案</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
<p>
|
||||||
|
<strong>阶段档案规范:</strong>正文只保留最终成功实验、有效结果和结论;
|
||||||
|
失败尝试压缩到末尾的经验教训。阶段完成时删除“暂不能下结论”等过渡内容。
|
||||||
|
</p>
|
||||||
<h2>0. 先看懂双机通信</h2>
|
<h2>0. 先看懂双机通信</h2>
|
||||||
<p>
|
<p>
|
||||||
在分析 TP16 性能前,先区分设备、传输后端与 NCCL 参数。
|
在分析 TP16 性能前,先区分设备、传输后端与 NCCL 参数。
|
||||||
@ -471,14 +475,6 @@
|
|||||||
<code>400 Gbit/s ≈ 50 GB/s</code> 只是单向理论上限,NCCL 的
|
<code>400 Gbit/s ≈ 50 GB/s</code> 只是单向理论上限,NCCL 的
|
||||||
<code>algbw/busbw</code> 与端到端模型吞吐都不能直接等同于该数字。
|
<code>algbw/busbw</code> 与端到端模型吞吐都不能直接等同于该数字。
|
||||||
</p>
|
</p>
|
||||||
<blockquote>
|
|
||||||
<p>
|
|
||||||
2026-07-30 已确认:当时容器内没有 <code>/dev/infiniband</code>,NCCL 日志显示
|
|
||||||
<code>NET/IB : No device found</code> 并回退 <code>NET/Socket</code>。quick-map 又误选
|
|
||||||
低速非计算网,因此 1K/32K 冷 Prefill 比原脚本的 <code>eth0</code> Socket 路径慢约
|
|
||||||
10.9×/13.25×。这不是 <code>NCCL_CROSS_NIC=1</code> 导致的。
|
|
||||||
</p>
|
|
||||||
</blockquote>
|
|
||||||
<h2>1. 目标与原则</h2>
|
<h2>1. 目标与原则</h2>
|
||||||
<h3>1.1 最终目标</h3>
|
<h3>1.1 最终目标</h3>
|
||||||
<p>在不做 PD 分离的前提下,定位 DeepSeek-V4-Pro 在双机 6000D 上的端到端瓶颈,并提高:</p>
|
<p>在不做 PD 分离的前提下,定位 DeepSeek-V4-Pro 在双机 6000D 上的端到端瓶颈,并提高:</p>
|
||||||
|
|||||||
@ -68,6 +68,17 @@ DRY_RUN=1 bash run_quick_map.sh all
|
|||||||
The launcher refuses to start while either node has an active GPU compute
|
The launcher refuses to start while either node has an active GPU compute
|
||||||
process. `ALLOW_BUSY_GPU=1` exists only for deliberate operator override.
|
process. `ALLOW_BUSY_GPU=1` exists only for deliberate operator override.
|
||||||
|
|
||||||
|
Before the complete run, use the same entry for a four-point Sanity pass:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CASE_IDS="short_prefill_latency_1k_c1,mid_prefill_latency_32k_c1,decode_latency_1k_to_1k_c1,decode_throughput_1k_to_1k_c32" \
|
||||||
|
DRY_RUN=1 bash run_quick_map.sh fixed
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove `DRY_RUN=1` only after the TP16 service has passed the `NET/IB` startup
|
||||||
|
gate. An unknown `CASE_IDS` value fails before any benchmark request is sent.
|
||||||
|
Leave `CASE_IDS` empty to run all nine fixed points.
|
||||||
|
|
||||||
## GPU run
|
## GPU run
|
||||||
|
|
||||||
Run the complete quick map in `tmux` after both nodes are free:
|
Run the complete quick map in `tmux` after both nodes are free:
|
||||||
|
|||||||
@ -70,5 +70,9 @@ SCENARIO_FILE="${SCENARIO_FILE:-${SCRIPT_DIR}/quick_map_scenarios.tsv}"
|
|||||||
RESULT_BASE="${RESULT_BASE:-${SCRIPT_DIR}/results}"
|
RESULT_BASE="${RESULT_BASE:-${SCRIPT_DIR}/results}"
|
||||||
RUNTIME_BASE="${RUNTIME_BASE:-${SCRIPT_DIR}/runtime}"
|
RUNTIME_BASE="${RUNTIME_BASE:-${SCRIPT_DIR}/runtime}"
|
||||||
|
|
||||||
|
# Optional comma- or space-separated subset of quick_map_scenarios.tsv.
|
||||||
|
# Leave empty for the complete nine-point fixed suite.
|
||||||
|
CASE_IDS="${CASE_IDS:-}"
|
||||||
|
|
||||||
DRY_RUN="${DRY_RUN:-0}"
|
DRY_RUN="${DRY_RUN:-0}"
|
||||||
RESUME="${RESUME:-1}"
|
RESUME="${RESUME:-1}"
|
||||||
|
|||||||
@ -369,6 +369,7 @@ def write_manifest(args: argparse.Namespace) -> None:
|
|||||||
"git_commit": args.git_commit,
|
"git_commit": args.git_commit,
|
||||||
"git_dirty": bool(args.git_dirty),
|
"git_dirty": bool(args.git_dirty),
|
||||||
"scenario_file": args.scenario_file,
|
"scenario_file": args.scenario_file,
|
||||||
|
"case_ids": args.case_ids,
|
||||||
"notes": [
|
"notes": [
|
||||||
"The fixed quick map does not stop on SLO.",
|
"The fixed quick map does not stop on SLO.",
|
||||||
"Profiler is disabled; these results are eligible for performance comparison.",
|
"Profiler is disabled; these results are eligible for performance comparison.",
|
||||||
@ -649,6 +650,7 @@ def add_manifest_arguments(parser: argparse.ArgumentParser) -> None:
|
|||||||
parser.add_argument("--git-commit", required=True)
|
parser.add_argument("--git-commit", required=True)
|
||||||
parser.add_argument("--git-dirty", type=int, required=True)
|
parser.add_argument("--git-dirty", type=int, required=True)
|
||||||
parser.add_argument("--scenario-file", required=True)
|
parser.add_argument("--scenario-file", required=True)
|
||||||
|
parser.add_argument("--case-ids", default="")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|||||||
@ -55,6 +55,33 @@ run_on_node() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case_selected() {
|
||||||
|
local candidate="$1"
|
||||||
|
local normalized="${CASE_IDS//,/ }"
|
||||||
|
local selected
|
||||||
|
[[ -z "${normalized//[[:space:]]/}" ]] && return 0
|
||||||
|
for selected in ${normalized}; do
|
||||||
|
[[ "${selected}" == "${candidate}" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_case_filter() {
|
||||||
|
local normalized="${CASE_IDS//,/ }"
|
||||||
|
local selected
|
||||||
|
[[ -z "${normalized//[[:space:]]/}" ]] && return 0
|
||||||
|
|
||||||
|
for selected in ${normalized}; do
|
||||||
|
if ! awk -F $'\t' -v case_id="${selected}" \
|
||||||
|
'$1 == case_id { found = 1 } END { exit !found }' \
|
||||||
|
"${SCENARIO_FILE}"; then
|
||||||
|
log "ERROR: CASE_IDS contains an unknown case: ${selected}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
log "Selected fixed cases: ${normalized}"
|
||||||
|
}
|
||||||
|
|
||||||
validate_network_config() {
|
validate_network_config() {
|
||||||
case "${ENABLE_RDMA}" in
|
case "${ENABLE_RDMA}" in
|
||||||
0|1) ;;
|
0|1) ;;
|
||||||
@ -644,7 +671,8 @@ write_run_manifest() {
|
|||||||
--rdma-device-paths "${RDMA_DEVICE_PATHS}" \
|
--rdma-device-paths "${RDMA_DEVICE_PATHS}" \
|
||||||
--git-commit "${git_commit}" \
|
--git-commit "${git_commit}" \
|
||||||
--git-dirty "${git_dirty}" \
|
--git-dirty "${git_dirty}" \
|
||||||
--scenario-file "${SCENARIO_FILE}"
|
--scenario-file "${SCENARIO_FILE}" \
|
||||||
|
--case-ids "${CASE_IDS}"
|
||||||
}
|
}
|
||||||
|
|
||||||
summarize_results() {
|
summarize_results() {
|
||||||
@ -667,12 +695,14 @@ enable_result_logging() {
|
|||||||
run_fixed_suite() {
|
run_fixed_suite() {
|
||||||
log "Validating scenario file: ${SCENARIO_FILE}"
|
log "Validating scenario file: ${SCENARIO_FILE}"
|
||||||
python3 "${RESULT_TOOL}" validate-scenarios "${SCENARIO_FILE}"
|
python3 "${RESULT_TOOL}" validate-scenarios "${SCENARIO_FILE}"
|
||||||
|
validate_case_filter
|
||||||
|
|
||||||
local completed=0 total=0
|
local completed=0 total=0
|
||||||
local rep case_id stage isl osl concurrency multiplier minimum warmup note
|
local rep case_id stage isl osl concurrency multiplier minimum warmup note
|
||||||
for (( rep=1; rep<=NUM_REPETITIONS; rep++ )); do
|
for (( rep=1; rep<=NUM_REPETITIONS; rep++ )); do
|
||||||
while IFS=$'\t' read -r case_id stage isl osl concurrency multiplier minimum warmup note; do
|
while IFS=$'\t' read -r case_id stage isl osl concurrency multiplier minimum warmup note; do
|
||||||
[[ -z "${case_id}" || "${case_id}" == \#* ]] && continue
|
[[ -z "${case_id}" || "${case_id}" == \#* ]] && continue
|
||||||
|
case_selected "${case_id}" || continue
|
||||||
|
|
||||||
[[ -n "${PROMPT_MULTIPLIER_OVERRIDE}" ]] && multiplier="${PROMPT_MULTIPLIER_OVERRIDE}"
|
[[ -n "${PROMPT_MULTIPLIER_OVERRIDE}" ]] && multiplier="${PROMPT_MULTIPLIER_OVERRIDE}"
|
||||||
[[ -n "${MIN_NUM_PROMPTS_OVERRIDE}" ]] && minimum="${MIN_NUM_PROMPTS_OVERRIDE}"
|
[[ -n "${MIN_NUM_PROMPTS_OVERRIDE}" ]] && minimum="${MIN_NUM_PROMPTS_OVERRIDE}"
|
||||||
@ -698,6 +728,10 @@ run_fixed_suite() {
|
|||||||
[[ "${DRY_RUN}" == "1" ]] || sleep "${CASE_COOLDOWN_S}"
|
[[ "${DRY_RUN}" == "1" ]] || sleep "${CASE_COOLDOWN_S}"
|
||||||
done < "${SCENARIO_FILE}"
|
done < "${SCENARIO_FILE}"
|
||||||
done
|
done
|
||||||
|
if (( total == 0 )); then
|
||||||
|
log "ERROR: fixed suite selected zero cases"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
log "Fixed quick map complete: completed=${completed}/${total} failed=${FIXED_FAILURES}"
|
log "Fixed quick map complete: completed=${completed}/${total} failed=${FIXED_FAILURES}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,6 +882,10 @@ run_standalone_suite() {
|
|||||||
|
|
||||||
run_all() {
|
run_all() {
|
||||||
SERVER_ARTIFACT_DIR="${RESULT_DIR}/server"
|
SERVER_ARTIFACT_DIR="${RESULT_DIR}/server"
|
||||||
|
log "Validating scenario file before service startup: ${SCENARIO_FILE}"
|
||||||
|
python3 "${RESULT_TOOL}" validate-scenarios "${SCENARIO_FILE}"
|
||||||
|
validate_case_filter
|
||||||
|
|
||||||
if [[ "${DRY_RUN}" != "1" ]]; then
|
if [[ "${DRY_RUN}" != "1" ]]; then
|
||||||
preflight_bench_client
|
preflight_bench_client
|
||||||
enable_result_logging
|
enable_result_logging
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user