[BugFix] enforce RDMA transport for DSV4-Pro TP16 quick map
This commit is contained in:
parent
595bdde5d7
commit
0d3dd86519
@ -1,5 +1,13 @@
|
|||||||
# sskj — 多平台大模型推理性能基准测试项目
|
# sskj — 多平台大模型推理性能基准测试项目
|
||||||
|
|
||||||
|
> **更新(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 重跑尚未执行。
|
||||||
|
>
|
||||||
|
> **更新(2026-07-30 17:45:18 CST)**
|
||||||
|
>
|
||||||
|
> 修正 DeepSeek-V4-Pro 双机 TP16 quick-map 的 NCCL Socket 网卡错误。控制组确认服务容器未暴露 `/dev/infiniband`,NCCL 实际回退 `NET/Socket`;旧 quick-map 又误选低速非计算网,导致冷 1K/32K Prefill 比 `eth0` 计算网 Socket 控制组慢约 10.9 倍/13.25 倍。默认 `NCCL_SOCKET_IFNAME` 已改为 `eth0`;旧约 65 token/s 结果降级为事故证据,Phase 2 暂停并等待修正后的 Phase 1。新增双机通信/NCCL 术语 HTML、网络审计报告,并保留原 `/data/qqt/sskj` TP16 脚本不变。
|
||||||
|
>
|
||||||
> **更新(2026-07-30 16:38:41 CST)**
|
> **更新(2026-07-30 16:38:41 CST)**
|
||||||
>
|
>
|
||||||
> 完成 DeepSeek-V4-Pro 双机 TP16 新旧脚本 TTFT 口径审计。确认旧产物受到 16 条 Warm-up、跨 Case 固定 Seed 递增长度、未清 Prefix Cache 及前一轮残留服务状态影响;同配置冷请求稳定复现约 16 秒/1K。新增 Phase 1 结果、脚本审计和 Phase 2 设计 HTML 档案,后续 Cold/Warm Prefix 指标分开报告。
|
> 完成 DeepSeek-V4-Pro 双机 TP16 新旧脚本 TTFT 口径审计。确认旧产物受到 16 条 Warm-up、跨 Case 固定 Seed 递增长度、未清 Prefix Cache 及前一轮残留服务状态影响;同配置冷请求稳定复现约 16 秒/1K。新增 Phase 1 结果、脚本审计和 Phase 2 设计 HTML 档案,后续 Cold/Warm Prefix 指标分开报告。
|
||||||
|
|||||||
696
docs/dsv4pro_pro6000d_2node_sglang/6000D双机通信与NCCL术语入门.html
Normal file
696
docs/dsv4pro_pro6000d_2node_sglang/6000D双机通信与NCCL术语入门.html
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>6000D 双机通信与 NCCL 术语入门</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
--ink: #17202a;
|
||||||
|
--muted: #5f6b76;
|
||||||
|
--line: #d9dee3;
|
||||||
|
--panel: #f5f7f8;
|
||||||
|
--cyan: #087b83;
|
||||||
|
--cyan-soft: #e5f4f3;
|
||||||
|
--orange: #a64b17;
|
||||||
|
--orange-soft: #fff0e5;
|
||||||
|
--green: #287a45;
|
||||||
|
--green-soft: #eaf6ee;
|
||||||
|
--red: #a73535;
|
||||||
|
--red-soft: #fdecec;
|
||||||
|
--code: #f0f2f4;
|
||||||
|
--max: 1160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html { scroll-behavior: smooth; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ink);
|
||||||
|
background: #fff;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
||||||
|
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.72;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
color: #fff;
|
||||||
|
background: #172f35;
|
||||||
|
border-bottom: 5px solid #e57932;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-inner {
|
||||||
|
max-width: var(--max);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 46px 28px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
color: #9ed8d5;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 { letter-spacing: 0; }
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
max-width: 850px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(32px, 5vw, 54px);
|
||||||
|
line-height: 1.12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 22px;
|
||||||
|
margin-top: 22px;
|
||||||
|
color: #d4e4e5;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 240px minmax(0, 1fr);
|
||||||
|
gap: 38px;
|
||||||
|
max-width: var(--max);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 34px 28px 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
|
align-self: start;
|
||||||
|
padding-right: 20px;
|
||||||
|
border-right: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
display: block;
|
||||||
|
padding: 6px 0;
|
||||||
|
color: #40515a;
|
||||||
|
font-size: 14px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover { color: var(--cyan); }
|
||||||
|
|
||||||
|
main { min-width: 0; }
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 48px 0 16px;
|
||||||
|
padding-top: 8px;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 1.25;
|
||||||
|
border-top: 2px solid var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:first-child { margin-top: 0; }
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 30px 0 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
p { margin: 10px 0; }
|
||||||
|
|
||||||
|
a { color: var(--cyan); }
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: var(--code);
|
||||||
|
font-family: "SFMono-Regular", Consolas, monospace;
|
||||||
|
font-size: .92em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 14px 0;
|
||||||
|
padding: 16px 18px;
|
||||||
|
color: #e8f1f1;
|
||||||
|
background: #1c292d;
|
||||||
|
border-left: 4px solid #4cb3ae;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
padding: 0;
|
||||||
|
color: inherit;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 16px 0 24px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 11px 12px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: #26363e;
|
||||||
|
background: #eef1f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol { padding-left: 24px; }
|
||||||
|
|
||||||
|
.note, .warning, .finding, .good {
|
||||||
|
margin: 18px 0;
|
||||||
|
padding: 15px 18px;
|
||||||
|
border-left: 4px solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note { background: var(--cyan-soft); border-color: var(--cyan); }
|
||||||
|
.warning { background: var(--orange-soft); border-color: var(--orange); }
|
||||||
|
.finding { background: var(--red-soft); border-color: var(--red); }
|
||||||
|
.good { background: var(--green-soft); border-color: var(--green); }
|
||||||
|
|
||||||
|
.path {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
margin: 18px 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.path div {
|
||||||
|
min-height: 108px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-top: 4px solid var(--cyan);
|
||||||
|
background: var(--panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
.path b { display: block; margin-bottom: 5px; }
|
||||||
|
|
||||||
|
.split {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 18px;
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split section {
|
||||||
|
padding: 16px 18px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split h3 { margin-top: 0; }
|
||||||
|
|
||||||
|
.metric {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 26px 28px 40px;
|
||||||
|
color: var(--muted);
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.layout { grid-template-columns: 1fr; }
|
||||||
|
nav {
|
||||||
|
position: static;
|
||||||
|
padding: 0 0 18px;
|
||||||
|
border-right: 0;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
nav a { display: inline-block; margin-right: 14px; }
|
||||||
|
.path { grid-template-columns: 1fr; }
|
||||||
|
.split { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
nav { display: none; }
|
||||||
|
.layout { display: block; max-width: none; }
|
||||||
|
header { color: #000; background: #fff; border-bottom-color: #000; }
|
||||||
|
.header-meta, .eyebrow { color: #333; }
|
||||||
|
pre { color: #000; background: #f4f4f4; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="header-inner">
|
||||||
|
<p class="eyebrow">Two-node communication primer</p>
|
||||||
|
<h1>6000D 双机通信与 NCCL 术语入门</h1>
|
||||||
|
<div class="header-meta">
|
||||||
|
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
||||||
|
<span>规模:16 GPU / TP16</span>
|
||||||
|
<span>版本:2026-07-30 17:54 CST</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
<nav aria-label="目录">
|
||||||
|
<strong>阅读顺序</strong>
|
||||||
|
<a href="#mental-model">1. 一张总图</a>
|
||||||
|
<a href="#names">2. 设备名字</a>
|
||||||
|
<a href="#glossary">3. 核心术语</a>
|
||||||
|
<a href="#bootstrap">4. Bootstrap</a>
|
||||||
|
<a href="#parameters">5. NCCL 参数</a>
|
||||||
|
<a href="#logs">6. 日志怎么读</a>
|
||||||
|
<a href="#incident">7. 本次事故</a>
|
||||||
|
<a href="#checklist">8. 排查清单</a>
|
||||||
|
<a href="#sources">9. 官方资料</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h2 id="mental-model">1. 先建立一张总图</h2>
|
||||||
|
<p>
|
||||||
|
SGLang 不会自己搬运 16 张 GPU 之间的 Tensor。模型代码发起 TP/MoE 通信,
|
||||||
|
NCCL 决定用什么算法、经过哪条链路把数据送到其他 rank。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="path" aria-label="通信路径">
|
||||||
|
<div><b>SGLang</b>执行模型层、TP16 和 EP2</div>
|
||||||
|
<div><b>Collective</b>AllReduce、AllGather、ReduceScatter、AllToAll</div>
|
||||||
|
<div><b>NCCL</b>构造 rank、ring/tree 和 channel</div>
|
||||||
|
<div><b>Transport</b>机内 P2P/IPC;跨机 NET/IB 或 NET/Socket</div>
|
||||||
|
<div><b>硬件</b>GPU、PCIe、HCA、网卡、光模块、交换机</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<strong>最重要的区分:</strong>
|
||||||
|
<code>NCCL bootstrap</code> 是“启动时要完成的一件事”;
|
||||||
|
<code>NCCL_SOCKET_IFNAME</code> 是“选择 IP 网卡的一个参数”;
|
||||||
|
<code>NET/IB</code> 和 <code>NET/Socket</code> 才是 NCCL 实际搬运数据的传输后端。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>两类跨机路径</h3>
|
||||||
|
<div class="split">
|
||||||
|
<section>
|
||||||
|
<h3>理想路径:RDMA</h3>
|
||||||
|
<p><code>GPU → HCA → RoCE 网络 → HCA → GPU</code></p>
|
||||||
|
<p>日志应出现 <code>NET/IB</code>,支持时还会出现 <code>GDRDMA</code>。</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>回退路径:TCP Socket</h3>
|
||||||
|
<p><code>GPU/CPU → Linux Socket → ethX → TCP/IP → ethX</code></p>
|
||||||
|
<p>日志会出现 <code>Using network Socket</code>。这不是报错,但性能通常低得多。</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 id="names">2. eth0 和 mlx5_0 不是同一个设备</h2>
|
||||||
|
<div class="note">
|
||||||
|
<strong>400G 是物理 Ethernet 端口的标称链路速率。</strong>
|
||||||
|
<code>eth0</code> 是该端口的 Linux netdev/IP 入口;
|
||||||
|
<code>mlx5_0</code> 是映射到该端口的 RDMA Verbs/HCA 入口。
|
||||||
|
二者相关联,但不相等,也不代表 TCP 或 RDMA 应用一定能跑到 400G。
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>名字</th><th>属于哪一层</th><th>负责什么</th><th>本机实例</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>eth0</code></td>
|
||||||
|
<td>Linux IP 网卡接口</td>
|
||||||
|
<td>配置 IP、TCP/UDP、路由;由 <code>NCCL_SOCKET_IFNAME</code> 选择</td>
|
||||||
|
<td>400 Gbit/s 计算网</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>eth3</code></td>
|
||||||
|
<td>Linux IP 网卡接口</td>
|
||||||
|
<td>第二条计算网 Rail</td>
|
||||||
|
<td>400 Gbit/s 计算网</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>mlx5_0</code></td>
|
||||||
|
<td>RDMA HCA / Verbs 设备</td>
|
||||||
|
<td>供 <code>NET/IB</code> 使用;由 <code>NCCL_IB_HCA</code> 选择</td>
|
||||||
|
<td>对应 <code>eth0</code>,挂 switch 1</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>mlx5_3</code></td>
|
||||||
|
<td>RDMA HCA / Verbs 设备</td>
|
||||||
|
<td>第二条 RDMA Rail</td>
|
||||||
|
<td>对应 <code>eth3</code>,挂 switch 2</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/dev/infiniband/uverbs0</code></td>
|
||||||
|
<td>Linux 字符设备</td>
|
||||||
|
<td>容器进程访问 RDMA Verbs 的入口</td>
|
||||||
|
<td>对应 <code>mlx5_0</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<pre><code>mlx5_0 port 1 ==> eth0 (Up)
|
||||||
|
mlx5_3 port 1 ==> eth3 (Up)</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
同一条物理端口可以同时暴露 Linux IP 接口和 RDMA HCA。
|
||||||
|
<code>eth0</code> 是 IP/Socket 世界的入口,<code>mlx5_0</code> 是 RDMA
|
||||||
|
Verbs 世界的入口。<code>ibdev2netdev</code> 输出的是映射关系,不是等号。
|
||||||
|
本项目部署时只把 <code>eth0/eth3</code> 作为节点间计算网。
|
||||||
|
</p>
|
||||||
|
<pre><code>同一条 400G 物理 Ethernet 端口
|
||||||
|
├── eth0 -> Linux netdev -> IP / TCP Socket
|
||||||
|
└── mlx5_0 -> RDMA HCA -> RoCE / Verbs / GDRDMA</code></pre>
|
||||||
|
<p>
|
||||||
|
<code>400 Gbit/s = 50 GB/s</code> 只是单方向理论线速。协议开销、PCIe、
|
||||||
|
CPU、Socket 线程、消息大小和 collective 算法都会让实际
|
||||||
|
<code>algbw/busbw</code> 低于或采用不同统计口径。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="glossary">3. 核心术语字典</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>术语</th><th>通俗解释</th><th>在本项目中的意义</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><strong>NCCL</strong></td>
|
||||||
|
<td>NVIDIA 的多 GPU 通信库,负责高效实现 collective 和点对点通信。</td>
|
||||||
|
<td>SGLang TP16 每层跨 GPU 通信最终大量落到 NCCL。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>rank</strong></td>
|
||||||
|
<td>一个通信参与者的编号。TP16 communicator 有 rank 0–15。</td>
|
||||||
|
<td>两台机器各 8 个 GPU rank,共 16 个。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>collective</strong></td>
|
||||||
|
<td>一组 rank 共同参与的通信操作。</td>
|
||||||
|
<td>TP 常见 AllReduce、AllGather、ReduceScatter;MoE 还可能有 AllToAll。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>RDMA</strong></td>
|
||||||
|
<td>远端直接内存访问。网卡可直接读写远端内存,减少 CPU 和内核数据拷贝。</td>
|
||||||
|
<td>双机 TP16 希望使用的高速数据路径。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>IB</strong></td>
|
||||||
|
<td>InfiniBand。既是一套高速网络体系,也常被 NCCL 用作 Verbs/RDMA 后端的统称。</td>
|
||||||
|
<td>NCCL 日志里的 <code>NET/IB</code> 也可承载 RoCE,不代表交换机一定是原生 IB。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>RoCE</strong></td>
|
||||||
|
<td>RDMA over Converged Ethernet,在以太网上承载 RDMA。</td>
|
||||||
|
<td>本项目的 400G 计算网类型。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>HCA</strong></td>
|
||||||
|
<td>Host Channel Adapter,提供 RDMA 能力的适配器。</td>
|
||||||
|
<td><code>mlx5_0</code>、<code>mlx5_3</code>。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>GDRDMA</strong></td>
|
||||||
|
<td>GPUDirect RDMA,让 HCA 直接访问 GPU 显存,减少经 CPU 内存中转。</td>
|
||||||
|
<td>跨机 GPU 通信的理想路径,日志可见 <code>via NET/IB/.../GDRDMA</code>。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Socket / TCP</strong></td>
|
||||||
|
<td>普通 IP 网络编程路径。NCCL 找不到 RDMA 时会使用。</td>
|
||||||
|
<td>本次脚本实际发生的回退路径。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Rail</strong></td>
|
||||||
|
<td>一条相对独立的网络通道,通常由一张 HCA 和一套交换路径组成。</td>
|
||||||
|
<td><code>mlx5_0/switch 1</code> 与 <code>mlx5_3/switch 2</code> 是双 Rail。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ring / tree</strong></td>
|
||||||
|
<td>NCCL 对 collective 的通信拓扑组织方式。</td>
|
||||||
|
<td><code>NCCL_CROSS_NIC</code> 决定同一 ring/tree 能否跨不同 NIC。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>PFC / ECN</strong></td>
|
||||||
|
<td>RoCE 网络控制拥塞和丢包的机制。</td>
|
||||||
|
<td>RDMA 出现 retry、pause 或吞吐抖动时由运维检查。</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2 id="bootstrap">4. NCCL bootstrap 到底是什么</h2>
|
||||||
|
<p>
|
||||||
|
NCCL 本身不是进程启动器。SGLang 先启动各个 worker,NCCL communicator 初始化时,
|
||||||
|
rank 之间需要交换地址、唯一 ID、拓扑和连接信息,这段“先认识彼此”的过程就是 bootstrap。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>每个 rank 启动并获得自己的 rank ID。</li>
|
||||||
|
<li>通过 IP Socket 交换 NCCL unique ID 和连接信息。</li>
|
||||||
|
<li>NCCL 探测 GPU、PCIe、HCA 和节点拓扑。</li>
|
||||||
|
<li>构造 ring/tree/channel。</li>
|
||||||
|
<li>选择真正的数据传输后端:P2P、SHM、NET/IB 或 NET/Socket。</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
<strong>容易误解的地方:</strong>
|
||||||
|
<code>NCCL_SOCKET_IFNAME</code> 不保证“只用于 bootstrap”。
|
||||||
|
RDMA 正常时它主要承担 bootstrap;RDMA 失败并回退 Socket 后,它也会决定大块 Tensor
|
||||||
|
数据走哪张 IP 网卡。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 id="parameters">5. 常见 NCCL 参数</h2>
|
||||||
|
|
||||||
|
<h3>NCCL_SOCKET_IFNAME</h3>
|
||||||
|
<p>筛选 NCCL 可使用的 Linux IP 接口。精确指定接口时可写:</p>
|
||||||
|
<pre><code>NCCL_SOCKET_IFNAME="=eth0"</code></pre>
|
||||||
|
<ul>
|
||||||
|
<li>RDMA 正常:主要影响 bootstrap/OOB IP 连接。</li>
|
||||||
|
<li>RDMA 不可用:决定 <code>NET/Socket</code> 的数据网卡。</li>
|
||||||
|
<li>部署时只允许使用计算网 <code>eth0/eth3</code>;Socket 回退时不能落到其他接口。</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>NCCL_IB_HCA</h3>
|
||||||
|
<p>筛选 NCCL 的 RDMA HCA。推荐使用精确匹配:</p>
|
||||||
|
<pre><code>NCCL_IB_HCA="=mlx5_0:1,mlx5_3:1"</code></pre>
|
||||||
|
<p>
|
||||||
|
这个变量只是“允许选择谁”,不会自动把宿主机 RDMA 设备送进容器。
|
||||||
|
容器还必须看到 <code>/dev/infiniband/rdma_cm</code>、
|
||||||
|
<code>uverbs0</code> 和 <code>uverbs3</code>。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>NCCL_CROSS_NIC</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>值</th><th>行为</th><th>适用直觉</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>0</code></td>
|
||||||
|
<td>尽量让同一 ring/tree 在不同节点使用对应的同一条 Rail。</td>
|
||||||
|
<td>每张 NIC 接不同交换机、跨 Rail 代价高的 rail-optimized 网络。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>1</code></td>
|
||||||
|
<td>允许同一 ring/tree 在不同节点使用不同 NIC。</td>
|
||||||
|
<td>所有 NIC 进入同一网络 Fabric,跨 NIC 不构成额外问题。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>2</code></td>
|
||||||
|
<td>优先对应同一 NIC,但必要时允许跨 NIC。</td>
|
||||||
|
<td>NCCL 默认的折中策略。</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="note">
|
||||||
|
本项目的 <code>mlx5_0</code> 和 <code>mlx5_3</code> 分挂 switch 1/2,
|
||||||
|
拓扑直觉上更偏向 <code>0</code> 或默认 <code>2</code>。最终值必须在
|
||||||
|
<strong>真正启用 NET/IB 后</strong>用 all_reduce 和 SGLang 端到端 A/B 决定。
|
||||||
|
当 NCCL 使用 NET/Socket 时,这个参数不参与路径选择。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>NCCL_DEBUG 与 NCCL_DEBUG_SUBSYS</h3>
|
||||||
|
<pre><code>NCCL_DEBUG=INFO
|
||||||
|
NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH,TUNING</code></pre>
|
||||||
|
<p>用于确认实际路径,诊断完成后应关闭,正式性能数据不要长期带 INFO 日志。</p>
|
||||||
|
|
||||||
|
<h2 id="logs">6. NCCL 日志速查</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>日志</th><th>含义</th><th>判断</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>Bootstrap: Using eth0:...</code></td>
|
||||||
|
<td>初始化控制连接选择 eth0。</td>
|
||||||
|
<td>只说明 bootstrap,尚不能证明数据走 RDMA。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>NET/IB : Using ... mlx5_0 ...</code></td>
|
||||||
|
<td>NCCL 已识别 RDMA HCA。</td>
|
||||||
|
<td>RDMA 数据后端可用。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>via NET/IB/.../GDRDMA</code></td>
|
||||||
|
<td>跨机边通过 GPUDirect RDMA。</td>
|
||||||
|
<td>理想证据。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>NET/IB : No device found</code></td>
|
||||||
|
<td>容器没有可用 RDMA 设备或驱动/权限不完整。</td>
|
||||||
|
<td>继续看是否回退 Socket。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>NET/Socket : Using 非计算网...</code></td>
|
||||||
|
<td>跨机数据由普通 TCP Socket 传输。</td>
|
||||||
|
<td>若误入低速非计算网,性能会严重受限。</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>via P2P/IPC</code></td>
|
||||||
|
<td>同机 GPU 通过 CUDA P2P/IPC。</td>
|
||||||
|
<td>机内路径,不代表跨机路径。</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2 id="incident">7. 2026-07-30 Prefill 变慢事故复盘</h2>
|
||||||
|
<h3>已观测事实</h3>
|
||||||
|
<ul>
|
||||||
|
<li>宿主机存在 <code>/dev/infiniband</code>,两条 400G Rail 均 Up。</li>
|
||||||
|
<li>原脚本容器内不存在 <code>/dev/infiniband</code>。</li>
|
||||||
|
<li>NCCL INFO 明确打印 <code>NET/IB : No device found</code> 和 <code>Using network Socket</code>。</li>
|
||||||
|
<li>原脚本选择 400G 计算网 <code>eth0</code>;quick-map 曾误选低速非计算网。</li>
|
||||||
|
<li>部署规定只有 <code>eth0/eth3</code> 用于节点间通信,两者均为 400G。</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>冷缓存 Shape</th><th>原脚本网络:eth0 Socket</th><th>quick-map:错误的非计算网</th><th>差异</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1K → 1, C=1</td>
|
||||||
|
<td class="metric">TTFT 1.458s / 693.1 input tok/s</td>
|
||||||
|
<td class="metric">TTFT 15.88–16.04s / 约 64 tok/s</td>
|
||||||
|
<td>约 10.9×</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>32K → 1, C=1</td>
|
||||||
|
<td class="metric">TTFT 38.062s / 860.5 input tok/s</td>
|
||||||
|
<td class="metric">TTFT 504.44s / 64.96 tok/s</td>
|
||||||
|
<td>约 13.25×</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="finding">
|
||||||
|
<strong>根因判断:</strong>
|
||||||
|
quick-map 没有把 RDMA 设备透传进容器,却把 <code>NCCL_SOCKET_IFNAME</code>
|
||||||
|
设成低速非计算网。NCCL 回退 NET/Socket 后,TP16 跨机数据没有进入规定的
|
||||||
|
<code>eth0/eth3</code> 计算网。<code>NCCL_CROSS_NIC=1</code> 在没有 NET/IB
|
||||||
|
的情况下不是致因。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>为什么旧日志还会比 38 秒更短</h3>
|
||||||
|
<p>
|
||||||
|
旧矩阵脚本还有第二个独立因素:<code>warmup_requests=16</code>、固定
|
||||||
|
<code>seed=42</code>、ISL/OSL/C 升序运行,而且从不 flush Prefix Cache。
|
||||||
|
因此旧日志混入缓存命中,不能直接与冷 Prefill 比较。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="good">
|
||||||
|
quick-map 现在只允许 <code>eth0/eth3</code> 和
|
||||||
|
<code>mlx5_0/mlx5_3</code>,并会透传精确 RDMA 设备、强制检查两端
|
||||||
|
<code>NET/IB</code> 日志。代码与 dry-run 已通过;下一步是真机启动验证,
|
||||||
|
在拿到运行时证据前不进入 Kernel 归因。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 id="checklist">8. 从宿主机到 NCCL 的排查清单</h2>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<strong>宿主机链路:</strong>
|
||||||
|
<code>ethtool eth0</code>、<code>ethtool eth3</code>。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>HCA 映射:</strong>
|
||||||
|
<code>ibdev2netdev</code>,确认 <code>mlx5_0→eth0</code>、
|
||||||
|
<code>mlx5_3→eth3</code>。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>宿主机设备:</strong>
|
||||||
|
<code>ls -l /dev/infiniband</code>。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>容器设备:</strong>
|
||||||
|
<code>docker exec CONTAINER ls -l /dev/infiniband</code>。
|
||||||
|
宿主机有、容器没有,NCCL 仍然用不了 RDMA。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>运行时证据:</strong>
|
||||||
|
用一次 <code>NCCL_DEBUG=INFO</code> 启动,搜索
|
||||||
|
<code>NET/IB</code>、<code>NET/Socket</code>、<code>GDRDMA</code>。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>硬件计数器:</strong>
|
||||||
|
同时观察 eth0/eth3 流量和 RDMA 端口计数;不能只看环境变量。
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>端到端 A/B:</strong>
|
||||||
|
冷缓存、同一 prompt、同一模型参数,仅改变一个网络变量。
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3>最小 RDMA 设备透传验证</h3>
|
||||||
|
<pre><code>docker run --rm \
|
||||||
|
--device=/dev/infiniband/rdma_cm \
|
||||||
|
--device=/dev/infiniband/uverbs0 \
|
||||||
|
--device=/dev/infiniband/uverbs3 \
|
||||||
|
IMAGE \
|
||||||
|
ls -l /dev/infiniband</code></pre>
|
||||||
|
<p>
|
||||||
|
能看到设备只是第一关。最终仍必须从 NCCL INFO 中看到 <code>NET/IB</code>,
|
||||||
|
并通过通信基准与 SGLang 结果确认。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="sources">9. 官方资料</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/setup.html">
|
||||||
|
NVIDIA NCCL Setup:bootstrap 与通信安全边界
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/env.html">
|
||||||
|
NVIDIA NCCL Environment Variables:SOCKET_IFNAME、IB_HCA、CROSS_NIC
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/troubleshooting/networking_troubleshooting.html">
|
||||||
|
NVIDIA NCCL Networking Troubleshooting
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="./推理优化计划.html">返回推理优化主计划</a>
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
本页以两台 6000D 的真实设备映射和 2026-07-30 实测日志为例。
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -228,7 +228,7 @@
|
|||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
||||||
<span>拓扑:SGLang TP16 / EP2</span>
|
<span>拓扑:SGLang TP16 / EP2</span>
|
||||||
<span>更新:2026-07-30 15:46 CST</span>
|
<span>更新:2026-07-30 17:54 CST</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -237,15 +237,19 @@
|
|||||||
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
||||||
|
|
||||||
<p class="status">
|
<p class="status">
|
||||||
<strong>阶段状态:提前结束,已进入硬件归因。</strong>
|
<strong>阶段状态:网络错误已定位,RDMA fail-closed 代码已完成,等待真机验证和重跑。</strong>
|
||||||
第一次真机 Run
|
第一次真机 Run
|
||||||
<code>dsv4pro-pro6000d-2node-sglang-quick-20260730-140026</code>
|
<code>dsv4pro-pro6000d-2node-sglang-quick-20260730-140026</code>
|
||||||
已验证双机服务可用,但因发现请求量和 Prefix Cache 口径问题而主动停止。
|
已验证双机服务可用,但因发现请求量和 Prefix Cache 口径问题而主动停止。
|
||||||
精简后的第二次 Run
|
精简后的第二次 Run
|
||||||
<code>dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625</code>
|
<code>dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625</code>
|
||||||
完成 3 个 Prefill 固定点后,发现输入吞吐稳定锁定在约
|
完成 3 个 Prefill 固定点后曾观察到约 <code>65 token/s</code>。
|
||||||
<code>65 token/s</code>。继续扫描 Decode 和混合流量不能解释该异常,
|
后续控制组确认 quick-map 容器看不到 RDMA,NCCL 回退
|
||||||
因此用户决定中止第 4 个 Case,直接进入 Phase 2。
|
<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>;
|
Manifest 终态为 <code>ABORTED_EARLY_FOR_PHASE2</code>;
|
||||||
两节点容器和 16 张 GPU 已清理。
|
两节点容器和 16 张 GPU 已清理。
|
||||||
</p>
|
</p>
|
||||||
@ -261,7 +265,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;三个已完成 Case 可作为对应 Shape 的端到端基线。</li>
|
<li>本轮不启用 Profiler;受错误 Socket 网络影响的三个旧 Case 仅保留为事故证据,不再作为端到端基线。</li>
|
||||||
<li>不修改或调用旧的全天全量 Benchmark 脚本。</li>
|
<li>不修改或调用旧的全天全量 Benchmark 脚本。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -351,14 +355,19 @@ bash run_quick_map.sh stop</code></pre>
|
|||||||
<td>覆盖固定矩阵中的 Decode C64</td>
|
<td>覆盖固定矩阵中的 Decode C64</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>NCCL bootstrap</td>
|
<td>NCCL Socket 接口</td>
|
||||||
<td><code>eth1</code></td>
|
<td><code>eth0</code></td>
|
||||||
<td>普通 TCP 建连接口</td>
|
<td>RDMA 失败回退时也承载跨机 Tensor,不只是 bootstrap</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>RoCE HCA</td>
|
<td>RoCE HCA</td>
|
||||||
<td><code>mlx5_0,mlx5_3</code></td>
|
<td><code>mlx5_0,mlx5_3</code></td>
|
||||||
<td>双 Rail 数据面,<code>NCCL_CROSS_NIC=1</code></td>
|
<td>启动器只透传对应的 <code>uverbs0/uverbs3</code> 与 <code>rdma_cm</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>传输后端门禁</td>
|
||||||
|
<td><code>REQUIRE_NCCL_IB=1</code></td>
|
||||||
|
<td>两端日志未证明 <code>NET/IB + mlx5_0 + mlx5_3</code> 时禁止开始 benchmark</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>代码分支</td>
|
<td>代码分支</td>
|
||||||
@ -565,38 +574,43 @@ wait "${background_pid}"</code></pre>
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>9.1 可以下的结论</h3>
|
<p class="status">
|
||||||
|
<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>三个长度的输入吞吐只相差约 1.2%,稳定在 <code>64.44–65.20 token/s</code>。</li>
|
||||||
<li>32K TTFT 约为 1K 的 31.76 倍;128K TTFT 约为 32K 的 3.99 倍,几乎按 token 数线性增长。</li>
|
<li>32K TTFT 约为 1K 的 31.76 倍;128K TTFT 约为 32K 的 3.99 倍,几乎按 token 数线性增长。</li>
|
||||||
<li>这不是偶发卡顿:服务端每约 125 秒完成一个 8192-token Chunk,GPU 在该期间持续忙碌。</li>
|
<li>这不是偶发卡顿:服务端每约 125 秒完成一个 8192-token Chunk,GPU 在该期间持续忙碌。</li>
|
||||||
<li><code>32K, C=16</code> 的观察窗口内,日志持续显示 <code>#new-seq: 1</code>,队列从 15 降到 14;至少当前路径没有立即把 16 条 Prefill 合成大批次。</li>
|
<li><code>32K, C=16</code> 的观察窗口内,日志持续显示 <code>#new-seq: 1</code>,队列从 15 降到 14;至少当前路径没有立即把 16 条 Prefill 合成大批次。</li>
|
||||||
<li>没有出现 OOM、NCCL、CUDA、EngineDead 或请求失败,所以“服务崩溃”不能解释低吞吐。</li>
|
<li>没有出现 OOM、CUDA、EngineDead 或请求失败,但 NCCL 没有使用预期 RDMA 数据面。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>9.2 现在还不能下的结论</h3>
|
<h3>9.2 现在还不能下的结论</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>仅凭 GPU Utilization=100% 不能判断是算力、显存带宽还是通信瓶颈。</li>
|
<li>仅凭 GPU Utilization=100% 不能判断是算力、显存带宽还是通信瓶颈。</li>
|
||||||
<li>尚不能断言是 SGLang Bug、DSV4/NSA Kernel、TP16 NCCL、MoE Backend 或 Scheduler 中的哪一项。</li>
|
<li>不能用本轮断言 SGLang Bug、DSV4/NSA Kernel、MoE Backend 或 Scheduler 存在性能问题。</li>
|
||||||
<li>Decode、Balanced 和混合 A/B 未执行,Phase 1 不提供这些场景的基线。</li>
|
<li>Decode、Balanced 和混合 A/B 未执行,Phase 1 不提供这些场景的基线。</li>
|
||||||
<li>每个 Shape 只有一次重复,不能用于稳定性或 CV 结论。</li>
|
<li>每个 Shape 只有一次重复,不能用于稳定性或 CV 结论。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>9.3 为什么提前结束</h3>
|
<h3>9.3 为什么提前结束</h3>
|
||||||
<p>
|
<p>
|
||||||
Phase 1 的目标是发现值得归因的关键异常,而不是机械完成九个格子。
|
当时根据约 65 token/s 的稳定信号提前停止第 4 个 Case,并写入
|
||||||
三个独立长度已经给出同一个稳定信号;第 4 个并发 Prefill 在 922 秒后仍表现为
|
<code>EARLY_STOP_FOR_PHASE2</code>。后续网络控制组表明这个停止动作仍然避免了
|
||||||
单序列 Chunk 推进。继续执行剩余矩阵预计还需数小时,却不能回答
|
无效算力消耗,但调查方向需要修正:不是立即进入 Kernel/硬件归因,而是先校正
|
||||||
“这 65 token/s 到底卡在哪里”。因此第 4 个 Case 被写入
|
NCCL 数据面并重跑快速地图。
|
||||||
<code>EARLY_STOP_FOR_PHASE2</code>,其余固定点和混合 A/B 保留为未执行。
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>9.4 为什么旧脚本的 TTFT 短很多</h3>
|
<h3>9.4 为什么旧脚本的 TTFT 短很多:缓存与网络两个因素</h3>
|
||||||
<p>
|
<p>
|
||||||
2026-07-30 对旧目录
|
2026-07-30 对旧目录
|
||||||
<code>/data/qqt/sskj/experiments/pro6000/dsv4_pro6000_sglang_tp16</code>
|
<code>/data/qqt/sskj/experiments/pro6000/dsv4_pro6000_sglang_tp16</code>
|
||||||
做了逐项审计。结论是:<strong>旧结果与本轮冷 Prefill 不是同一缓存口径</strong>,
|
做了逐项审计,并追加原网络配置冷请求控制组。最终结论是:
|
||||||
不是 quick-map 把相同请求跑慢了。
|
<strong>旧结果与 quick-map 既不是同一缓存口径,也不是同一 Socket 网络路径。</strong>
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@ -606,8 +620,8 @@ wait "${background_pid}"</code></pre>
|
|||||||
<tr>
|
<tr>
|
||||||
<td>服务端配置</td>
|
<td>服务端配置</td>
|
||||||
<td>同一镜像,TP16 / EP2,8K Chunk,FlashInfer MXFP4 MoE</td>
|
<td>同一镜像,TP16 / EP2,8K Chunk,FlashInfer MXFP4 MoE</td>
|
||||||
<td>相同</td>
|
<td>模型参数相同,但 NCCL/IP 接口不同</td>
|
||||||
<td class="pass">排除明显的启动参数回归</td>
|
<td>不能排除网络启动参数回归</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>正式请求数</td>
|
<td>正式请求数</td>
|
||||||
@ -645,10 +659,10 @@ wait "${background_pid}"</code></pre>
|
|||||||
<tr><th>最小复现</th><th>Mean TTFT</th><th>P95 TTFT</th><th>解释</th></tr>
|
<tr><th>最小复现</th><th>Mean TTFT</th><th>P95 TTFT</th><th>解释</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>1K→1,首次冷缓存</td><td>16.04 s</td><td>16.04 s</td><td>清 Prefix Cache,1 条正式请求</td></tr>
|
<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>15.90 s</td><td>15.90 s</td><td>再次清 Cache,排除一次性 JIT 主导</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>1K→128,冷缓存</td><td>15.79 s</td><td>15.79 s</td><td>排除 OSL=1 特殊慢路径</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>旧命令语义重新复现</td><td>14.60 s</td><td>15.97 s</td><td>10 条正式请求、16 条 Warm-up、不清 Cache</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>
|
<tr><td>2026-07-28 旧产物</td><td>0.455 s</td><td>0.513 s</td><td>服务已被前一次 Run 和后续递增长度预热</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -659,9 +673,7 @@ wait "${background_pid}"</code></pre>
|
|||||||
Input TPS,因此会把只计算新增后缀的耗时除进完整 token 数,进一步放大吞吐。
|
Input TPS,因此会把只计算新增后缀的耗时除进完整 token 数,进一步放大吞吐。
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
审计结论:Phase 1 的约 65 token/s 是<strong>冷 Prefix Cache 的完整 Prompt 路径</strong>,
|
缓存审计原始产物保存在:
|
||||||
旧结果是热缓存/递增前缀路径。两者都可以测,但必须分成 Cold 与 Warm 两套实验,
|
|
||||||
不能放在同一列直接比较。审计原始产物保存在:
|
|
||||||
</p>
|
</p>
|
||||||
<pre><code>/data/hzy/dsv4_script_audit_20260730/</code></pre>
|
<pre><code>/data/hzy/dsv4_script_audit_20260730/</code></pre>
|
||||||
<p>
|
<p>
|
||||||
@ -670,6 +682,42 @@ wait "${background_pid}"</code></pre>
|
|||||||
及同目录原始 JSON/log。
|
及同目录原始 JSON/log。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<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>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -679,12 +727,13 @@ wait "${background_pid}"</code></pre>
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>服务健康</td><td class="pass">通过</td><td>端口 <code>30002</code> 已就绪,无 OOM、NCCL 或 Engine 异常</td></tr>
|
<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>第一次固定点 Run</td><td class="pending">主动停止</td><td>发现 32K 单请求约需十余分钟;原协议的 4 次同形状请求会使整轮再次接近半天</td></tr>
|
||||||
<tr><td>精简后九个固定点</td><td class="pending">3 完成 / 1 中止 / 5 未执行</td><td>Prefill 异常信号已足够清晰,停止继续消耗算力</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>混合干扰 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="pass">约 65 分钟</td><td>14:36:26 启动,15:41:52 完成进程与容器清理</td></tr>
|
||||||
<tr><td>是否进入下一阶段</td><td class="pass">是</td><td>用户决定立即进入 Phase 2 硬件指标归因</td></tr>
|
<tr><td>是否进入下一阶段</td><td class="pending">否</td><td>Phase 2 暂停;先证明双 Rail NET/IB 并重跑 Phase 1</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
@ -135,7 +135,7 @@
|
|||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
||||||
<span>拓扑:SGLang TP16 / EP2</span>
|
<span>拓扑:SGLang TP16 / EP2</span>
|
||||||
<span>更新:2026-07-30 16:35 CST</span>
|
<span>更新:2026-07-30 17:54 CST</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -144,53 +144,55 @@
|
|||||||
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
||||||
|
|
||||||
<p class="status">
|
<p class="status">
|
||||||
<strong>当前状态:旧脚本口径审计完成,Phase 2 代码尚未开始。</strong>
|
<strong>当前状态:网络前置条件未满足,Phase 2 暂停,代码尚未开始。</strong>
|
||||||
本页从第一行 Phase 2 代码开始同步维护。每次代码改动、静态验证、真机运行和
|
本页从第一行 Phase 2 代码开始同步维护。每次代码改动、静态验证、真机运行和
|
||||||
结果判断都会在对应小节留下文件路径、命令和证据,不在阶段结束后凭记忆补写。
|
结果判断都会在对应小节留下文件路径、命令和证据,不在阶段结束后凭记忆补写。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>1. 为什么立即进入 Phase 2</h2>
|
<h2>1. 为什么现在不能直接进入 Phase 2</h2>
|
||||||
<p>
|
<p>
|
||||||
Phase 1 在没有 Profiler、没有 Prefix Cache 命中的条件下得到以下结果:
|
Phase 1 在没有 Profiler、没有 Prefix Cache 命中的条件下得到以下结果,
|
||||||
|
但这些数值后来确认受错误 Socket 网络路径污染:
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>ISL / OSL / C</th><th>输入 TPS</th><th>TTFT</th><th>结果</th></tr>
|
<tr><th>ISL / OSL / C</th><th>输入 TPS</th><th>TTFT</th><th>结果</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>1K / 1 / 1</td><td>64.44 tok/s</td><td>15.88 s</td><td>完成</td></tr>
|
<tr><td>1K / 1 / 1</td><td>64.44 tok/s</td><td>15.88 s</td><td>错误网络证据,不作为基线</td></tr>
|
||||||
<tr><td>32K / 1 / 1</td><td>64.96 tok/s</td><td>504.44 s</td><td>完成</td></tr>
|
<tr><td>32K / 1 / 1</td><td>64.96 tok/s</td><td>504.44 s</td><td>错误网络证据,不作为基线</td></tr>
|
||||||
<tr><td>128K / 1 / 1</td><td>65.20 tok/s</td><td>2010.38 s</td><td>完成</td></tr>
|
<tr><td>128K / 1 / 1</td><td>65.20 tok/s</td><td>2010.38 s</td><td>错误网络证据,不作为基线</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>
|
<p>
|
||||||
三个长度的输入吞吐几乎相同,TTFT 近似按 token 数线性增加。
|
quick-map 容器没有 <code>/dev/infiniband</code>,NCCL 回退
|
||||||
这已经不是“继续扩充 Shape”能回答的问题。Phase 2 要回答:
|
<code>NET/Socket</code>;脚本又误选低速非计算网。原网络配置冷请求控制组中,
|
||||||
<strong>稳定的约 65 token/s 到底受 GPU 计算、显存、CPU 调度还是双机通信中的哪一项限制。</strong>
|
1K/32K TTFT 分别只有 1.458s/38.062s,比 quick-map 快约 10.9×/13.25×。
|
||||||
|
因此约 65 token/s 不是待 profile 的模型现象,而是已定位的部署配置错误。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>1.1 Phase 2 前置审计</h3>
|
<h3>1.1 Phase 2 前置审计</h3>
|
||||||
<p>
|
<p>
|
||||||
旧脚本较短的 TTFT 已确认不是同口径反例。旧脚本固定执行 16 条同 Prompt
|
旧脚本较短的 TTFT 包含两个因素。其一,旧脚本固定执行 16 条同 Prompt
|
||||||
Warm-up,从不清 Prefix Cache,并按固定 Seed 递增长度;17:40 的失败 Run
|
Warm-up,从不清 Prefix Cache,并按固定 Seed 递增长度;17:40 的失败 Run
|
||||||
还在 18:01 正式 Run 前预热了同一批 1K 请求。全字段比较显示新旧
|
还在 18:01 正式 Run 前预热了同一批 1K 请求。其二,新旧模型参数虽相同,
|
||||||
<code>server_info</code> 的关键运行参数相同。
|
NCCL Socket 接口不同,而二者容器都没有形成真实 RDMA 数据面。
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
同一服务上的最小复现得到:两次独立清 Cache 的 1K→1 TTFT 分别为
|
网络控制组明确打印 <code>NET/IB : No device found</code> 和
|
||||||
16.04 秒和 15.90 秒;把 OSL 改为 128 后是 15.79 秒;按旧命令语义重新执行
|
<code>Using network Socket</code>。Phase 1 入口现已加入 RDMA 设备透传与
|
||||||
仍为 14.60 秒,而不是旧产物的 0.455 秒。因此 Phase 2 将继续 profile
|
<code>NET/IB</code> fail-closed 校验,但真机验证尚未运行。Phase 2 只有在
|
||||||
<strong>清 Prefix Cache 后的完整冷 Prefill</strong>。Warm Prefix/Prefix Cache
|
双 Rail RDMA 得到运行时证据并重跑 Phase 1 后才会开始。Warm Prefix/Prefix Cache
|
||||||
收益另立 A/B,不与本阶段混算。
|
收益仍另立 A/B,不与冷 Prefill 混算。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>2. 本阶段的边界</h2>
|
<h2>2. 本阶段的边界</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>只测试 SGLang,不测试 vLLM。</li>
|
<li>只测试 SGLang,不测试 vLLM。</li>
|
||||||
<li>保留 Phase 1 的模型、镜像、TP16、EP2、显存比例和 NCCL 参数。</li>
|
<li>保留模型、镜像、TP16、EP2 和显存比例;NCCL 参数必须使用修正并验证后的版本。</li>
|
||||||
<li>不启用 Nsight Systems、PyTorch Profiler、NCCL DEBUG 或投机解码。</li>
|
<li>不启用 Nsight Systems、PyTorch Profiler、NCCL DEBUG 或投机解码。</li>
|
||||||
<li>不调参,不尝试优化;先获得足以区分瓶颈类别的硬件证据。</li>
|
<li>不调参,不尝试优化;先获得足以区分瓶颈类别的硬件证据。</li>
|
||||||
<li>第一轮只重放 <code>32K → 1, C=1</code>,与 Phase 1 结果直接对齐。</li>
|
<li>第一轮仍只重放 <code>32K → 1, C=1</code>,但必须与修正后的 Phase 1 结果对齐。</li>
|
||||||
<li>采集器从请求开始前启动,到请求结束后停止,不能中途补采后声称完整。</li>
|
<li>采集器从请求开始前启动,到请求结束后停止,不能中途补采后声称完整。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -311,13 +313,15 @@ dsv4pro_pro6000d_2node_sglang_prefill_hardware_attribution/</code></pre>
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>2026-07-30 15:46 CST</td><td>创建 Phase 2 设计与档案</td><td>代码尚未开始,等待按本页设计实现</td></tr>
|
<tr><td>2026-07-30 15:46 CST</td><td>创建 Phase 2 设计与档案</td><td>代码尚未开始,等待按本页设计实现</td></tr>
|
||||||
<tr><td>2026-07-30 16:35 CST</td><td>完成旧脚本与 quick-map 同口径审计</td><td>排除服务参数、OSL=1 和一次性 JIT;确认旧产物被 Warm-up、跨 Case 与前一轮 Prefix Cache 污染</td></tr>
|
<tr><td>2026-07-30 16:35 CST</td><td>完成旧脚本与 quick-map 同口径审计</td><td>排除服务参数、OSL=1 和一次性 JIT;确认旧产物被 Warm-up、跨 Case 与前一轮 Prefix Cache 污染</td></tr>
|
||||||
|
<tr><td>2026-07-30 17:30 CST</td><td>完成原网络配置冷请求控制组</td><td>确认 quick-map 误入低速非计算网 Socket;Phase 2 暂停,先修正并重跑 Phase 1</td></tr>
|
||||||
|
<tr><td>2026-07-30 17:54 CST</td><td>完成 Phase 1 RDMA fail-closed 代码与 dry-run</td><td>只允许 eth0/eth3 与 mlx5_0/mlx5_3;等待真机 NET/IB 证据</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>10. 真机结果</h2>
|
<h2>10. 真机结果</h2>
|
||||||
<p class="pending">
|
<p class="pending">
|
||||||
尚未运行。代码实现、静态验证和 Dry-run 完成后,将先向用户说明具体代码改动,
|
尚未运行,也不应立即运行。先获得修正网络后的 Phase 1 冷 32K 基线;
|
||||||
再启动真机诊断。
|
Phase 2 代码实现、静态验证和 Dry-run 完成后,再向用户说明具体改动并等待阶段门。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p><a class="back" href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">返回 Phase 1 实施记录</a></p>
|
<p><a class="back" href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">返回 Phase 1 实施记录</a></p>
|
||||||
|
|||||||
@ -0,0 +1,104 @@
|
|||||||
|
# DeepSeek-V4-Pro TP16 网络路径审计
|
||||||
|
|
||||||
|
- 时间:2026-07-30
|
||||||
|
- 节点:`174.1.51.5 + 174.1.51.7`
|
||||||
|
- 模型:DeepSeek-V4-Pro
|
||||||
|
- 引擎:SGLang nightly
|
||||||
|
- 拓扑:TP16 / EP2 / 2 nodes
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
Phase 1 quick-map 的约 65 input token/s 不是可直接归因给模型、Kernel 或 GPU
|
||||||
|
的性能基线。它同时受两个独立因素影响:
|
||||||
|
|
||||||
|
1. quick-map 容器没有 RDMA 设备,NCCL 回退 `NET/Socket`。
|
||||||
|
2. quick-map 的 `NCCL_SOCKET_IFNAME` 误选低速非计算网,Socket 数据没有进入
|
||||||
|
部署规定的 `eth0/eth3` 计算网。
|
||||||
|
|
||||||
|
`NCCL_CROSS_NIC=1` 不是本次 10 倍以上退化的原因。该参数只有在 NCCL
|
||||||
|
真正使用多个 RDMA HCA 时才影响 ring/tree 的 NIC 选择;本次实际后端为 Socket。
|
||||||
|
|
||||||
|
## 设备关系
|
||||||
|
|
||||||
|
部署时只使用两条节点间计算网:
|
||||||
|
|
||||||
|
| 物理端口 | Linux netdev/IP 入口 | RDMA Verbs/HCA 入口 | 状态 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 400G Rail 1 | `eth0` | `mlx5_0` | Up |
|
||||||
|
| 400G Rail 2 | `eth3` | `mlx5_3` | Up |
|
||||||
|
|
||||||
|
`eth0` 与 `mlx5_0` 不是同一个软件设备。它们是同一条 400G 物理 Ethernet
|
||||||
|
端口的两种入口:前者服务 IP/TCP Socket,后者服务 RoCE/RDMA Verbs。
|
||||||
|
|
||||||
|
## 运行时证据
|
||||||
|
|
||||||
|
宿主机存在:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/dev/infiniband/rdma_cm
|
||||||
|
/dev/infiniband/uverbs0
|
||||||
|
/dev/infiniband/uverbs3
|
||||||
|
```
|
||||||
|
|
||||||
|
按原脚本启动的容器内:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ls: cannot access '/dev/infiniband': No such file or directory
|
||||||
|
```
|
||||||
|
|
||||||
|
NCCL INFO:
|
||||||
|
|
||||||
|
```text
|
||||||
|
NCCL_SOCKET_IFNAME set by environment to eth0
|
||||||
|
Bootstrap: Using eth0:10.101.0.11
|
||||||
|
NET/IB : No device found.
|
||||||
|
Failed to initialize NET plugin IB
|
||||||
|
NET/Socket : Using [0]eth0:10.101.0.11
|
||||||
|
Using network Socket
|
||||||
|
```
|
||||||
|
|
||||||
|
## 冷缓存对照
|
||||||
|
|
||||||
|
共同条件:
|
||||||
|
|
||||||
|
- 同一模型、镜像、TP16/EP2 和 serving 参数。
|
||||||
|
- `random` 数据集,`seed=42`。
|
||||||
|
- `warmup_requests=0`。
|
||||||
|
- 测量前 `--flush-cache`。
|
||||||
|
- `num_prompts=1`、`max_concurrency=1`。
|
||||||
|
|
||||||
|
| Shape | 原脚本网络:`eth0` Socket | quick-map 错误网络 | 退化 |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| 1K -> 1 | TTFT 1.458s;693.1 input tok/s | TTFT 15.88-16.04s;约 64 tok/s | 约 10.9x |
|
||||||
|
| 32K -> 1 | TTFT 38.062s;860.5 input tok/s | TTFT 504.44s;64.96 tok/s | 约 13.25x |
|
||||||
|
|
||||||
|
## 旧矩阵为什么还能更快
|
||||||
|
|
||||||
|
旧矩阵结果还有 Prefix Cache 污染:
|
||||||
|
|
||||||
|
- 每个 case 固定 16 条相同首个 prompt 的 warm-up。
|
||||||
|
- benchmark 默认 `seed=42`,每次使用相同 ShareGPT shuffle 顺序。
|
||||||
|
- ISL、OSL 和并发升序运行。
|
||||||
|
- 从不传 `--flush-cache`。
|
||||||
|
- 17:40 的失败 Run 已执行过首个 1K case,18:01 正式 Run 复用同一服务。
|
||||||
|
|
||||||
|
所以旧 1K 的 0.455s、32K 的 32.03s 和 128K 的 130.44s 不是完整冷
|
||||||
|
Prefill,不能与任一冷缓存结果直接比较。
|
||||||
|
|
||||||
|
## 修复顺序
|
||||||
|
|
||||||
|
1. 当前 Socket baseline 默认改为 `NCCL_SOCKET_IFNAME=eth0`。
|
||||||
|
2. 重跑 Phase 1 的冷 1K/32K/128K 代表点。
|
||||||
|
3. 单独给容器透传 `rdma_cm`、`uverbs0`、`uverbs3`。
|
||||||
|
4. 用 `NCCL_DEBUG=INFO` 确认出现 `NET/IB`,不能只看环境变量。
|
||||||
|
5. 真正启用双 Rail RDMA 后,再比较 `NCCL_CROSS_NIC=0/1/2`。
|
||||||
|
6. 以修正后的端到端结果决定是否进入 Phase 2 硬件归因。
|
||||||
|
|
||||||
|
## 产物
|
||||||
|
|
||||||
|
- `head_server.log`
|
||||||
|
- `worker_server.log`
|
||||||
|
- `oldscript_network_cold_1k_o1.log`
|
||||||
|
- `oldscript_network_cold_1k_o1.jsonl`
|
||||||
|
- `oldscript_network_cold_32k_o1.log`
|
||||||
|
- `oldscript_network_cold_32k_o1.jsonl`
|
||||||
@ -400,7 +400,7 @@
|
|||||||
<div class="document-header__meta">
|
<div class="document-header__meta">
|
||||||
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
||||||
<span>资源:16 × RTX PRO 6000 Blackwell</span>
|
<span>资源:16 × RTX PRO 6000 Blackwell</span>
|
||||||
<span>版本:2026-07-30 15:52 CST</span>
|
<span>版本:2026-07-30 17:54 CST</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -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 15:52 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 17:54 CST</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<h2>当前执行状态与阶段档案</h2>
|
<h2>当前执行状态与阶段档案</h2>
|
||||||
<table>
|
<table>
|
||||||
@ -428,16 +428,57 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>6000D 双机通信与 NCCL 基础</td>
|
||||||
|
<td>已建立;覆盖计算网、RDMA、Bootstrap、NCCL 参数和日志判读</td>
|
||||||
|
<td><a href="./6000D双机通信与NCCL术语入门.html">打开通信术语入门</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td>DeepSeek-V4-Pro / 双机 Pro6000D / SGLang TP16 快速性能地图</td>
|
<td>DeepSeek-V4-Pro / 双机 Pro6000D / SGLang TP16 快速性能地图</td>
|
||||||
<td>提前结束;3 个冷 Prefill 点完成,输入吞吐稳定约 65 token/s;旧脚本缓存口径审计已完成</td>
|
<td>网络错误已定位;RDMA fail-closed 代码与 dry-run 已完成,待真机 NET/IB 验证后重跑</td>
|
||||||
<td><a href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">打开实施记录</a></td>
|
<td><a href="./phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html">打开实施记录</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>DeepSeek-V4-Pro / 双机 Pro6000D / SGLang Prefill 硬件指标归因</td>
|
<td>DeepSeek-V4-Pro / 双机 Pro6000D / SGLang Prefill 硬件指标归因</td>
|
||||||
<td>设计已固化;代码尚未开始</td>
|
<td>设计已固化、代码尚未开始;等待修正后的 Phase 1 基线</td>
|
||||||
<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>
|
||||||
|
<h2>0. 先看懂双机通信</h2>
|
||||||
|
<p>
|
||||||
|
在分析 TP16 性能前,先区分设备、传输后端与 NCCL 参数。
|
||||||
|
完整解释和本次网络误配置复盘见
|
||||||
|
<a href="./6000D双机通信与NCCL术语入门.html">《6000D 双机通信与 NCCL 术语入门》</a>。
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr><th>术语</th><th>一句话解释</th><th>本项目实例</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>NCCL</td><td>NVIDIA 多 GPU 通信库,执行 collective 和点对点通信</td><td>SGLang TP16 的跨 GPU 通信层</td></tr>
|
||||||
|
<tr><td>RDMA</td><td>网卡绕过常规 TCP/内核拷贝直接访问远端内存</td><td>双机高速数据面的目标路径</td></tr>
|
||||||
|
<tr><td>IB / RoCE</td><td>IB 是高速网络/Verbs 体系;RoCE 在以太网上承载 RDMA</td><td>NCCL 日志统一显示为 <code>NET/IB</code></td></tr>
|
||||||
|
<tr><td><code>eth0/eth3</code></td><td>400G 物理端口的 Linux netdev/IP 入口</td><td>仅这两个接口用于节点间部署通信</td></tr>
|
||||||
|
<tr><td><code>mlx5_0/mlx5_3</code></td><td>映射到上述物理端口的 RDMA Verbs/HCA 入口</td><td>与 <code>eth0/eth3</code> 有映射关系,但不是同一个软件设备</td></tr>
|
||||||
|
<tr><td>NCCL bootstrap</td><td>rank 启动时交换身份、地址、拓扑和连接信息的阶段</td><td>先建连,再选择真正的数据后端</td></tr>
|
||||||
|
<tr><td><code>NCCL_SOCKET_IFNAME</code></td><td>选择 NCCL 可用的 IP 接口</td><td>RDMA 失败时也决定 Socket 数据走哪张网卡</td></tr>
|
||||||
|
<tr><td><code>NCCL_IB_HCA</code></td><td>选择 NCCL 可用的 RDMA HCA</td><td><code>mlx5_0,mlx5_3</code></td></tr>
|
||||||
|
<tr><td><code>NCCL_CROSS_NIC</code></td><td>控制同一 ring/tree 能否在节点间跨不同 HCA</td><td>只有真正使用多 HCA <code>NET/IB</code> 时才有意义</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
<strong>400G 是每条物理 Ethernet 链路的标称线速,不是“TCP 速度”或“RDMA
|
||||||
|
速度”。</strong>同一条链路可以承载 TCP,也可以承载 RoCE/RDMA;
|
||||||
|
<code>400 Gbit/s ≈ 50 GB/s</code> 只是单向理论上限,NCCL 的
|
||||||
|
<code>algbw/busbw</code> 与端到端模型吞吐都不能直接等同于该数字。
|
||||||
|
</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>
|
||||||
@ -469,7 +510,7 @@
|
|||||||
<tbody><tr>
|
<tbody><tr>
|
||||||
<td>H1</td>
|
<td>H1</td>
|
||||||
<td>TP16 每层跨机通信暴露过多</td>
|
<td>TP16 每层跨机通信暴露过多</td>
|
||||||
<td>两台机器没有跨机 NVLink,TP Collective 需要经过 RoCE</td>
|
<td>两台机器没有跨机 NVLink;应先确保 Collective 真正经过计算网/RDMA,而不是 Socket 回退</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>H2</td>
|
<td>H2</td>
|
||||||
@ -725,6 +766,20 @@ numastat -p <PID>
|
|||||||
<li>CPU 空洞是否对应 GPU 空洞。</li>
|
<li>CPU 空洞是否对应 GPU 空洞。</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>6.3 网络</h3>
|
<h3>6.3 网络</h3>
|
||||||
|
<blockquote>
|
||||||
|
<p>
|
||||||
|
2026-07-30 控制组已确认:宿主机具备 <code>mlx5_0/eth0</code> 与
|
||||||
|
<code>mlx5_3/eth3</code> 两条 400G Rail,但原服务容器没有
|
||||||
|
<code>/dev/infiniband</code>,NCCL 实际使用 <code>NET/Socket</code>。
|
||||||
|
当前第一优先级是校正容器数据面并重做 Phase 1,不再把约 65 token/s 当作模型或算子瓶颈。
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
<p>
|
||||||
|
唯一启动入口现已在两端预检并透传
|
||||||
|
<code>rdma_cm/uverbs0/uverbs3</code>,且服务健康后强制从两端 NCCL INFO
|
||||||
|
日志确认 <code>NET/IB</code> 同时识别 <code>mlx5_0/mlx5_3</code>。代码、
|
||||||
|
单测与 dry-run 已通过;真机服务尚未启动,因此这里仍不宣称 RDMA 已验证成功。
|
||||||
|
</p>
|
||||||
<p>当前拓扑中需要分别观察两条 Compute Rail,确认:</p>
|
<p>当前拓扑中需要分别观察两条 Compute Rail,确认:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>两条 Rail 是否同时有流量。</li>
|
<li>两条 Rail 是否同时有流量。</li>
|
||||||
@ -744,8 +799,10 @@ NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH,TUNING
|
|||||||
<p>该日志开销较高,不应在正式性能结果中长期启用。</p>
|
<p>该日志开销较高,不应在正式性能结果中长期启用。</p>
|
||||||
<h3>6.4 NCCL_CROSS_NIC 快速 A/B</h3>
|
<h3>6.4 NCCL_CROSS_NIC 快速 A/B</h3>
|
||||||
<p>
|
<p>
|
||||||
Phase 1 固定使用 <code>NCCL_CROSS_NIC=1</code>。完成首轮 Prefill 硬件归因后,
|
旧 Phase 1 虽然设置了 <code>NCCL_CROSS_NIC=1</code>,但由于 NCCL 回退
|
||||||
固定其余环境,快速比较 <code>0/1/2</code>,不能仅凭双 Rail 拓扑判断最优值。
|
<code>NET/Socket</code>,该参数没有参与实际路径选择。先让容器真正使用
|
||||||
|
<code>mlx5_0/mlx5_3</code> 的 <code>NET/IB</code>,再固定其余环境比较
|
||||||
|
<code>0/1/2</code>,不能仅凭双 Rail 拓扑判断最优值。
|
||||||
</p>
|
</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>分别执行相同消息范围的 <code>all_reduce_perf</code>,每个值至少重复 3 次,检查错误、algbw 和 busbw。</li>
|
<li>分别执行相同消息范围的 <code>all_reduce_perf</code>,每个值至少重复 3 次,检查错误、algbw 和 busbw。</li>
|
||||||
|
|||||||
@ -4,6 +4,20 @@ This directory contains the short, repeatable performance-map suite for
|
|||||||
DeepSeek-V4-Pro on two RTX PRO 6000 Blackwell nodes. It does not modify or call the existing
|
DeepSeek-V4-Pro on two RTX PRO 6000 Blackwell nodes. It does not modify or call the existing
|
||||||
`dsv4_pro6000_sglang_tp16/run_batch.sh`.
|
`dsv4_pro6000_sglang_tp16/run_batch.sh`.
|
||||||
|
|
||||||
|
## Network precondition
|
||||||
|
|
||||||
|
The 2026-07-30 audit found that the current service container does not expose
|
||||||
|
`/dev/infiniband`, so NCCL falls back to `NET/Socket`. The quick-map previously
|
||||||
|
selected a low-speed non-compute interface and produced invalid 1K/32K cold
|
||||||
|
Prefill baselines. The Socket default is now `eth0`, one of the two deployment
|
||||||
|
compute interfaces (`eth0/eth3`).
|
||||||
|
|
||||||
|
Declaring `NCCL_IB_HCA=mlx5_0,mlx5_3` does not by itself enable RDMA. The
|
||||||
|
launcher now exposes only the three device nodes needed by the two deployment
|
||||||
|
rails (`rdma_cm`, `uverbs0`, `uverbs3`) and fails before benchmarking unless
|
||||||
|
both nodes' NCCL INFO logs prove `NET/IB` is using `mlx5_0` and `mlx5_3`.
|
||||||
|
`NCCL_CROSS_NIC` has no effect while the active transport is `NET/Socket`.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
The fixed suite covers nine points:
|
The fixed suite covers nine points:
|
||||||
|
|||||||
@ -21,11 +21,18 @@ DOCKER_IMAGE="${DOCKER_IMAGE:-lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45
|
|||||||
DOCKER_CLIENT_IMAGE="${DOCKER_CLIENT_IMAGE:-$DOCKER_IMAGE}"
|
DOCKER_CLIENT_IMAGE="${DOCKER_CLIENT_IMAGE:-$DOCKER_IMAGE}"
|
||||||
SGLANG_CACHE_DIR="${SGLANG_CACHE_DIR:-/data/hzy/sglang_cache/dsv4_pro_tp16}"
|
SGLANG_CACHE_DIR="${SGLANG_CACHE_DIR:-/data/hzy/sglang_cache/dsv4_pro_tp16}"
|
||||||
|
|
||||||
# eth1 is the TCP bootstrap interface. mlx5_0/mlx5_3 are the two RoCE rails.
|
# Only eth0/eth3 are deployment compute interfaces. eth0 is the bootstrap and
|
||||||
NCCL_SOCKET_IFNAME="${NCCL_SOCKET_IFNAME:-eth1}"
|
# Socket-fallback interface; mlx5_0/mlx5_3 are the two RoCE HCA rails.
|
||||||
NCCL_IB_HCA="${NCCL_IB_HCA:-mlx5_0,mlx5_3}"
|
NCCL_SOCKET_IFNAME="${NCCL_SOCKET_IFNAME:-eth0}"
|
||||||
|
NCCL_IB_HCA="${NCCL_IB_HCA:-=mlx5_0:1,mlx5_3:1}"
|
||||||
NCCL_CROSS_NIC="${NCCL_CROSS_NIC:-1}"
|
NCCL_CROSS_NIC="${NCCL_CROSS_NIC:-1}"
|
||||||
NCCL_DEBUG="${NCCL_DEBUG:-WARN}"
|
NCCL_DEBUG="${NCCL_DEBUG:-INFO}"
|
||||||
|
|
||||||
|
# Fail closed: a run labeled as RDMA must expose the exact HCA device nodes on
|
||||||
|
# both nodes and show NET/IB in NCCL startup logs.
|
||||||
|
ENABLE_RDMA="${ENABLE_RDMA:-1}"
|
||||||
|
REQUIRE_NCCL_IB="${REQUIRE_NCCL_IB:-1}"
|
||||||
|
RDMA_DEVICE_PATHS="${RDMA_DEVICE_PATHS:-/dev/infiniband/rdma_cm,/dev/infiniband/uverbs0,/dev/infiniband/uverbs3}"
|
||||||
|
|
||||||
# Keep the known working TP16 baseline. The quick map changes workload, not serving knobs.
|
# Keep the known working TP16 baseline. The quick map changes workload, not serving knobs.
|
||||||
MEM_FRACTION_STATIC="${MEM_FRACTION_STATIC:-0.9}"
|
MEM_FRACTION_STATIC="${MEM_FRACTION_STATIC:-0.9}"
|
||||||
|
|||||||
@ -363,6 +363,9 @@ def write_manifest(args: argparse.Namespace) -> None:
|
|||||||
"nccl_socket_ifname": args.nccl_socket_ifname,
|
"nccl_socket_ifname": args.nccl_socket_ifname,
|
||||||
"nccl_ib_hca": args.nccl_ib_hca,
|
"nccl_ib_hca": args.nccl_ib_hca,
|
||||||
"nccl_cross_nic": args.nccl_cross_nic,
|
"nccl_cross_nic": args.nccl_cross_nic,
|
||||||
|
"enable_rdma": bool(args.enable_rdma),
|
||||||
|
"require_nccl_ib": bool(args.require_nccl_ib),
|
||||||
|
"rdma_device_paths": args.rdma_device_paths,
|
||||||
"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,
|
||||||
@ -640,6 +643,9 @@ def add_manifest_arguments(parser: argparse.ArgumentParser) -> None:
|
|||||||
parser.add_argument("--nccl-socket-ifname", required=True)
|
parser.add_argument("--nccl-socket-ifname", required=True)
|
||||||
parser.add_argument("--nccl-ib-hca", required=True)
|
parser.add_argument("--nccl-ib-hca", required=True)
|
||||||
parser.add_argument("--nccl-cross-nic", required=True)
|
parser.add_argument("--nccl-cross-nic", required=True)
|
||||||
|
parser.add_argument("--enable-rdma", type=int, required=True)
|
||||||
|
parser.add_argument("--require-nccl-ib", type=int, required=True)
|
||||||
|
parser.add_argument("--rdma-device-paths", required=True)
|
||||||
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)
|
||||||
|
|||||||
@ -55,6 +55,78 @@ run_on_node() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate_network_config() {
|
||||||
|
case "${ENABLE_RDMA}" in
|
||||||
|
0|1) ;;
|
||||||
|
*)
|
||||||
|
log "ERROR: ENABLE_RDMA must be 0 or 1, got: ${ENABLE_RDMA}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
case "${REQUIRE_NCCL_IB}" in
|
||||||
|
0|1) ;;
|
||||||
|
*)
|
||||||
|
log "ERROR: REQUIRE_NCCL_IB must be 0 or 1, got: ${REQUIRE_NCCL_IB}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [[ "${REQUIRE_NCCL_IB}" == "1" && "${ENABLE_RDMA}" != "1" ]]; then
|
||||||
|
log "ERROR: REQUIRE_NCCL_IB=1 requires ENABLE_RDMA=1"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local interface_spec="${NCCL_SOCKET_IFNAME#=}"
|
||||||
|
local -a interfaces=()
|
||||||
|
local interface
|
||||||
|
IFS=',' read -r -a interfaces <<< "${interface_spec}"
|
||||||
|
for interface in "${interfaces[@]}"; do
|
||||||
|
case "${interface#=}" in
|
||||||
|
eth0|eth3) ;;
|
||||||
|
*)
|
||||||
|
log "ERROR: only deployment compute interfaces eth0/eth3 are allowed; got: ${interface}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local hca_spec="${NCCL_IB_HCA#=}"
|
||||||
|
local -a hca_entries=()
|
||||||
|
local hca_entry hca saw_mlx5_0=0 saw_mlx5_3=0
|
||||||
|
IFS=',' read -r -a hca_entries <<< "${hca_spec}"
|
||||||
|
for hca_entry in "${hca_entries[@]}"; do
|
||||||
|
hca="${hca_entry%%:*}"
|
||||||
|
case "${hca}" in
|
||||||
|
mlx5_0) saw_mlx5_0=1 ;;
|
||||||
|
mlx5_3) saw_mlx5_3=1 ;;
|
||||||
|
*)
|
||||||
|
log "ERROR: only deployment RDMA HCAs mlx5_0/mlx5_3 are allowed; got: ${hca}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [[ "${ENABLE_RDMA}" == "1" ]] \
|
||||||
|
&& (( saw_mlx5_0 == 0 || saw_mlx5_3 == 0 )); then
|
||||||
|
log "ERROR: RDMA mode requires both mlx5_0 and mlx5_3"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
preflight_rdma_devices_on_node() {
|
||||||
|
local node="$1"
|
||||||
|
[[ "${ENABLE_RDMA}" == "1" ]] || return 0
|
||||||
|
|
||||||
|
local -a device_paths=()
|
||||||
|
local device_path quoted_path
|
||||||
|
IFS=',' read -r -a device_paths <<< "${RDMA_DEVICE_PATHS}"
|
||||||
|
for device_path in "${device_paths[@]}"; do
|
||||||
|
printf -v quoted_path '%q' "${device_path}"
|
||||||
|
if ! run_on_node "${node}" "test -c ${quoted_path}"; then
|
||||||
|
log "ERROR: ${node} is missing RDMA character device: ${device_path}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
service_is_healthy() {
|
service_is_healthy() {
|
||||||
curl --fail --silent --show-error --max-time 5 \
|
curl --fail --silent --show-error --max-time 5 \
|
||||||
"http://${HEAD_IP}:${SGLANG_PORT}/health" >/dev/null 2>&1
|
"http://${HEAD_IP}:${SGLANG_PORT}/health" >/dev/null 2>&1
|
||||||
@ -78,8 +150,12 @@ remote_has_gpu_processes() {
|
|||||||
|
|
||||||
preflight_service_node() {
|
preflight_service_node() {
|
||||||
local node="$1"
|
local node="$1"
|
||||||
run_on_node "${node}" \
|
if ! run_on_node "${node}" \
|
||||||
"test -d '${MODEL_PATH}' && command -v docker >/dev/null && command -v nvidia-smi >/dev/null && docker image inspect '${DOCKER_IMAGE}' >/dev/null"
|
"test -d '${MODEL_PATH}' && command -v docker >/dev/null && command -v nvidia-smi >/dev/null && docker image inspect '${DOCKER_IMAGE}' >/dev/null"; then
|
||||||
|
log "ERROR: ${node} failed model, Docker, GPU, or image preflight"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
preflight_rdma_devices_on_node "${node}" || return 1
|
||||||
if [[ "${ALLOW_BUSY_GPU}" != "1" ]] && remote_has_gpu_processes "${node}"; then
|
if [[ "${ALLOW_BUSY_GPU}" != "1" ]] && remote_has_gpu_processes "${node}"; then
|
||||||
log "ERROR: ${node} has active GPU compute processes"
|
log "ERROR: ${node} has active GPU compute processes"
|
||||||
log "This experiment will not evict another workload."
|
log "This experiment will not evict another workload."
|
||||||
@ -130,6 +206,16 @@ build_server_command() {
|
|||||||
-e "NCCL_CROSS_NIC=${NCCL_CROSS_NIC}"
|
-e "NCCL_CROSS_NIC=${NCCL_CROSS_NIC}"
|
||||||
-e "NCCL_DEBUG=${NCCL_DEBUG}"
|
-e "NCCL_DEBUG=${NCCL_DEBUG}"
|
||||||
-e SGLANG_SHARED_EXPERT_TP1=1
|
-e SGLANG_SHARED_EXPERT_TP1=1
|
||||||
|
)
|
||||||
|
if [[ "${ENABLE_RDMA}" == "1" ]]; then
|
||||||
|
local -a device_paths=()
|
||||||
|
local device_path
|
||||||
|
IFS=',' read -r -a device_paths <<< "${RDMA_DEVICE_PATHS}"
|
||||||
|
for device_path in "${device_paths[@]}"; do
|
||||||
|
DOCKER_CMD+=(--device "${device_path}")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
DOCKER_CMD+=(
|
||||||
--entrypoint python3
|
--entrypoint python3
|
||||||
"${DOCKER_IMAGE}"
|
"${DOCKER_IMAGE}"
|
||||||
-m sglang.launch_server
|
-m sglang.launch_server
|
||||||
@ -162,16 +248,64 @@ start_service_node() {
|
|||||||
build_server_command "${node_rank}" "${container_name}"
|
build_server_command "${node_rank}" "${container_name}"
|
||||||
print_command "${DOCKER_CMD[@]}" > "${SERVER_ARTIFACT_DIR}/${role}_server_cmd.txt"
|
print_command "${DOCKER_CMD[@]}" > "${SERVER_ARTIFACT_DIR}/${role}_server_cmd.txt"
|
||||||
log "Starting ${role} node=${node} rank=${node_rank} container=${container_name}"
|
log "Starting ${role} node=${node} rank=${node_rank} container=${container_name}"
|
||||||
run_on_node "${node}" "mkdir -p '${SGLANG_CACHE_DIR}'"
|
if ! run_on_node "${node}" "mkdir -p '${SGLANG_CACHE_DIR}'"; then
|
||||||
|
log "ERROR: failed to create SGLang cache directory on ${node}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
run_on_node "${node}" "docker rm -f '${container_name}' >/dev/null 2>&1 || true"
|
run_on_node "${node}" "docker rm -f '${container_name}' >/dev/null 2>&1 || true"
|
||||||
|
|
||||||
local command
|
local command
|
||||||
command="$(print_command "${DOCKER_CMD[@]}")"
|
command="$(print_command "${DOCKER_CMD[@]}")"
|
||||||
run_on_node "${node}" "${command}" \
|
if ! run_on_node "${node}" "${command}" \
|
||||||
> "${SERVER_ARTIFACT_DIR}/${role}_container_id.txt"
|
> "${SERVER_ARTIFACT_DIR}/${role}_container_id.txt"; then
|
||||||
|
log "ERROR: failed to launch ${role} container on ${node}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_nccl_transport_node() {
|
||||||
|
local node="$1"
|
||||||
|
local container_name="$2"
|
||||||
|
local role="$3"
|
||||||
|
local transport_log="${SERVER_ARTIFACT_DIR}/${role}_nccl_transport.log"
|
||||||
|
|
||||||
|
run_on_node "${node}" "docker logs '${container_name}' 2>&1" \
|
||||||
|
> "${transport_log}" 2>&1
|
||||||
|
|
||||||
|
if grep -Fq "NET/IB : No device found" "${transport_log}"; then
|
||||||
|
log "ERROR: ${role} NCCL could not find an RDMA device"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! grep -Eq 'NET/IB.*Using|Using network IB|via NET/IB' "${transport_log}"; then
|
||||||
|
log "ERROR: ${role} NCCL log does not prove NET/IB is active"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local hca_spec="${NCCL_IB_HCA#=}"
|
||||||
|
local -a hca_entries=()
|
||||||
|
local hca_entry hca
|
||||||
|
IFS=',' read -r -a hca_entries <<< "${hca_spec}"
|
||||||
|
for hca_entry in "${hca_entries[@]}"; do
|
||||||
|
hca="${hca_entry%%:*}"
|
||||||
|
if ! grep -E "NET/IB.*${hca}|${hca}.*NET/IB" "${transport_log}" >/dev/null; then
|
||||||
|
log "ERROR: ${role} NCCL log does not show configured HCA ${hca}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "Verified ${role} NCCL transport: NET/IB with mlx5_0 and mlx5_3"
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_nccl_transport() {
|
||||||
|
[[ "${REQUIRE_NCCL_IB}" == "1" ]] || return 0
|
||||||
|
verify_nccl_transport_node \
|
||||||
|
"${WORKER_NODE}" "${WORKER_CONTAINER}" "worker" || return 1
|
||||||
|
verify_nccl_transport_node \
|
||||||
|
"${HEAD_NODE}" "${HEAD_CONTAINER}" "head" || return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
|
validate_network_config || return 1
|
||||||
build_server_command 1 "${WORKER_CONTAINER}"
|
build_server_command 1 "${WORKER_CONTAINER}"
|
||||||
if [[ "${DRY_RUN}" == "1" ]]; then
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
||||||
printf '[DRY] worker (%s): ' "${WORKER_NODE}"
|
printf '[DRY] worker (%s): ' "${WORKER_NODE}"
|
||||||
@ -183,18 +317,21 @@ start_service() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "${SERVER_ARTIFACT_DIR}"
|
mkdir -p "${SERVER_ARTIFACT_DIR}"
|
||||||
preflight_service_node "${HEAD_NODE}"
|
preflight_service_node "${HEAD_NODE}" || return 1
|
||||||
preflight_service_node "${WORKER_NODE}"
|
preflight_service_node "${WORKER_NODE}" || return 1
|
||||||
|
|
||||||
start_service_node "${WORKER_NODE}" 1 "${WORKER_CONTAINER}" "worker"
|
start_service_node \
|
||||||
|
"${WORKER_NODE}" 1 "${WORKER_CONTAINER}" "worker" || return 1
|
||||||
sleep 5
|
sleep 5
|
||||||
start_service_node "${HEAD_NODE}" 0 "${HEAD_CONTAINER}" "head"
|
start_service_node \
|
||||||
|
"${HEAD_NODE}" 0 "${HEAD_CONTAINER}" "head" || return 1
|
||||||
|
|
||||||
log "Waiting for SGLang health at ${HEAD_IP}:${SGLANG_PORT}"
|
log "Waiting for SGLang health at ${HEAD_IP}:${SGLANG_PORT}"
|
||||||
local attempt
|
local attempt
|
||||||
for (( attempt=1; attempt<=HEALTH_CHECK_RETRIES; attempt++ )); do
|
for (( attempt=1; attempt<=HEALTH_CHECK_RETRIES; attempt++ )); do
|
||||||
if service_is_healthy; then
|
if service_is_healthy; then
|
||||||
log "SGLang is healthy after ${attempt} checks"
|
log "SGLang is healthy after ${attempt} checks"
|
||||||
|
verify_nccl_transport || return 1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if ! run_on_node "${HEAD_NODE}" \
|
if ! run_on_node "${HEAD_NODE}" \
|
||||||
@ -502,6 +639,9 @@ write_run_manifest() {
|
|||||||
--nccl-socket-ifname "${NCCL_SOCKET_IFNAME}" \
|
--nccl-socket-ifname "${NCCL_SOCKET_IFNAME}" \
|
||||||
--nccl-ib-hca "${NCCL_IB_HCA}" \
|
--nccl-ib-hca "${NCCL_IB_HCA}" \
|
||||||
--nccl-cross-nic "${NCCL_CROSS_NIC}" \
|
--nccl-cross-nic "${NCCL_CROSS_NIC}" \
|
||||||
|
--enable-rdma "${ENABLE_RDMA}" \
|
||||||
|
--require-nccl-ib "${REQUIRE_NCCL_IB}" \
|
||||||
|
--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}"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user