2026-07-31 17:38:36 +08:00

854 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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 1DeepSeek-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 &amp; Result Record</p>
<h1>Phase 1DeepSeek-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>AControl</td><td>仅运行 64 条 <code>1K → 1K, C=32</code> Decode</td><td>建立无干扰基线</td></tr>
<tr><td>BTreatment</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>ADecode ───────────────────────────────→ 结束
BDecode ───────────────────────────────→ 结束
正式测量 + 10 秒
└─ 128K Prefill ─→ 结束
共同占用同一服务</code></pre>
<pre><code class="language-bash">(
run_bench_case ... 1024 1024 32 64
) &amp;
background_pid=$!
# 实际代码先从 bench.log 确认正式测量已经开始。
sleep 10
run_bench_case ... 131072 1 1 1
wait "${background_pid}"</code></pre>
<p>
<code>&amp;</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/&lt;RUN_ID&gt;/
run_manifest.json
run.log
summary.csv
summary.jsonl
aggregate.csv
report.md
cases/&lt;case_id&gt;/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>两节点相关容器与计算进程为 016 张 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 → 1C=1</td><td>1,969.66 tok/s</td><td>0.502 s</td><td>短请求固定开销占比更高</td></tr>
<tr><td>32K → 1C=1</td><td>2,652.76 tok/s</td><td>12.335 s</td><td>单请求吞吐进入稳定区间</td></tr>
<tr><td>128K → 1C=1</td><td>2,710.16 tok/s</td><td>48.344 s</td><td>长 Prefill 代表点</td></tr>
<tr><td>32K → 1C=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 → 1KC=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 → 4KC=16</td><td>310.02 tok/s</td><td>6.241 s</td><td>50.33 ms</td><td>211.364 s</td></tr>
<tr><td>128K → 1KC=1</td><td>12.43 tok/s</td><td>49.326 s</td><td>32.24 ms</td><td>82.312 s</td></tr>
</tbody>
</table>
<p>
<code>1K → 4KC=16</code> 相比 <code>1K → 1KC=16</code>
Output TPS 增加 4.99%TPOT P95 只增加 0.62%。较长 Decode 没有出现
稳态吞吐塌陷;吞吐略升是固定启动和 Prefill 成本被更多输出 token 摊薄。
</p>
<p>
<code>128K → 1KC=1</code> 的 TTFT 只比 <code>128K → 1</code>
纯 Prefill 高 2.03%,而 TPOT P95 只比 <code>1K → 1KC=1</code>
高 2.47%。因此这次长上下文请求的主要新增成本在 Prefill而不是每个 Decode
token。表中的 12.43 Output TPS 是把 49 秒 Prefill 也计入总时长的端到端值,
不能把它误读为纯 Decode 速率。
</p>
<h3>9.5 混合 Prefill/Decode A/B</h3>
<table>
<thead>
<tr><th>指标</th><th>A仅 Decode</th><th>B注入 128K Prefill</th><th>变化</th></tr>
</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 → 1C=1</code><code>32K → 1C=16</code>
<code>1K → 1KC=32</code><code>1K → 4KC=16</code>
<code>128K → 1KC=1</code>,再执行有无 128K 注入的混合 A/B。
目标是区分计算、显存带宽、调度排队、跨机通信和节点不均衡。
</p>
<p>
完整产物:
<a href="./results/dsv4pro-phase1-full-20260730-220916/report.md">报告</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/summary.csv">逐点汇总</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/aggregate.csv">聚合表</a>
<a href="./results/dsv4pro-phase1-full-20260730-220916/run_manifest.json">运行清单</a>
长 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/&lt;RUN_ID&gt;/server/*_server_cmd.txt</code> 和每个 Case 的
<code>bench_cmd.txt</code>;这些落盘文件是最终证据,正文中的换行仅用于阅读。
</p>
<h3>11.1 实际执行:完整 Phase 1</h3>
<p><strong>执行位置:</strong><code>174.1.51.5</code>;脚本通过 SSH 启动 <code>.7</code> Worker。</p>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
tmux new-session -d -s dsv4pro-phase1-full \
"RUN_ID=dsv4pro-phase1-full-20260730-220916 bash run_quick_map.sh all \
2&gt;&amp;1 | tee /data/hzy/dsv4pro_phase1_full_20260730-220916.log"
tmux attach -t dsv4pro-phase1-full</code></pre>
<p>
<code>all</code> 的真实顺序是:
<code>Worker 启动 → Head 启动 → /health → NET/IB 门禁 → fixed → mixed → stop → summarize</code>
</p>
<h3>11.2 实际执行Worker 服务</h3>
<details>
<summary>展开 174.1.51.7 的完整 docker run</summary>
<pre><code class="language-bash">docker run -d \
--name dsv4pro_pro6000d_2node_sglang_tp16_quick_map_worker \
--gpus all \
--network host \
--ipc host \
--shm-size 20g \
--ulimit memlock=-1 \
--ulimit stack=67108864 \
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
-v /data/hzy/sglang_cache/dsv4_pro_tp16:/root/.cache \
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
-e NCCL_SOCKET_IFNAME=eth0 \
-e 'NCCL_IB_HCA==mlx5_0:1,mlx5_3:1' \
-e NCCL_CROSS_NIC=1 \
-e NCCL_DEBUG=INFO \
-e SGLANG_SHARED_EXPERT_TP1=1 \
--device /dev/infiniband/rdma_cm \
--device /dev/infiniband/uverbs0 \
--device /dev/infiniband/uverbs3 \
--entrypoint python3 \
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
-m sglang.launch_server \
--model-path /data/hf_models/DeepSeek-V4-Pro \
--tp-size 16 \
--ep-size 2 \
--nnodes 2 \
--node-rank 1 \
--dist-init-addr 10.101.0.11:20002 \
--trust-remote-code \
--host 0.0.0.0 \
--port 30002 \
--mem-fraction-static 0.9 \
--cuda-graph-max-bs-decode 64 \
--max-running-requests 256</code></pre>
</details>
<h3>11.3 实际执行Head 服务</h3>
<details>
<summary>展开 174.1.51.5 的完整 docker run</summary>
<pre><code class="language-bash">docker run -d \
--name dsv4pro_pro6000d_2node_sglang_tp16_quick_map_head \
--gpus all \
--network host \
--ipc host \
--shm-size 20g \
--ulimit memlock=-1 \
--ulimit stack=67108864 \
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
-v /data/hzy/sglang_cache/dsv4_pro_tp16:/root/.cache \
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-e PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
-e NCCL_SOCKET_IFNAME=eth0 \
-e 'NCCL_IB_HCA==mlx5_0:1,mlx5_3:1' \
-e NCCL_CROSS_NIC=1 \
-e NCCL_DEBUG=INFO \
-e SGLANG_SHARED_EXPERT_TP1=1 \
--device /dev/infiniband/rdma_cm \
--device /dev/infiniband/uverbs0 \
--device /dev/infiniband/uverbs3 \
--entrypoint python3 \
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
-m sglang.launch_server \
--model-path /data/hf_models/DeepSeek-V4-Pro \
--tp-size 16 \
--ep-size 2 \
--nnodes 2 \
--node-rank 0 \
--dist-init-addr 10.101.0.11:20002 \
--trust-remote-code \
--host 0.0.0.0 \
--port 30002 \
--mem-fraction-static 0.9 \
--cuda-graph-max-bs-decode 64 \
--max-running-requests 256</code></pre>
</details>
<p>
<code>NCCL_IB_HCA==...</code> 的两个等号不是笔误:第一个是环境变量赋值分隔符,
第二个是 NCCL HCA 列表的“精确匹配”前缀。
</p>
<h3>11.4 实际执行:代表 Benchmark</h3>
<p>以下是正式 Run 的 <code>128K → 1, C=1</code> 冷 Prefix 命令:</p>
<details>
<summary>展开完整 sglang.benchmark.serving 命令</summary>
<pre><code class="language-bash">timeout --signal=TERM --kill-after=30s 7200s \
docker run --rm \
--network host \
-v /data/hf_models/DeepSeek-V4-Pro:/data/hf_models/DeepSeek-V4-Pro:ro \
-v /data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json:/data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json:ro \
-v /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1:/data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1 \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
--entrypoint python3 \
lmsysorg/sglang:nightly-dev-cu13-20260720-b3570a45 \
-m sglang.benchmark.serving \
--backend sglang \
--host 10.101.0.11 \
--port 30002 \
--dataset-name random \
--dataset-path /data/yy/sskj/dataset/ShareGPT_V3_unfiltered_cleaned_split.json \
--random-input-len 131072 \
--random-output-len 1 \
--random-range-ratio 1.0 \
--num-prompts 1 \
--max-concurrency 1 \
--request-rate 10000 \
--output-file /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/dsv4pro-phase1-full-20260730-220916/cases/long_prefill_latency_128k_c1/rep1/bench.jsonl \
--output-details \
--disable-tqdm \
--warmup-requests 0 \
--seed 42 \
--flush-cache</code></pre>
</details>
<p>
其余固定点使用同一命令模板,只替换 ISL、OSL、并发、请求数、Warm-up、Seed
和输出目录。每个点的最终展开命令保存在自己的 <code>bench_cmd.txt</code>
混合 A/B 的并行启动顺序和两条请求命令见第 6 节及相应 Case 目录。
</p>
<h3>11.5 实际执行:长 Decode 补测</h3>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
export CASE_IDS="long_output_decode_1k_to_4k_c16,long_context_decode_128k_to_1k_c1"
export RUN_ID="dsv4pro-phase1-long-decode-20260730-234236"
trap 'bash run_quick_map.sh stop' EXIT INT TERM
bash run_quick_map.sh start
bash run_quick_map.sh fixed</code></pre>
<h3>11.6 停止、清理与检查</h3>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map
bash run_quick_map.sh stop
curl -fsS http://10.101.0.11:30002/health || true
docker ps --filter name=dsv4pro_pro6000d_2node_sglang_tp16_quick_map
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv</code></pre>
<p>
下一阶段:
<a class="back" href="./phase2_exp.html">打开 Phase 2 实验档案</a>
</p>
<p><a class="back" href="./推理优化计划.html">返回推理优化主计划</a></p>
</main>
</body>
</html>