856 lines
33 KiB
HTML
856 lines
33 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="color-scheme" content="light">
|
||
<title>Phase 1:DeepSeek-V4-Pro 双机 Pro6000D SGLang 快速性能地图</title>
|
||
<style>
|
||
:root {
|
||
--canvas: #f3f6f7;
|
||
--paper: #ffffff;
|
||
--ink: #172126;
|
||
--muted: #5d6b71;
|
||
--line: #d8e0e3;
|
||
--teal: #087e75;
|
||
--teal-soft: #e8f5f3;
|
||
--orange: #b65318;
|
||
--orange-soft: #fff1e8;
|
||
--code-bg: #18262b;
|
||
--code-ink: #eaf2f3;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
html {
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
color: var(--ink);
|
||
background: var(--canvas);
|
||
font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC",
|
||
Arial, sans-serif;
|
||
font-size: 16px;
|
||
line-height: 1.72;
|
||
}
|
||
|
||
a {
|
||
color: var(--teal);
|
||
text-underline-offset: 3px;
|
||
}
|
||
|
||
header {
|
||
color: #f7fbfb;
|
||
background: #17343a;
|
||
border-bottom: 5px solid #d76a2a;
|
||
}
|
||
|
||
.header-inner,
|
||
main {
|
||
width: min(100% - 40px, 1020px);
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.header-inner {
|
||
padding: 34px 0 30px;
|
||
}
|
||
|
||
.eyebrow {
|
||
margin: 0 0 6px;
|
||
color: #9edbd5;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
h1 {
|
||
max-width: 900px;
|
||
margin: 0;
|
||
font-size: clamp(28px, 4vw, 42px);
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px 20px;
|
||
margin-top: 16px;
|
||
color: #d4e4e6;
|
||
font-size: 14px;
|
||
}
|
||
|
||
main {
|
||
margin-top: 32px;
|
||
margin-bottom: 72px;
|
||
padding: 40px 50px 60px;
|
||
background: var(--paper);
|
||
border: 1px solid var(--line);
|
||
border-radius: 6px;
|
||
box-shadow: 0 12px 34px rgba(28, 43, 49, 0.07);
|
||
}
|
||
|
||
.back {
|
||
display: inline-block;
|
||
margin-bottom: 20px;
|
||
font-weight: 700;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.status {
|
||
margin: 0 0 28px;
|
||
padding: 14px 18px;
|
||
background: var(--teal-soft);
|
||
border-left: 4px solid var(--teal);
|
||
}
|
||
|
||
.status strong {
|
||
color: #075e58;
|
||
}
|
||
|
||
.decision {
|
||
padding: 14px 18px;
|
||
background: var(--teal-soft);
|
||
border-left: 4px solid var(--teal);
|
||
}
|
||
|
||
h2 {
|
||
margin: 48px 0 16px;
|
||
padding-bottom: 9px;
|
||
font-size: 25px;
|
||
line-height: 1.35;
|
||
border-bottom: 2px solid #afbcc1;
|
||
}
|
||
|
||
h2:first-of-type {
|
||
margin-top: 14px;
|
||
}
|
||
|
||
h3 {
|
||
margin: 30px 0 10px;
|
||
color: #21434a;
|
||
font-size: 19px;
|
||
}
|
||
|
||
p,
|
||
ul,
|
||
ol {
|
||
margin-top: 0;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
li + li {
|
||
margin-top: 5px;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
margin: 18px 0 28px;
|
||
border-collapse: collapse;
|
||
font-size: 14px;
|
||
}
|
||
|
||
th,
|
||
td {
|
||
padding: 10px 12px;
|
||
vertical-align: top;
|
||
text-align: left;
|
||
border: 1px solid var(--line);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
th {
|
||
color: #15393e;
|
||
background: #eaf2f2;
|
||
}
|
||
|
||
tbody tr:nth-child(even) {
|
||
background: #fafcfc;
|
||
}
|
||
|
||
code {
|
||
padding: 2px 5px;
|
||
color: #85380d;
|
||
background: var(--orange-soft);
|
||
border-radius: 3px;
|
||
font-family: "SFMono-Regular", Consolas, monospace;
|
||
font-size: 0.9em;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
pre {
|
||
margin: 18px 0 24px;
|
||
padding: 16px 18px;
|
||
overflow: auto;
|
||
color: var(--code-ink);
|
||
background: var(--code-bg);
|
||
border-radius: 6px;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
pre code {
|
||
padding: 0;
|
||
color: inherit;
|
||
background: transparent;
|
||
white-space: pre;
|
||
}
|
||
|
||
.pending {
|
||
color: var(--orange);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.pass {
|
||
color: var(--teal);
|
||
font-weight: 700;
|
||
}
|
||
|
||
@media (max-width: 680px) {
|
||
.header-inner,
|
||
main {
|
||
width: min(100% - 24px, 1020px);
|
||
}
|
||
|
||
main {
|
||
padding: 28px 18px 44px;
|
||
}
|
||
|
||
table {
|
||
display: block;
|
||
overflow-x: auto;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<div class="header-inner">
|
||
<p class="eyebrow">Implementation & Result Record</p>
|
||
<h1>Phase 1:DeepSeek-V4-Pro 双机 Pro6000D SGLang 快速性能地图</h1>
|
||
<div class="meta">
|
||
<span>节点:174.1.51.5 + 174.1.51.7</span>
|
||
<span>拓扑:SGLang TP16 / EP2</span>
|
||
<span>更新:2026-07-31 16:46:05 CST</span>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
<a class="back" href="./推理优化计划.html">返回推理优化主计划</a>
|
||
<a class="back" href="./phase1_code.html">打开 Phase 1 代码详解</a>
|
||
|
||
<p class="status">
|
||
<strong>阶段状态:已完成。</strong>
|
||
正式 Run <code>dsv4pro-phase1-full-20260730-220916</code> 在双 Rail
|
||
<code>NET/IB + GDRDMA</code> 下完成 9 个固定点和 3 个混合 A/B 结果,
|
||
共 12/12 成功,用时 28 分 36 秒。补充 Run
|
||
<code>dsv4pro-phase1-long-decode-20260730-234236</code> 完成长输出与
|
||
长上下文 Decode 2/2。两个 Run 合计 11 个固定点和 3 个混合结果,
|
||
14/14 成功;服务、容器与 16 张 GPU 已清理。
|
||
</p>
|
||
|
||
<h2>1. 目标与边界</h2>
|
||
<p>
|
||
用数小时以内、可重复的小矩阵替代约一天以上的全量扫描,先回答
|
||
Prefill、Decode、长上下文和混合干扰各自是否存在明显异常,再决定后续
|
||
Timeline 和 Kernel Profiling 的捕获对象。该阶段不要求为了“跑满表格”
|
||
而浪费算力;一旦出现稳定、可复现且足以改变调查方向的异常,就可以提前结束。
|
||
</p>
|
||
<ul>
|
||
<li>只测试 SGLang,不测试 vLLM。</li>
|
||
<li>使用双机 16 卡完整实例,不做 PD 分离。</li>
|
||
<li>不启用 MTP、EAGLE、DSpark 或其他投机解码。</li>
|
||
<li>本轮不启用 Profiler;正文只记录最终有效 Run,失败尝试仅在末尾总结经验。</li>
|
||
<li>不修改或调用旧的全天全量 Benchmark 脚本。</li>
|
||
</ul>
|
||
|
||
<h2>2. 精简实现</h2>
|
||
<p>实验代码位于:</p>
|
||
<pre><code>/data/hzy/sskj/experiments/pro6000/
|
||
dsv4pro_pro6000d_2node_sglang_tp16_quick_map/</code></pre>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>文件</th>
|
||
<th>职责</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>run_quick_map.sh</code></td>
|
||
<td>唯一 Shell 入口:双机服务启停、固定矩阵、混合 A/B、错误处理与清理</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>config.env</code></td>
|
||
<td>节点、模型、镜像、并行与容量参数</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>quick_map_scenarios.tsv</code></td>
|
||
<td>十一个固定工作负载点</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>quick_map_results.py</code></td>
|
||
<td>验证 Bench JSON,生成 CSV、JSONL 和 Markdown 汇总</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tests/test_quick_map_results.py</code></td>
|
||
<td>结果解析回归测试</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>单入口的操作面:</p>
|
||
<pre><code class="language-bash">bash run_quick_map.sh all
|
||
|
||
# 仅排障时使用同一个入口
|
||
bash run_quick_map.sh start
|
||
bash run_quick_map.sh fixed
|
||
bash run_quick_map.sh mixed
|
||
bash run_quick_map.sh stop</code></pre>
|
||
|
||
<h2>3. 服务配置</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>配置项</th>
|
||
<th>当前值</th>
|
||
<th>说明</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>镜像</td>
|
||
<td><code>lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45</code></td>
|
||
<td>沿用已验证可加载 DSV4-Pro 的版本</td>
|
||
</tr>
|
||
<tr>
|
||
<td>模型</td>
|
||
<td><code>/data/hf_models/DeepSeek-V4-Pro</code></td>
|
||
<td>两台节点均有本地权重</td>
|
||
</tr>
|
||
<tr>
|
||
<td>并行</td>
|
||
<td><code>TP=16, EP=2, nnodes=2</code></td>
|
||
<td>每台 8 卡,共 16 Rank</td>
|
||
</tr>
|
||
<tr>
|
||
<td>显存比例</td>
|
||
<td><code>0.9</code></td>
|
||
<td>保持已知基线,不在本阶段调参</td>
|
||
</tr>
|
||
<tr>
|
||
<td>活跃请求上限</td>
|
||
<td><code>256</code></td>
|
||
<td>覆盖本轮最大并发 64</td>
|
||
</tr>
|
||
<tr>
|
||
<td>CUDA Graph Decode BS</td>
|
||
<td><code>64</code></td>
|
||
<td>覆盖固定矩阵中的 Decode C64</td>
|
||
</tr>
|
||
<tr>
|
||
<td>NCCL Socket 接口</td>
|
||
<td><code>eth0</code></td>
|
||
<td>RDMA 失败回退时也承载跨机 Tensor,不只是 bootstrap</td>
|
||
</tr>
|
||
<tr>
|
||
<td>RoCE HCA</td>
|
||
<td><code>mlx5_0,mlx5_3</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>
|
||
<td>代码分支</td>
|
||
<td><code>hzy</code></td>
|
||
<td>从该维护分支向中央仓库 <code>main</code> 提交合并请求</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2>4. 固定快速矩阵</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Case ID</th>
|
||
<th>ISL</th>
|
||
<th>OSL</th>
|
||
<th>C</th>
|
||
<th>目的</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><code>short_prefill_latency_1k_c1</code></td><td>1K</td><td>1</td><td>1</td><td>最小 TTFT</td></tr>
|
||
<tr><td><code>mid_prefill_latency_32k_c1</code></td><td>32K</td><td>1</td><td>1</td><td>中长 Prefill</td></tr>
|
||
<tr><td><code>long_prefill_latency_128k_c1</code></td><td>128K</td><td>1</td><td>1</td><td>长上下文 Prefill</td></tr>
|
||
<tr><td><code>mid_prefill_throughput_32k_c16</code></td><td>32K</td><td>1</td><td>16</td><td>Prefill 输入吞吐</td></tr>
|
||
<tr><td><code>decode_latency_1k_to_1k_c1</code></td><td>1K</td><td>1K</td><td>1</td><td>单请求 TPOT</td></tr>
|
||
<tr><td><code>decode_throughput_1k_to_1k_c16</code></td><td>1K</td><td>1K</td><td>16</td><td>Decode 吞吐</td></tr>
|
||
<tr><td><code>decode_throughput_1k_to_1k_c32</code></td><td>1K</td><td>1K</td><td>32</td><td>Decode 吞吐</td></tr>
|
||
<tr><td><code>decode_throughput_1k_to_1k_c64</code></td><td>1K</td><td>1K</td><td>64</td><td>Decode 高并发</td></tr>
|
||
<tr><td><code>long_output_decode_1k_to_4k_c16</code></td><td>1K</td><td>4K</td><td>16</td><td>持续 Decode 与 KV 增长</td></tr>
|
||
<tr><td><code>long_context_decode_128k_to_1k_c1</code></td><td>128K</td><td>1K</td><td>1</td><td>长上下文上的 Decode 成本</td></tr>
|
||
<tr><td><code>balanced_32k_to_1k_c8</code></td><td>32K</td><td>1K</td><td>8</td><td>综合压力</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p>
|
||
快速 Run 使用一次重复和一波测量请求,即 <code>num_prompts=C</code>。
|
||
32K/128K Prefill 与 128K 长上下文 Decode 不做昂贵的同形状 Warm-up;
|
||
短 Prefill、普通 Decode 与 1K → 4K 长输出 Decode 使用一个 Warm-up,
|
||
并在正式计时前清空 Prefix Cache。固定矩阵不做 SLO 截断或自适应并发搜索。
|
||
</p>
|
||
|
||
<h2>5. SGLang Benchmark 与 Prefix Cache</h2>
|
||
<h3>5.1 <code>random</code> 如何生成 ISL</h3>
|
||
<p>
|
||
当前镜像的实现位于
|
||
<code>/sgl-workspace/sglang/python/sglang/benchmark/datasets/random.py</code>。
|
||
<code>dataset-name=random</code> 会读取 ShareGPT,打乱样本后取每条会话的首轮用户文本:
|
||
文本过长就截断,过短就重复其 token,直到达到目标 ISL。
|
||
<code>random-range-ratio=1.0</code> 使每条请求都使用精确的目标长度。
|
||
本机数据集共有 94,145 行,其中 92,886 行可用、71,904 个不同首轮文本,
|
||
因此不存在此前“两条数据只能形成两个并发请求”的问题。
|
||
</p>
|
||
<p>
|
||
<code>random-ids</code> 则直接构造随机整数 token id,不读取 ShareGPT。
|
||
当前源码同时警告这种方式可能触发 NaN,因此本阶段继续使用
|
||
<code>random + 大规模 ShareGPT</code>,并通过清缓存隔离不同测试点。
|
||
</p>
|
||
|
||
<h3>5.2 OSL 为什么能达到指定长度</h3>
|
||
<p>
|
||
SGLang 原生请求函数位于
|
||
<code>/sgl-workspace/sglang/python/sglang/benchmark/serving.py</code>。
|
||
它将目标 OSL 写入 <code>max_new_tokens</code>,并默认设置
|
||
<code>ignore_eos=True</code>。因此模型即使提前生成 EOS,也会继续生成到指定 OSL;
|
||
只有请求失败、超时或触及上下文限制时,实际输出才可能不足。
|
||
</p>
|
||
<pre><code class="language-python">sampling_params = {
|
||
"max_new_tokens": request_func_input.output_len,
|
||
"ignore_eos": not args.disable_ignore_eos,
|
||
}</code></pre>
|
||
|
||
<h3>5.3 为什么 Warm-up 会污染 Prefix Cache</h3>
|
||
<p>
|
||
SGLang benchmark 的 Warm-up 直接复用 <code>input_requests[0]</code>,
|
||
而正式测量随后仍会遍历包含该请求的完整列表。因此,只要服务启用了 Prefix Cache,
|
||
第一条正式请求就可能命中刚刚 Warm-up 的前缀。第一次 Run 的服务日志实际出现
|
||
<code>#cached-token: 768</code>,证明该污染在当前环境真实发生。
|
||
</p>
|
||
<p>
|
||
修复方式是在每个隔离测试点传入 <code>--flush-cache</code>。benchmark 会先完成
|
||
Warm-up,再调用服务端 <code>/flush_cache</code>,最后才启动计时。这样保留 Kernel
|
||
和执行路径预热,同时不把 Warm-up 的 KV 前缀带入测量。混合干扰中的长 Prefill
|
||
注入不会清缓存,避免在 Decode 背景运行时改变其服务状态;背景与注入使用不同随机种子。
|
||
</p>
|
||
|
||
<h3>5.4 如何单独测试 Prefix Caching</h3>
|
||
<ol>
|
||
<li>调用 <code>/flush_cache</code>,发送固定长 Prompt P,记录 Cold TTFT 和 <code>#cached-token</code>。</li>
|
||
<li>不清缓存,原样重发 P,记录 Warm TTFT;预期 cached token 明显增加、TTFT 降低。</li>
|
||
<li>再次清缓存,发送同长度但内容不同的 Prompt Q,排除长度、JIT 和偶然波动造成的假提升。</li>
|
||
</ol>
|
||
<p>
|
||
三组请求保持 OSL、采样参数和并发一致,各重复至少 3 次。Prefix Cache 是生产优化能力,
|
||
不是“坏东西”;这里只是在无缓存性能基线中隔离它,后续会把缓存命中场景作为单独 A/B。
|
||
</p>
|
||
|
||
<h2>6. 混合干扰实现</h2>
|
||
<p>
|
||
这里的“背景”不是 SGLang 后台线程,而是<strong>先启动并持续运行的一批
|
||
Decode 基准流量</strong>。它既在实验期间占用 GPU,也是我们希望观察是否
|
||
变慢的对象。混合 A/B 的问题非常具体:同样一批 Decode 请求,在没有长
|
||
Prefill 干扰和有长 Prefill 干扰时,性能会相差多少?
|
||
</p>
|
||
<table>
|
||
<thead>
|
||
<tr><th>组别</th><th>运行内容</th><th>作用</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>A:Control</td><td>仅运行 64 条 <code>1K → 1K, C=32</code> Decode</td><td>建立无干扰基线</td></tr>
|
||
<tr><td>B:Treatment</td><td>运行相同 Decode,并在正式测量开始 10 秒后注入一条 <code>128K → 1</code> Prefill</td><td>测量 Prefill 对 Decode 的干扰</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<ol>
|
||
<li>先完成 A 组,仅运行 Decode,保存对照指标。</li>
|
||
<li>启动 B 组的 Decode 基准流量,并从日志确认它已进入正式测量,而不只是完成客户端初始化。</li>
|
||
<li>正式测量开始 10 秒后,并行提交一个 <code>128K → 1</code> 长 Prefill。</li>
|
||
<li>等待两类请求都结束,分别保存 Decode 流量和长 Prefill 请求的结果。</li>
|
||
<li>用 A、B 两组 Decode 的 Output TPS、TTFT P95、TPOT P95 与 E2E P95 计算变化率;长 Prefill 自身的 TTFT 单独报告。</li>
|
||
</ol>
|
||
<pre><code>A:Decode ───────────────────────────────→ 结束
|
||
|
||
B:Decode ───────────────────────────────→ 结束
|
||
正式测量 + 10 秒
|
||
└─ 128K Prefill ─→ 结束
|
||
共同占用同一服务</code></pre>
|
||
<pre><code class="language-bash">(
|
||
run_bench_case ... 1024 1024 32 64
|
||
) &
|
||
background_pid=$!
|
||
|
||
# 实际代码先从 bench.log 确认正式测量已经开始。
|
||
sleep 10
|
||
run_bench_case ... 131072 1 1 1
|
||
wait "${background_pid}"</code></pre>
|
||
<p>
|
||
<code>&</code> 让 Decode benchmark 与后续 Prefill 并行;
|
||
<code>$!</code> 取得该 Decode benchmark 的进程号;
|
||
<code>wait</code> 等待它完成。总请求数 64、并发 32,表示最多同时有
|
||
32 条请求在途,通常形成约两波请求。如果 Decode 流量在注入前已经结束,
|
||
两类请求没有发生重叠,结果会被明确改写为
|
||
<code>BACKGROUND_FINISHED_BEFORE_INJECTION</code>,避免生成虚假的“混合成功”。
|
||
</p>
|
||
|
||
<h2>7. 结果与可追溯性</h2>
|
||
<pre><code>results/<RUN_ID>/
|
||
run_manifest.json
|
||
run.log
|
||
summary.csv
|
||
summary.jsonl
|
||
aggregate.csv
|
||
report.md
|
||
cases/<case_id>/rep1/
|
||
bench_cmd.txt
|
||
bench.jsonl
|
||
bench.log
|
||
meta.json
|
||
server/
|
||
head_server_cmd.txt
|
||
worker_server_cmd.txt
|
||
head_server.log
|
||
worker_server.log</code></pre>
|
||
<p>
|
||
汇总保留 Request/Input/Output/Total TPS,以及 E2E、TTFT、TPOT、ITL 的
|
||
Mean、P50、P95、P99。断点续跑前会重新解析原始 Bench JSON,不能只凭文件存在就跳过。
|
||
</p>
|
||
|
||
<h2>8. 已完成验证</h2>
|
||
<table>
|
||
<thead>
|
||
<tr><th>检查</th><th>结果</th><th>证据</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Shell 语法</td><td class="pass">通过</td><td><code>bash -n run_quick_map.sh</code></td></tr>
|
||
<tr><td>Python 单测</td><td class="pass">3/3 通过</td><td>场景唯一性、百分位回退、失败结果汇总</td></tr>
|
||
<tr><td>完整 Dry-run</td><td class="pass">通过</td><td>服务、十一个固定点、混合 A/B、清理均展开成功</td></tr>
|
||
<tr><td>真实旧 Bench JSON 解析</td><td class="pass">通过</td><td>成功解析 P50/P95/P99 与吞吐字段</td></tr>
|
||
<tr><td>项目精简</td><td class="pass">通过</td><td>实验目录顶层仅保留一个 Shell 入口</td></tr>
|
||
<tr><td>双 Rail 传输门禁</td><td class="pass">通过</td><td>Head 与 Worker 均识别 <code>mlx5_0/mlx5_3</code>,跨节点 Channel 使用 <code>NET/IB/*/GDRDMA</code></td></tr>
|
||
<tr><td>四点 Sanity</td><td class="pass">4/4 通过</td><td>1K/32K Prefill 与 C1/C32 Decode 均恢复到合理量级</td></tr>
|
||
<tr><td>冷 Prefix 口径</td><td class="pass">通过</td><td>正式测量请求的 Head 日志显示 <code>#cached-token: 0</code></td></tr>
|
||
<tr><td>完整真机 Run</td><td class="pass">12/12 通过</td><td>固定矩阵 9/9,混合 A/B 3/3,运行期失败 0</td></tr>
|
||
<tr><td>长 Decode 补测</td><td class="pass">2/2 通过</td><td>1K → 4K C16 与 128K → 1K C1 均生成完整目标 OSL</td></tr>
|
||
<tr><td>资源清理</td><td class="pass">通过</td><td>两节点相关容器与计算进程为 0,16 张 GPU 显存占用为 0</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h2>9. 最终真机结果</h2>
|
||
<p>
|
||
本节只使用正式成功 Run。Profiler 与投机解码均关闭,每个 Case 只做一次快速测量,
|
||
所以它适合决定下一步 Profile 对象,不作为需要统计置信度的最终容量认证。
|
||
</p>
|
||
|
||
<h3>9.1 执行摘要</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>项目</th><th>结果</th><th>证据</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Run ID</td><td><code>dsv4pro-phase1-full-20260730-220916</code></td><td><code>COMPLETED</code></td></tr>
|
||
<tr><td>运行时间</td><td>28 分 36 秒</td><td>22:09:47 至 22:38:22 CST</td></tr>
|
||
<tr><td>网络路径</td><td>双 Rail <code>NET/IB + GDRDMA</code></td><td><code>mlx5_0</code> 与 <code>mlx5_3</code></td></tr>
|
||
<tr><td>正式 Run 完整性</td><td>12/12 成功</td><td>固定点 9/9;混合 A/B 3/3</td></tr>
|
||
<tr><td>补充 Run</td><td><code>dsv4pro-phase1-long-decode-20260730-234236</code></td><td>固定点 2/2;约 12 分钟含服务启动与清理</td></tr>
|
||
<tr><td>阶段合计</td><td>14/14 成功</td><td>固定点 11/11;混合 A/B 3/3</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>9.2 Prefill</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>场景</th><th>Input TPS</th><th>TTFT P95</th><th>观察</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>1K → 1,C=1</td><td>1,969.66 tok/s</td><td>0.502 s</td><td>短请求固定开销占比更高</td></tr>
|
||
<tr><td>32K → 1,C=1</td><td>2,652.76 tok/s</td><td>12.335 s</td><td>单请求吞吐进入稳定区间</td></tr>
|
||
<tr><td>128K → 1,C=1</td><td>2,710.16 tok/s</td><td>48.344 s</td><td>长 Prefill 代表点</td></tr>
|
||
<tr><td>32K → 1,C=16</td><td>3,112.77 tok/s</td><td>162.087 s</td><td>聚合吞吐仅比 C=1 高 17.3%,排队时延显著增加</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>9.3 Decode</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>1K → 1K</th><th>Output TPS</th><th>TTFT P95</th><th>TPOT P95</th><th>E2E P95</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>C=1</td><td>31.41 tok/s</td><td>0.363 s</td><td>31.47 ms</td><td>32.555 s</td></tr>
|
||
<tr><td>C=16</td><td>295.29 tok/s</td><td>4.950 s</td><td>50.02 ms</td><td>55.444 s</td></tr>
|
||
<tr><td>C=32</td><td>461.68 tok/s</td><td>8.022 s</td><td>63.31 ms</td><td>70.933 s</td></tr>
|
||
<tr><td>C=64</td><td>647.42 tok/s</td><td>12.716 s</td><td>93.44 ms</td><td>101.163 s</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p>
|
||
Decode 吞吐到 C=64 仍在上升,但增益递减且 TPOT 明显变差。综合场景
|
||
<code>32K → 1K,C=8</code> 的 Input/Output TPS 为
|
||
<code>2,038.00 / 63.69</code>,TTFT P95 为 <code>82.084 s</code>,
|
||
说明 Prefill 与 Decode 同时存在时干扰很强。
|
||
</p>
|
||
|
||
<h3>9.4 长 Decode 补测</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>场景</th><th>Output TPS</th><th>TTFT P95</th><th>TPOT P95</th><th>E2E P95</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>1K → 4K,C=16</td><td>310.02 tok/s</td><td>6.241 s</td><td>50.33 ms</td><td>211.364 s</td></tr>
|
||
<tr><td>128K → 1K,C=1</td><td>12.43 tok/s</td><td>49.326 s</td><td>32.24 ms</td><td>82.312 s</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p>
|
||
<code>1K → 4K,C=16</code> 相比 <code>1K → 1K,C=16</code>,
|
||
Output TPS 增加 4.99%,TPOT P95 只增加 0.62%。较长 Decode 没有出现
|
||
稳态吞吐塌陷;吞吐略升是固定启动和 Prefill 成本被更多输出 token 摊薄。
|
||
</p>
|
||
<p>
|
||
<code>128K → 1K,C=1</code> 的 TTFT 只比 <code>128K → 1</code>
|
||
纯 Prefill 高 2.03%,而 TPOT P95 只比 <code>1K → 1K,C=1</code>
|
||
高 2.47%。因此这次长上下文请求的主要新增成本在 Prefill,而不是每个 Decode
|
||
token。表中的 12.43 Output TPS 是把 49 秒 Prefill 也计入总时长的端到端值,
|
||
不能把它误读为纯 Decode 速率。
|
||
</p>
|
||
|
||
<h3>9.5 混合 Prefill/Decode A/B</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>指标</th><th>A:仅 Decode</th><th>B:注入 128K Prefill</th><th>变化</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Output TPS</td><td>455.68 tok/s</td><td>345.95 tok/s</td><td>-24.08%</td></tr>
|
||
<tr><td>TTFT P95</td><td>9.443 s</td><td>10.194 s</td><td>+7.96%</td></tr>
|
||
<tr><td>TPOT P95</td><td>65.88 ms</td><td>109.73 ms</td><td>+66.55%</td></tr>
|
||
<tr><td>E2E P95</td><td>72.008 s</td><td>117.630 s</td><td>+63.36%</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="decision">
|
||
Phase 2 重放五个固定代表点:
|
||
<code>128K → 1,C=1</code>、<code>32K → 1,C=16</code>、
|
||
<code>1K → 1K,C=32</code>、<code>1K → 4K,C=16</code>、
|
||
<code>128K → 1K,C=1</code>,再执行有无 128K 注入的混合 A/B。
|
||
目标是区分计算、显存带宽、调度排队、跨机通信和节点不均衡。
|
||
</p>
|
||
<p>
|
||
完整产物:
|
||
<a href="./results/dsv4pro-phase1-full-20260730-220916/report.md">报告</a>、
|
||
<a href="./results/dsv4pro-phase1-full-20260730-220916/summary.csv">逐点汇总</a>、
|
||
<a href="./results/dsv4pro-phase1-full-20260730-220916/aggregate.csv">聚合表</a>、
|
||
<a href="./results/dsv4pro-phase1-full-20260730-220916/run_manifest.json">运行清单</a>;
|
||
长 Decode 补测的
|
||
<a href="./results/dsv4pro-phase1-long-decode-20260730-234236/report.md">报告</a>、
|
||
<a href="./results/dsv4pro-phase1-long-decode-20260730-234236/summary.csv">逐点汇总</a>
|
||
和
|
||
<a href="./results/dsv4pro-phase1-long-decode-20260730-234236/run_manifest.json">运行清单</a>。
|
||
</p>
|
||
|
||
<h2>10. 经验教训</h2>
|
||
<ul>
|
||
<li>启动参数不等于实际传输路径;开始性能测试前必须由 NCCL 日志证明 <code>NET/IB</code>。</li>
|
||
<li>Warm-up、固定随机种子和跨 Case Prefix Cache 会改变 TTFT,冷缓存与热缓存必须分开报告。</li>
|
||
<li>先跑四点 Sanity 再启动完整矩阵,可以在几分钟内验证环境、口径和数量级。</li>
|
||
</ul>
|
||
<p>
|
||
历史排查细节保存在
|
||
<a href="./results/script-audit-20260730/report.md">TTFT 脚本口径审计报告</a>
|
||
与
|
||
<a href="./results/network-path-audit-20260730/report.md">TP16 网络路径审计报告</a>,
|
||
不作为本阶段最终结果。
|
||
</p>
|
||
|
||
<h2>11. 实验复现命令</h2>
|
||
<p class="decision">
|
||
本节记录的是本阶段<strong>实际执行过</strong>的命令。长命令同时由程序原样保存到
|
||
<code>results/<RUN_ID>/server/*_server_cmd.txt</code> 和每个 Case 的
|
||
<code>bench_cmd.txt</code>;这些落盘文件是最终证据,正文中的换行仅用于阅读。
|
||
</p>
|
||
|
||
<h3>11.1 实际执行:完整 Phase 1</h3>
|
||
<p><strong>执行位置:</strong><code>174.1.51.5</code>;脚本通过 SSH 启动 <code>.7</code> Worker。</p>
|
||
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
|
||
|
||
tmux new-session -d -s dsv4pro-phase1-full \
|
||
"RUN_ID=dsv4pro-phase1-full-20260730-220916 bash run_quick_map.sh all \
|
||
2>&1 | tee /data/hzy/dsv4pro_phase1_full_20260730-220916.log"
|
||
|
||
tmux attach -t dsv4pro-phase1-full</code></pre>
|
||
<p>
|
||
<code>all</code> 的真实顺序是:
|
||
<code>Worker 启动 → Head 启动 → /health → NET/IB 门禁 → fixed → mixed → stop → summarize</code>。
|
||
</p>
|
||
|
||
<h3>11.2 实际执行:Worker 服务</h3>
|
||
<details>
|
||
<summary>展开 174.1.51.7 的完整 docker run</summary>
|
||
<pre><code class="language-bash">docker run -d \
|
||
--name dsv4pro_pro6000d_2node_sglang_tp16_quick_map_worker \
|
||
--gpus all \
|
||
--network host \
|
||
--ipc host \
|
||
--shm-size 20g \
|
||
--ulimit memlock=-1 \
|
||
--ulimit stack=67108864 \
|
||
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
|
||
-v /data/hzy/sglang_cache/dsv4_pro_tp16:/root/.cache \
|
||
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||
-e PYTHONUNBUFFERED=1 \
|
||
-e HF_HUB_OFFLINE=1 \
|
||
-e TRANSFORMERS_OFFLINE=1 \
|
||
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
|
||
-e NCCL_SOCKET_IFNAME=eth0 \
|
||
-e 'NCCL_IB_HCA==mlx5_0:1,mlx5_3:1' \
|
||
-e NCCL_CROSS_NIC=1 \
|
||
-e NCCL_DEBUG=INFO \
|
||
-e SGLANG_SHARED_EXPERT_TP1=1 \
|
||
--device /dev/infiniband/rdma_cm \
|
||
--device /dev/infiniband/uverbs0 \
|
||
--device /dev/infiniband/uverbs3 \
|
||
--entrypoint python3 \
|
||
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
|
||
-m sglang.launch_server \
|
||
--model-path /data/hf_models/DeepSeek-V4-Pro \
|
||
--tp-size 16 \
|
||
--ep-size 2 \
|
||
--nnodes 2 \
|
||
--node-rank 1 \
|
||
--dist-init-addr 10.101.0.11:20002 \
|
||
--trust-remote-code \
|
||
--host 0.0.0.0 \
|
||
--port 30002 \
|
||
--mem-fraction-static 0.9 \
|
||
--cuda-graph-max-bs-decode 64 \
|
||
--max-running-requests 256</code></pre>
|
||
</details>
|
||
|
||
<h3>11.3 实际执行:Head 服务</h3>
|
||
<details>
|
||
<summary>展开 174.1.51.5 的完整 docker run</summary>
|
||
<pre><code class="language-bash">docker run -d \
|
||
--name dsv4pro_pro6000d_2node_sglang_tp16_quick_map_head \
|
||
--gpus all \
|
||
--network host \
|
||
--ipc host \
|
||
--shm-size 20g \
|
||
--ulimit memlock=-1 \
|
||
--ulimit stack=67108864 \
|
||
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
|
||
-v /data/hzy/sglang_cache/dsv4_pro_tp16:/root/.cache \
|
||
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||
-e PYTHONUNBUFFERED=1 \
|
||
-e HF_HUB_OFFLINE=1 \
|
||
-e TRANSFORMERS_OFFLINE=1 \
|
||
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
|
||
-e NCCL_SOCKET_IFNAME=eth0 \
|
||
-e 'NCCL_IB_HCA==mlx5_0:1,mlx5_3:1' \
|
||
-e NCCL_CROSS_NIC=1 \
|
||
-e NCCL_DEBUG=INFO \
|
||
-e SGLANG_SHARED_EXPERT_TP1=1 \
|
||
--device /dev/infiniband/rdma_cm \
|
||
--device /dev/infiniband/uverbs0 \
|
||
--device /dev/infiniband/uverbs3 \
|
||
--entrypoint python3 \
|
||
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
|
||
-m sglang.launch_server \
|
||
--model-path /data/hf_models/DeepSeek-V4-Pro \
|
||
--tp-size 16 \
|
||
--ep-size 2 \
|
||
--nnodes 2 \
|
||
--node-rank 0 \
|
||
--dist-init-addr 10.101.0.11:20002 \
|
||
--trust-remote-code \
|
||
--host 0.0.0.0 \
|
||
--port 30002 \
|
||
--mem-fraction-static 0.9 \
|
||
--cuda-graph-max-bs-decode 64 \
|
||
--max-running-requests 256</code></pre>
|
||
</details>
|
||
<p>
|
||
<code>NCCL_IB_HCA==...</code> 的两个等号不是笔误:第一个是环境变量赋值分隔符,
|
||
第二个是 NCCL HCA 列表的“精确匹配”前缀。
|
||
</p>
|
||
|
||
<h3>11.4 实际执行:代表 Benchmark</h3>
|
||
<p>以下是正式 Run 的 <code>128K → 1, C=1</code> 冷 Prefix 命令:</p>
|
||
<details>
|
||
<summary>展开完整 sglang.benchmark.serving 命令</summary>
|
||
<pre><code class="language-bash">timeout --signal=TERM --kill-after=30s 7200s \
|
||
docker run --rm \
|
||
--network host \
|
||
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
|
||
-v /data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json:/data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json:ro \
|
||
-v /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1:/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1 \
|
||
-e PYTHONUNBUFFERED=1 \
|
||
-e HF_HUB_OFFLINE=1 \
|
||
-e TRANSFORMERS_OFFLINE=1 \
|
||
--entrypoint python3 \
|
||
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
|
||
-m sglang.benchmark.serving \
|
||
--backend sglang \
|
||
--host 10.101.0.11 \
|
||
--port 30002 \
|
||
--dataset-name random \
|
||
--dataset-path /data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json \
|
||
--random-input-len 131072 \
|
||
--random-output-len 1 \
|
||
--random-range-ratio 1.0 \
|
||
--num-prompts 1 \
|
||
--max-concurrency 1 \
|
||
--request-rate 10000 \
|
||
--output-file /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1/bench.jsonl \
|
||
--output-details \
|
||
--disable-tqdm \
|
||
--warmup-requests 0 \
|
||
--seed 42 \
|
||
--flush-cache</code></pre>
|
||
</details>
|
||
<p>
|
||
其余固定点使用同一命令模板,只替换 ISL、OSL、并发、请求数、Warm-up、Seed
|
||
和输出目录。每个点的最终展开命令保存在自己的 <code>bench_cmd.txt</code>。
|
||
混合 A/B 的并行启动顺序和两条请求命令见第 6 节及相应 Case 目录。
|
||
</p>
|
||
|
||
<h3>11.5 实际执行:长 Decode 补测</h3>
|
||
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
|
||
|
||
export CASE_IDS="long_output_decode_1k_to_4k_c16,long_context_decode_128k_to_1k_c1"
|
||
export RUN_ID="dsv4pro-phase1-long-decode-20260730-234236"
|
||
trap 'bash run_quick_map.sh stop' EXIT INT TERM
|
||
bash run_quick_map.sh start
|
||
bash run_quick_map.sh fixed</code></pre>
|
||
|
||
<h3>11.6 停止、清理与检查</h3>
|
||
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
|
||
bash run_quick_map.sh stop
|
||
|
||
curl -fsS http://10.101.0.11:30002/health || true
|
||
docker ps --filter name=dsv4pro_pro6000d_2node_sglang_tp16_quick_map
|
||
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv</code></pre>
|
||
|
||
<p>
|
||
下一阶段:
|
||
<a class="back" href="./phase2_exp.html">
|
||
打开 Phase 2 实验档案
|
||
</a>
|
||
</p>
|
||
<p><a class="back" href="./推理优化计划.html">返回推理优化主计划</a></p>
|
||
</main>
|
||
</body>
|
||
</html>
|