2026-07-31 17:00:22 +08:00

770 lines
41 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">
<title>Phase 2DeepSeek-V4-Pro 双机 Pro6000D SGLang 硬件与资源竞争归因</title>
<style>
:root {
color-scheme: light;
--ink: #18202a;
--muted: #5b6570;
--line: #d8dde3;
--paper: #ffffff;
--page: #f3f5f7;
--blue: #1769aa;
--green: #16734a;
--amber: #9a5a00;
--red: #a13232;
--code: #f0f3f6;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--page);
color: var(--ink);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 16px;
line-height: 1.65;
}
header {
background: #202a34;
color: #fff;
border-bottom: 5px solid #34a17a;
}
.header-inner, main {
width: min(1120px, calc(100% - 32px));
margin: 0 auto;
}
.header-inner { padding: 38px 0 32px; }
h1, h2, h3 { letter-spacing: 0; }
h1 {
margin: 4px 0 12px;
font-size: clamp(26px, 4vw, 42px);
line-height: 1.2;
}
h2 {
margin: 38px 0 12px;
padding-bottom: 7px;
border-bottom: 2px solid var(--line);
font-size: 24px;
}
h3 { margin: 26px 0 8px; font-size: 19px; }
.eyebrow {
margin: 0;
color: #8fd8bd;
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 8px 22px;
color: #d7dee5;
font-size: 14px;
}
main {
margin-top: 24px;
margin-bottom: 48px;
padding: 30px 38px 42px;
background: var(--paper);
border: 1px solid var(--line);
border-radius: 6px;
}
.status {
padding: 14px 16px;
border-left: 4px solid var(--amber);
background: #fff7e7;
}
.decision {
padding: 14px 16px;
border-left: 4px solid var(--green);
background: #eef8f3;
}
a { color: var(--blue); }
.back {
display: inline-block;
margin-bottom: 10px;
font-weight: 650;
}
code {
padding: 1px 5px;
background: var(--code);
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.92em;
}
pre {
overflow-x: auto;
padding: 14px 16px;
background: #202a34;
color: #f4f7fa;
border-radius: 5px;
line-height: 1.5;
}
pre code { padding: 0; background: transparent; color: inherit; }
table {
width: 100%;
margin: 14px 0 22px;
border-collapse: collapse;
font-size: 14px;
}
th, td {
padding: 10px 11px;
border: 1px solid var(--line);
text-align: left;
vertical-align: top;
}
th { background: #edf1f4; }
.pass { color: var(--green); font-weight: 700; }
.pending { color: var(--amber); font-weight: 700; }
.fail { color: var(--red); font-weight: 700; }
li + li { margin-top: 5px; }
@media (max-width: 720px) {
main { padding: 22px 18px 30px; }
table { display: block; overflow-x: auto; white-space: nowrap; }
}
</style>
</head>
<body>
<header>
<div class="header-inner">
<p class="eyebrow">Design, Implementation &amp; Result Record</p>
<h1>Phase 2DeepSeek-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="./phase2_code.html">打开 Phase 2 代码详解</a>
<p class="status">
<strong>当前状态Phase 2 最终采集代码已完成,等待最终双机复跑。</strong>
首次 Run <code>dsv4pro-phase2-20260731-130125</code> 的 8/8 个 benchmark
均成功;提交 <code>30664faa41f8</code> 已补齐正式测量窗口、双节点 DCGM 门禁、
低开销进程采样、机内 PCIe P2P、单/双机 AllReduce 与逐指标自动报告。
本阶段复跑完成并汇报后才进入 Phase 3。
</p>
<h2>1. Phase 1 交接结果</h2>
<table>
<thead>
<tr><th>代表负载</th><th>关键结果</th><th>Phase 2 用途</th></tr>
</thead>
<tbody>
<tr><td>128K → 1C=1</td><td>Input TPS 2,710.16TTFT P95 48.344 s</td><td>纯长 Prefill 的计算、显存与通信归因</td></tr>
<tr><td>32K → 1C=16</td><td>Input TPS 3,112.77TTFT P95 162.087 s</td><td>并发 Prefill 的排队、Chunk 调度与节点均衡</td></tr>
<tr><td>1K → 1KC=32</td><td>Output TPS 461.68TPOT P95 63.31 ms</td><td>普通 Decode 的 GPU、CPU 与通信基线</td></tr>
<tr><td>1K → 4KC=16</td><td>Output TPS 310.02TPOT P95 50.33 ms</td><td>持续 Decode、KV 增长和稳态资源占用</td></tr>
<tr><td>128K → 1KC=1</td><td>TTFT P95 49.326 sTPOT P95 32.24 ms</td><td>分离长 Prefill 与长上下文 Decode 成本</td></tr>
<tr><td>1K → 1KC=32 + 128K 注入</td><td>Output TPS -24.08%TPOT P95 +66.55%</td><td>Prefill 干扰 Decode 时的硬件资源竞争</td></tr>
</tbody>
</table>
<p>
最终基线已由 Head 与 Worker 日志证明使用
<code>mlx5_0/mlx5_3</code> 双 Rail <code>NET/IB + GDRDMA</code>
正式测量请求为冷 Prefix。正式矩阵 12/12、长 Decode 补测 2/2 均成功。
Phase 2 保持相同服务配置和请求口径。
</p>
<h2>2. 本阶段的边界</h2>
<ul>
<li>只测试 SGLang不测试 vLLM。</li>
<li>保留模型、镜像、TP16、EP2、显存比例和已验证的双 Rail NCCL 配置。</li>
<li>模型端到端 Case 不启用 Nsight Systems、PyTorch Profiler、NCCL DEBUG 或投机解码;独立通信基线临时启用 NCCL INFO 以保存实际路径证据。</li>
<li>不调参,不尝试优化;先获得足以区分瓶颈类别的硬件证据。</li>
<li>只重放五个固定代表负载和一组混合 A/B不重复 Phase 1 全矩阵。</li>
<li>采集器从请求开始前启动,到请求结束后停止,不能中途补采后声称完整。</li>
</ul>
<h2>3. 待验证假设</h2>
<table>
<thead>
<tr><th>假设</th><th>预期硬件表现</th><th>后续方向</th></tr>
</thead>
<tbody>
<tr><td>DSV4/NSA Prefill Kernel 计算受限</td><td>GPU 持续忙、高功耗和稳定频率;双 Rail 流量不高</td><td>Phase 3 捕获 Kernel 与 Attention/Indexer 时间线</td></tr>
<tr><td>权重或激活显存带宽受限</td><td>GPU Memory Utilization 高SM 指标未必饱和;功耗可能低于纯计算</td><td>补 DCGM/Profiler 的 DRAM Active再看 Kernel</td></tr>
<tr><td>TP16 跨机通信受限</td><td>RoCE 吞吐高或两条 Rail 明显失衡GPU 出现等待</td><td>NCCL_CROSS_NIC 0/1/2 快速 A/B随后看 NCCL Timeline</td></tr>
<tr><td>CPU Scheduler 或 Kernel Launch 受限</td><td>GPU 利用率锯齿或有空洞,单 CPU 核持续满载</td><td>定位 Scheduler/Tokenizer 线程与 launch gap</td></tr>
<tr><td>频率、功耗或温度限制</td><td>P-state、SM Clock 或 Power 持续异常,可能出现节流原因</td><td>修正电源、散热或 Clock Policy 后复测</td></tr>
<tr><td>节点或 Rank 不均衡</td><td>两节点或不同 GPU 的利用率、功耗、网络流量存在固定偏差</td><td>检查 NUMA、GPU-NIC 亲和与慢 Rank</td></tr>
</tbody>
</table>
<h2>4. 诊断 Run</h2>
<ol>
<li>确认 16 张 GPU 空闲,先跑两节点 PCIe P2P、单机 8 rank AllReduce 和双机 16 rank AllReduce双机分别测试 <code>NCCL_CROSS_NIC=0/1/2</code></li>
<li>保存两节点静态快照GPU/NIC/NUMA 拓扑、驱动、CUDA、镜像与服务命令。</li>
<li>复用 Phase 1 已验证的 <code>run_quick_map.sh start</code> 启动同配置双机服务。</li>
<li>在 Head 和 Worker 同时启动 GPU、CPU、网卡与 RDMA 采样,先记录 15 秒空闲基线。</li>
<li>依次重放 <code>128K → 1, C=1</code><code>32K → 1, C=16</code><code>1K → 1K, C=32</code></li>
<li>重放 <code>1K → 4K, C=16</code><code>128K → 1K, C=1</code>,观察持续与长上下文 Decode。</li>
<li>重放 <code>1K → 1K, C=32</code> Control 与 128K Prefill 注入 Treatment保留相同注入时序。</li>
<li>请求结束后继续采样 15 秒,再停止采集器和服务。</li>
<li>按时间戳将请求、GPU、CPU 和双 Rail 指标对齐,生成摘要与判定。</li>
</ol>
<pre><code>idle 15s
│ 128K→1 C1 │ 32K→1 C16 │ 1K→1K C32
│ 1K→4K C16 │ 128K→1K C1
│ Decode Control │ Decode + Prefill
cooldown 15s
Head 与 Worker 的所有采集器覆盖完整诊断窗口。</code></pre>
<p>
Phase 1 中服务加载约 5 分 30 秒;通信基线、五个固定负载、混合 A/B、
静态快照、采样和清理组成一次完整 Phase 2 Run。
</p>
<h3>4.1 你只需要运行的入口</h3>
<p class="decision">
<strong>操作规则:先在 Worker <code>.7</code> 做一次 DCGM 准备,再只在
Head <code>.5</code> 执行 Phase 2 的 <code>all</code></strong>
不要手工执行 Phase 1 的 <code>start</code><code>stop</code>
Phase 2 会在内部复用它们并负责异常退出时的采集器、Head、Worker 清理。
</p>
<pre><code class="language-bash"># [仅在 Worker 174.1.51.7 执行一次]
# 不需要 source、conda activate也不要在 .7 运行 Phase 2 的 all
systemctl start nvidia-dcgm
systemctl is-active nvidia-dcgm
dcgmi discovery -l
# 预期:第二条输出 active第三条列出本机 8 张 GPU
# [以下仅在 Head 174.1.51.5 执行]
cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution
# 第一次先展开全部命令,不启动服务、不占用 GPU、不发送请求
DRY_RUN=1 RUN_ID=dsv4pro-phase2-dryrun-$(date +%Y%m%d-%H%M%S) \
bash run_hardware_contention_attribution.sh all
# 正式实验:仍然只有同一个 all 入口tmux 只负责断线后继续运行
RUN_ID=dsv4pro-phase2-$(date +%Y%m%d-%H%M%S)
tmux new-session -d -s dsv4pro-phase2 \
"RUN_ID=${RUN_ID} bash run_hardware_contention_attribution.sh all \
2&gt;&amp;1 | tee /data/hzy/${RUN_ID}.log"
tmux attach -t dsv4pro-phase2</code></pre>
<p>
<code>.7</code> 的三条命令只负责让 Worker DCGM Host Engine 可用;
SGLang Worker、其余采集器和结果回收仍由 <code>.5</code> 的唯一入口通过 SSH 管理。
若需要机器重启后自动启动 DCGM应由运维另行决定是否执行
<code>systemctl enable nvidia-dcgm</code>
</p>
<p><strong><code>all</code> 内部执行顺序:</strong></p>
<pre><code>配置、工具、DCGM 与 GPU 空闲门禁
→ 通信基线:两节点 P2P、两组单机 8-rank AllReduce、
三组双机 16-rank NCCL_CROSS_NIC A/B
→ Phase 1 start启动同配置 TP16 服务
→ 两节点静态快照
→ 启动两节点采集器并记录 15 秒 idle
→ 五个固定 Case
→ 混合 Prefill/Decode A/B
→ 15 秒 cooldown
→ 停止采集器并保存后快照
→ Phase 1 stop停止 Head/Worker
→ 生成按第 5 节逐项对应的 CSV、JSON 与 report.md</code></pre>
<p>
Phase 1 的作用是提供已经验证过的双机 Docker 服务和 Benchmark 实现,
不是第二个用户入口。实际展开的服务、Benchmark 和采集命令都会写入
<code>results/&lt;RUN_ID&gt;/service/</code><code>commands/</code>
<code>head|worker/collector_commands/</code>,不依赖跨文档猜测。
</p>
<h2>5. 采集指标</h2>
<p>
本节记录正式实现使用的命令,而不是建议性伪代码。命令由
<code>run_hardware_contention_attribution.sh</code> 在 Head 和 Worker 同时启动;
每条展开后的命令会另外保存在
<code>results/&lt;RUN_ID&gt;/head|worker/collector_commands/</code>
</p>
<table>
<thead>
<tr><th>层级</th><th>连续采样</th><th>静态或前后快照</th></tr>
</thead>
<tbody>
<tr><td>GPU</td><td>利用率、Memory Utilization、显存、功耗、SM/Memory Clock、温度、P-state</td><td><code>nvidia-smi topo -m</code>、Compute Process</td></tr>
<tr><td>CPU</td><td>每核利用率、上下文切换、服务进程 CPU/内存</td><td>NUMA 拓扑、容器 PID 与 CPU Affinity</td></tr>
<tr><td>Network</td><td><code>eth0/eth3</code> RX/TX</td><td><code>ethtool -S</code> 错误计数前后差</td></tr>
<tr><td>RDMA</td><td><code>mlx5_0/mlx5_3</code> port_xmit/recv_data 差分</td><td>Port State、GID 与错误计数</td></tr>
<tr><td>DCGM</td><td>SM Active、DRAM Active、Tensor Active、PCIe</td><td>工具版本与可用 Field</td></tr>
</tbody>
</table>
<h3>5.1 时间对齐与 Case Marker</h3>
<pre><code class="language-bash"># 每条 GPU/RDMA 样本写入相同格式的宿主机墙钟时间
date +%s%N
# 实验前检查两节点秒级时钟差
date +%s
# Case 开始、结束和服务状态由 Python 写入 markers.csv
python3 hardware_contention_attribution.py marker \
--path markers.csv \
--node head \
--event case_start \
--case-id long_prefill_latency_128k_c1</code></pre>
<table>
<thead><tr><th>数据</th><th>含义</th><th>为什么需要</th></tr></thead>
<tbody>
<tr><td><code>wall_time_ns</code></td><td>Unix Epoch 纳秒时间</td><td>把 GPU、CPU、RDMA 与 Benchmark 放到同一时间轴</td></tr>
<tr><td><code>case_start/case_end</code></td><td>一个 Case 的编排边界</td><td>从整段连续采样中切出对应负载</td></tr>
<tr><td><code>CLOCK_SKEW_TOLERANCE_S=2</code></td><td>两节点允许的最大秒级时钟差</td><td>避免 Head/Worker 的同一时刻被错位比较</td></tr>
</tbody>
</table>
<p>
最终实现由 Phase 1 监听 <code>bench.log</code> 中的
<code>Starting main benchmark run</code>,立刻写入
<code>measurement_start.json</code>;再使用 SGLang <code>bench.json</code>
的正式 benchmark duration 计算结束时间。Phase 2 优先读取
<code>measurement_started_at/measurement_ended_at</code>,不会把数据生成和
Warm-up 混入硬件均值。<code>REQUIRE_PRECISE_WINDOWS=1</code> 时,任何 Case
缺少精确窗口都会让汇总失败,而不是悄悄回退。
</p>
<h3>5.2 GPU 基础状态:<code>nvidia-smi</code></h3>
<pre><code class="language-bash">nvidia-smi \
--query-gpu=index,timestamp,utilization.gpu,utilization.memory,\
memory.used,memory.total,power.draw,temperature.gpu,\
clocks.sm,clocks.mem,pstate \
--format=csv,noheader,nounits</code></pre>
<p>脚本每秒运行一次,并在每行前加入 <code>wall_time_ns</code> 和节点角色。</p>
<table>
<thead><tr><th>字段</th><th>代表什么</th></tr></thead>
<tbody>
<tr><td><code>utilization.gpu</code></td><td>采样周期内至少有一个 Kernel 在执行的时间比例</td></tr>
<tr><td><code>utilization.memory</code></td><td>采样周期内显存控制器处于忙碌状态的时间比例</td></tr>
<tr><td><code>memory.used/total</code></td><td>当前总显存分配量与设备显存容量</td></tr>
<tr><td><code>power.draw</code></td><td>GPU 当前功耗,用于比较不同负载的能耗状态</td></tr>
<tr><td><code>clocks.sm/clocks.mem</code></td><td>SM 与显存当前工作频率</td></tr>
<tr><td><code>pstate</code></td><td>GPU 性能状态P0 通常是最高性能态</td></tr>
</tbody>
</table>
<p>
原始输出为 <code>head|worker/gpu_samples.csv</code>
<code>gpu_summary.csv</code> 汇总整段运行,
<code>case_gpu_summary.csv</code> 按节点、Case 和 GPU 汇总平均值与峰值。
</p>
<h3>5.3 GPU Profiling CounterDCGM</h3>
<pre><code class="language-bash">DCGM_FIELD_IDS=1001,1002,1003,1004,1005,1009,1010
dcgmi dmon \
-e 1001,1002,1003,1004,1005,1009,1010 \
-d 1000</code></pre>
<table>
<thead><tr><th>Field ID</th><th>Field Tag</th><th>含义</th></tr></thead>
<tbody>
<tr><td>1001</td><td><code>gr_engine_active</code></td><td>Graphics/Compute Engine 活跃比例,接近整体 GPU 执行忙碌度</td></tr>
<tr><td>1002</td><td><code>sm_active</code></td><td>SM 至少有一个 Warp 活跃的比例</td></tr>
<tr><td>1003</td><td><code>sm_occupancy</code></td><td>活跃 Warp 相对硬件可容纳 Warp 的比例</td></tr>
<tr><td>1004</td><td><code>tensor_active</code></td><td>Tensor Core 指令活跃比例</td></tr>
<tr><td>1005</td><td><code>dram_active</code></td><td>设备显存接口活跃比例Pro6000D 为 GDDR7用于判断设备显存带宽压力</td></tr>
<tr><td>1009</td><td><code>pcie_tx_bytes</code></td><td>GPU 经 PCIe 发出的字节速率</td></tr>
<tr><td>1010</td><td><code>pcie_rx_bytes</code></td><td>GPU 经 PCIe 接收的字节速率</td></tr>
</tbody>
</table>
<p>
<code>sm_active</code> 高而 <code>sm_occupancy</code> 低,表示 SM 经常有工作,
但同时驻留的 Warp 不多;后续通过 Kernel Timeline 区分小 Kernel、
寄存器/共享内存约束和同步。DCGM 是 NVIDIA Data Center GPU Manager
<code>nvidia-dcgm</code>/<code>nv-hostengine</code> 是后台 Host Engine
<code>dcgmi</code> 是客户端Field ID 是指标编号。最终代码在两节点预检
<code>dcgmi discovery -l</code>,任一 Host Engine 不可用即 fail-fast
正式结果必须同时包含 Head 和 Worker 的 <code>case_dcgm_summary.csv</code>
</p>
<h3>5.4 CPU、进程与 Kernel Launch 侧证据</h3>
<pre><code class="language-bash"># 全部逻辑 CPU每 5 秒输出一次
mpstat -P ALL 5
# 找到容器内进程对应的宿主 PID
docker top &lt;container&gt; -eo pid,ppid,psr,pcpu,pmem,stat,comm,args
# 最终命令:进程级 CPU、I/O、缺页、上下文切换不展开全部线程
pidstat -durw -p "&lt;comma-separated-host-pids&gt;" 5
# 每 5 秒输出一次硬件/软件计数器增量
perf stat -p "&lt;comma-separated-host-pids&gt;" -I 5000 \
-e cycles,instructions,cache-misses,context-switches,\
cpu-migrations,page-faults
# mpstat/pidstat/perf 每行都由包装器增加:
# wall_time_ns TAB node TAB 原始输出</code></pre>
<table>
<thead><tr><th>命令/字段</th><th>回答的问题</th></tr></thead>
<tbody>
<tr><td><code>mpstat -P ALL</code></td><td>整机是否 CPU 饱和,是否只有少量核心接近 100%,是否存在 I/O Wait</td></tr>
<tr><td><code>docker top</code></td><td>把容器进程映射为宿主 PID、CPU 核 <code>PSR</code> 和进程状态</td></tr>
<tr><td><code>pidstat -u</code></td><td>服务进程的用户态、内核态 CPU 时间</td></tr>
<tr><td><code>pidstat -d</code></td><td>进程块设备 I/O</td></tr>
<tr><td><code>pidstat -r</code></td><td>内存和 Page Fault 行为</td></tr>
<tr><td><code>pidstat -w</code></td><td>主动/被动上下文切换,辅助发现线程阻塞或调度抖动</td></tr>
<tr><td><code>perf cycles/instructions</code></td><td>CPU 周期与指令执行量,可计算近似 IPC</td></tr>
<tr><td><code>cache-misses/migrations</code></td><td>CPU Cache 压力和线程跨核迁移</td></tr>
</tbody>
</table>
<p>
最终实现使用进程级 5 秒采样,避免首轮线程级 1 秒采样产生数百 MB 日志。
<code>case_cpu_summary.csv</code><code>case_process_summary.csv</code>
<code>case_perf_summary.csv</code> 都按正式测量窗口切片;只有先发现异常进程,
才在后续短窗口单独开启线程级采样。
</p>
<h3>5.5 NUMA 与 CPU/内存亲和</h3>
<pre><code class="language-bash"># 静态 NUMA 节点、CPU 和内存布局
numactl --hardware
numastat -m
# 每 5 秒按容器宿主 PID 查看本地/远端 NUMA 内存
numastat -p &lt;host-pid&gt;
# 解析为:
# wall_time_ns,node,node0_mib,node1_mib,total_mib,process_count
# 同时保存 GPU、CPU、NIC 的拓扑关系
nvidia-smi topo -m</code></pre>
<p>
NUMA 是多路 CPU 机器的“本地内存”结构。进程长期从远端 NUMA Node 取内存,
或 GPU/NIC 对应的 CPU 线程被调度到另一侧,可能增加 Host 侧延迟。
最终采集器把 <code>numastat -p</code> 解析为
<code>numa_samples.csv</code>,再按正式测量窗口生成
<code>case_numa_summary.csv</code>。这样可以直接比较 Node0/Node1 MiB
而不是依靠人工阅读不断刷新的文本。
</p>
<h3>5.6 普通网卡统计与 RDMA 数据面</h3>
<pre><code class="language-bash"># Linux netdev 层,每 5 秒采样吞吐与错误
sar -n DEV,EDEV 5
# Case 前后保存物理端口状态和驱动计数器
ethtool eth0
ethtool eth3
ethtool -S eth0
ethtool -S eth3
# RDMA 设备与端口状态
ibdev2netdev
ibstat
rdma link show</code></pre>
<p>
<code>sar</code> 记录 Linux 普通网络栈中的 <code>eth0/eth3</code> 流量;
GDRDMA 数据量由 <code>mlx5_0/mlx5_3</code> HCA 的 sysfs Counter 记录:
</p>
<pre><code class="language-bash">for hca in mlx5_0 mlx5_3; do
base="/sys/class/infiniband/${hca}/ports/1"
cat "${base}/counters/port_xmit_data"
cat "${base}/counters/port_rcv_data"
cat "${base}/counters/port_xmit_wait"
cat "${base}/counters/port_xmit_discards"
cat "${base}/counters/port_rcv_errors"
cat "${base}/hw_counters/req_transport_retries_exceeded"
cat "${base}/hw_counters/req_rnr_retries_exceeded"
done</code></pre>
<table>
<thead><tr><th>Counter</th><th>含义</th></tr></thead>
<tbody>
<tr><td><code>port_xmit_data/port_rcv_data</code></td><td>HCA 发送/接收数据累计量IB Counter 单位是 4 Octets脚本用 <code>delta × 4 × 8 / seconds</code> 换算 Gbit/s</td></tr>
<tr><td><code>port_xmit_wait</code></td><td>端口因缺少发送 Credit 等原因等待的时间,持续增长可能指向拥塞</td></tr>
<tr><td><code>port_xmit_discards/port_rcv_errors</code></td><td>发送丢弃和接收错误增量</td></tr>
<tr><td><code>req_transport_retries_exceeded</code></td><td>RDMA Transport 重试耗尽</td></tr>
<tr><td><code>req_rnr_retries_exceeded</code></td><td>Receiver Not Ready 重试耗尽</td></tr>
<tr><td><code>roce_adp_retrans*</code></td><td>RoCE 自适应重传及超时相关计数</td></tr>
<tr><td><code>np_ecn_marked* / *cnp*</code></td><td>ECN 标记和拥塞通知包,用于辅助判断 RoCE 拥塞</td></tr>
</tbody>
</table>
<p>
原始数据为 <code>head|worker/rdma.csv</code>
<code>case_rdma_summary.csv</code> 按 Case、节点和 HCA 计算吞吐及错误增量。
它说明双 Rail 的实际流量、均衡性和错误增量;
<code>case_netdev_summary.csv</code> 同时保留 Linux netdev 层的
<code>eth0/eth3</code> RX/TX 与错误。Phase 3 再把 NCCL Collective
放到请求 Timeline 中分析持续时间和计算重叠。
</p>
<h3>5.7 机内 PCIe 与 NCCL 通信基线</h3>
<pre><code class="language-bash"># 由 all 入口自动执行;不需要用户手工运行 torchrun
# 每个节点:所有 GPU 源/目标对FP16 256 MiB CUDA P2P copy
python3 communication_baseline.py p2p \
--size 256M --warmup 3 --iterations 10
# 每个节点8 rank NCCL AllReduce
torchrun --standalone --nproc-per-node=8 \
communication_baseline.py all-reduce \
--sizes 1M,64M,1G --repetitions 3 --warmup 5 --iterations 10
# 双节点16 rank分别设置 NCCL_CROSS_NIC=0、1、2
torchrun --nnodes=2 --nproc-per-node=8 \
--master-addr 10.101.0.11 --node-rank &lt;0-or-1&gt; \
communication_baseline.py all-reduce \
--sizes 1M,64M,1G --repetitions 3 --warmup 5 --iterations 10</code></pre>
<p>
P2P 结果按 <code>same_pcie_switch</code>PIX
<code>cross_numa_sys</code>SYS分别汇总不用一个平均值掩盖跨 CPU 路径。
AllReduce 同时报告 P50/P95 latency、<code>algbw</code>
<code>busbw</code>、正确性错误数和实际 NCCL 路径。1 MiB、64 MiB、1 GiB
分别覆盖小消息延迟、中等消息和大消息带宽;双机 A/B 直接给出
<code>NCCL_CROSS_NIC=0/1/2</code> 的数值比较。
</p>
<h3>5.8 静态快照与结果关系</h3>
<pre><code class="language-bash">nvidia-smi
nvidia-smi topo -m
lscpu
numactl --hardware
ip -details link show eth0
ip -details link show eth3
docker inspect &lt;container&gt;
docker top &lt;container&gt; -eo pid,ppid,psr,pcpu,pmem,stat,comm,args</code></pre>
<table>
<thead><tr><th>结果文件</th><th>内容</th><th>主要用途</th></tr></thead>
<tbody>
<tr><td><code>static_before.log / static_after.log</code></td><td>GPU、CPU、NUMA、NIC、RDMA、容器前后快照</td><td>证明运行环境,并比较错误计数和清理状态</td></tr>
<tr><td><code>collector_status.csv</code></td><td>每个采集器的启动、停止或提前退出状态</td><td>防止把缺失采集器当作 0 值</td></tr>
<tr><td><code>case_windows.csv</code></td><td>每个 Benchmark Case 的起止时间</td><td>从连续硬件日志中切片</td></tr>
<tr><td><code>bench_summary.csv</code></td><td>TPS、TTFT、TPOT、ITL、E2E</td><td>把硬件现象与用户侧性能对应</td></tr>
<tr><td><code>case_gpu_summary.csv</code></td><td>每 Case、节点、GPU 的利用率、显存、功耗、频率</td><td>比较负载与节点/GPU 不均衡</td></tr>
<tr><td><code>case_dcgm_summary.csv</code></td><td>每 Case、节点、GPU 的 SM/Tensor/显存接口/PCIe 指标</td><td>区分计算、设备显存和 PCIe 活跃度</td></tr>
<tr><td><code>case_cpu_summary.csv</code></td><td>整机与逐核 CPU 利用率、I/O Wait</td><td>识别整机饱和和少数热点核</td></tr>
<tr><td><code>case_process_summary.csv</code></td><td>服务进程 CPU、I/O、缺页、内存与上下文切换</td><td>定位 Host 进程开销与阻塞</td></tr>
<tr><td><code>case_perf_summary.csv</code></td><td>cycles、instructions、cache miss、迁移与缺页</td><td>计算 IPC 并判断 Cache/调度压力</td></tr>
<tr><td><code>case_numa_summary.csv</code></td><td>Node0/Node1 进程内存分布</td><td>识别跨 NUMA 放置</td></tr>
<tr><td><code>case_netdev_summary.csv</code></td><td><code>eth0/eth3</code> 吞吐与错误</td><td>与 RDMA HCA Counter 做分层核对</td></tr>
<tr><td><code>case_rdma_summary.csv</code></td><td>每 Case、节点、Rail 的吞吐和错误增量</td><td>判断双 Rail 使用、均衡和数据面错误</td></tr>
<tr><td><code>communication_summary.csv</code></td><td>每次 P2P/AllReduce 原始测量</td><td>保留每条 GPU 对、消息尺寸、CROSS_NIC 和重复实验</td></tr>
<tr><td><code>communication_aggregate.csv</code></td><td>PIX/SYS P2P 与单/双机 AllReduce 聚合</td><td>提供 P50/P95、algbw、busbw 和正确性比较</td></tr>
</tbody>
</table>
<h3>5.9 最终结果如何逐项汇报</h3>
<p class="decision">
最终 <code>report.md</code> 的章节顺序与本节一一对应。每一项必须同时给出
<strong>原始文件、有效样本数、Head/Worker 数值、Case 间变化和解释</strong>
不能只写“GPU 较忙”“网络未饱和”这类抽象结论。
</p>
<table>
<thead><tr><th>第 5 节指标</th><th>报告中的数值</th><th>最小分析动作</th></tr></thead>
<tbody>
<tr><td>5.1 时间窗</td><td>窗口来源、开始/结束、duration、采样数</td><td>确认全部为 <code>bench_main_marker_plus_duration</code></td></tr>
<tr><td>5.2 GPU</td><td>利用率/显存/功耗/频率的 Mean、P95、Max</td><td>比较两节点、8 卡离散度和不同 Case</td></tr>
<tr><td>5.3 DCGM</td><td>SM Active/Occupancy、Tensor/DRAM Active、PCIe TX/RX</td><td>比较计算、设备显存和 PCIe 哪一侧随负载上升</td></tr>
<tr><td>5.4 CPU/进程/perf</td><td>整机/热点核、进程 CPU/I/O/缺页/切换、IPC/Cache miss</td><td>区分整机容量、单线程热点和 Host 调度开销</td></tr>
<tr><td>5.5 NUMA</td><td>Node0/Node1 MiB 与比例</td><td>比较服务内存是否偏离 GPU/NIC 所在 NUMA</td></tr>
<tr><td>5.6 Network/RDMA</td><td>eth0/eth3、mlx5_0/mlx5_3 Gbit/s 与错误增量</td><td>计算双 Rail 均衡比例并核对丢弃/重试</td></tr>
<tr><td>5.7 Communication</td><td>PIX/SYS P2P、8/16 rank AllReduce P50/P95、algbw/busbw</td><td>比较跨 NUMA 损失与 CROSS_NIC 0/1/2</td></tr>
</tbody>
</table>
<p>
某个采集器无数据时报告显示 <code>-</code> 并附失败状态,不会把缺失值写成
<code>0</code>。正式 Run 默认 <code>ALLOW_PARTIAL_COLLECTORS=0</code>
因此必需采集器提前退出会让 Run 失败。
</p>
<h2>6. 精简代码设计</h2>
<p>已新增目录:</p>
<pre><code>/data/hzy/sskj/experiments/pro6000/
dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution/</code></pre>
<table>
<thead>
<tr><th>文件</th><th>职责</th><th>当前状态</th></tr>
</thead>
<tbody>
<tr><td><code>run_hardware_contention_attribution.sh</code></td><td>唯一 Shell 入口;按 Run 分发通信代码、通信基线、服务启停、双节点采集器、Case 编排、门禁和 Trap 清理</td><td class="pass">已实现</td></tr>
<tr><td><code>config.env</code></td><td>Phase 1 相对路径、节点、代表 Case、分层采样周期、通信基线与 fail-closed 策略</td><td class="pass">已实现</td></tr>
<tr><td><code>communication_baseline.py</code></td><td>CUDA P2P 全矩阵与 PyTorch/NCCL 8/16-rank AllReduce 微基准</td><td class="pass">已实现</td></tr>
<tr><td><code>hardware_contention_attribution.py</code></td><td>精确窗口、全部采集器解析、逐 Case 汇总、通信聚合和逐指标报告</td><td class="pass">已实现</td></tr>
<tr><td><code>tests/test_hardware_contention_attribution.py</code></td><td>Worker 无仓库依赖、GPU/RDMA、精确窗口、DCGM/CPU 解析、通信聚合和结果生成测试</td><td class="pass">9/9 通过</td></tr>
<tr><td><code>README.md</code></td><td>唯一入口、范围和结果目录说明</td><td class="pass">已实现</td></tr>
</tbody>
</table>
<p class="decision">
Phase 2 不复制双机 Docker 启停实现。唯一入口在内部调用 Phase 1 的
<code>run_quick_map.sh start/fixed/mixed/stop</code>,只新增通信基线、硬件采集、时间对齐和代表负载编排。
顶层仍只保留一个 Shell 文件,不创建额外 tmux、launch、start 或 stop 脚本。
</p>
<h2>7. 结果结构</h2>
<pre><code>results/&lt;RUN_ID&gt;/
manifest.json
run.log
bench/
bench_cmd.txt
bench.log
bench.jsonl
service/
head_server_cmd.txt
worker_server_cmd.txt
head_server.log
worker_server.log
communication/
communication_baseline.py
communication_baseline.sha256
p2p_head.log
p2p_worker.log
allreduce_head_8gpu.log
allreduce_worker_8gpu.log
allreduce_two_node_x0.log
allreduce_two_node_x1.log
allreduce_two_node_x2.log
head/
gpu_samples.csv
dcgm_dmon.log
mpstat.log
pidstat.log
sar_net.log
perf_stat.log
docker_top.log
numa_samples.csv
rdma.csv
static_before.log
static_after.log
collector_commands/
worker/
...
collector_status.csv
markers.csv
bench_summary.csv
gpu_summary.csv
rdma_summary.csv
case_windows.csv
communication_summary.csv
communication_aggregate.csv
case_gpu_summary.csv
case_gpu_node_summary.csv
case_dcgm_summary.csv
case_cpu_summary.csv
case_process_summary.csv
case_perf_summary.csv
case_numa_summary.csv
case_netdev_summary.csv
case_rdma_summary.csv
summary.json
report.md</code></pre>
<h2>8. 验收结果</h2>
<ul>
<li class="pass">最终代码固定到提交 <code>39fc2ba565a3</code>Shell 语法检查和三个 Python 文件编译通过。</li>
<li class="pass">Phase 1 精确窗口 4/4 单元测试通过Phase 2 worker 分发、采集、解析与通信聚合 9/9 单元测试通过。</li>
<li class="pass">完整 Dry-run 不启动服务即可展开全部模型、采集和通信命令;通信子流程生成 2 个 P2P、2 个单机 AllReduce、6 个双机 rank 命令。</li>
<li class="pass">双节点 smoke Run <code>dsv4pro-phase2-stage-smoke-20260731-162326</code> 已通过 Head/Worker P2P、两组 8-rank 与一组 16-rank AllReduce<code>wrong_values=0</code>;暂存文件、容器和 GPU 进程清理完成。</li>
<li class="pass">最终代码默认要求精确测量窗口、两端 DCGM 可用和全部必需采集器存活,缺失时 fail-closed。</li>
<li>首次 Run 的性能数据保留在第 10 节;最终代码尚未在 16 卡上复跑,因此新通信基线和逐指标表暂不填写虚构数值。</li>
</ul>
<h2>9. 实施记录</h2>
<table>
<thead>
<tr><th>时间</th><th>代码或运行</th><th>结果</th></tr>
</thead>
<tbody>
<tr><td>2026-07-30 15:46 CST</td><td>创建 Phase 2 设计与档案</td><td>采用单入口和独立轻量采样,不复制双机服务启动逻辑</td></tr>
<tr><td>2026-07-30 22:55:37 CST</td><td>完成 Phase 1 阶段交接</td><td>双 Rail 门禁和 12/12 正式结果通过;选定纯 Prefill、并发 Prefill、混合干扰三个诊断负载</td></tr>
<tr><td>2026-07-31 12:26:00 CST</td><td>完成 Phase 2 代码</td><td>扩展为五个固定负载和混合 A/B实现双节点 GPU/DCGM/CPU/NUMA/网络/RDMA 采集、时间对齐和异常清理</td></tr>
<tr><td>2026-07-31 12:26:00 CST</td><td>本地验证</td><td><code>bash -n</code>、Python 编译、5 项单元测试与全流程 Dry-run 通过;未占用 GPU</td></tr>
<tr><td>2026-07-31 13:27:51 CST</td><td>完成正式双机 Run</td><td>Run <code>dsv4pro-phase2-20260731-130125</code>8/8 benchmark 成功,总用时 26 分 26 秒,无 OOM</td></tr>
<tr><td>2026-07-31 13:40:03 CST</td><td>完成首轮结果归因</td><td>排除原始双 Rail 带宽饱和、整机 CPU 饱和和频率塌陷作为首要原因;锁定 TP16 Kernel、调度与同步时间线</td></tr>
<tr><td>2026-07-31 15:25:00 CST</td><td>完成最终 Phase 2 代码</td><td>提交 <code>30664faa41f8</code>:精确主测量窗口、双节点 DCGM 门禁、5 秒低开销 Host 采样、PCIe/NCCL 基线和逐指标自动报告均已通过本地验证</td></tr>
<tr><td>2026-07-31 16:18:35 CST</td><td>修复 Worker 通信代码路径</td><td>提交 <code>39fc2ba565a3</code>:通信脚本按 Run 自动分发并校验哈希Worker 不再要求存在同路径 Git 仓库9/9 回归测试通过</td></tr>
<tr><td>2026-07-31 16:27:58 CST</td><td>双节点通信 smoke test</td><td>1 MiB 最小功能验证完整通过 P2P、8-rank 和 16-rank AllReduce仅验证链路不作为正式带宽数据</td></tr>
</tbody>
</table>
<h2>10. 首次真机结果</h2>
<p class="decision">
<strong>Run<code>dsv4pro-phase2-20260731-130125</code>,状态
<code>COMPLETED</code></strong>运行时间为 13:01:25 至 13:27:51 CST
8 个结果全部成功0 个失败。正式入口仅在 <code>174.1.51.5</code> 执行;
Worker 服务和采集器由脚本通过 SSH 自动启动。
</p>
<pre><code class="language-bash">cd /data/hzy/sskj/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_hardware_contention_attribution
RUN_ID=dsv4pro-phase2-20260731-130125
tmux new-session -d -s dsv4pro-phase2 \
"RUN_ID=${RUN_ID} bash run_hardware_contention_attribution.sh all \
2&gt;&amp;1 | tee /data/hzy/${RUN_ID}.log"</code></pre>
<h3>10.1 代表负载</h3>
<table>
<thead>
<tr><th>Case</th><th>Input TPS</th><th>Output TPS</th><th>TTFT P95</th><th>TPOT P95</th></tr>
</thead>
<tbody>
<tr><td>128K → 1, C=1</td><td>2,618.53</td><td>0.02</td><td>50.036 s</td><td></td></tr>
<tr><td>32K → 1, C=16</td><td>3,116.20</td><td>0.10</td><td>161.899 s</td><td></td></tr>
<tr><td>1K → 1K, C=32</td><td>447.41</td><td>447.41</td><td>10.144 s</td><td>65.63 ms</td></tr>
<tr><td>1K → 4K, C=16</td><td>79.35</td><td>317.41</td><td>1.727 s</td><td>50.01 ms</td></tr>
<tr><td>128K → 1K, C=1</td><td>1,610.89</td><td>12.59</td><td>48.489 s</td><td>32.12 ms</td></tr>
</tbody>
</table>
<h3>10.2 混合 Prefill/Decode</h3>
<table>
<thead>
<tr><th>Decode 指标</th><th>Control</th><th>注入 128K Prefill</th><th>变化</th></tr>
</thead>
<tbody>
<tr><td>Output TPS</td><td>454.39</td><td>345.20</td><td>-24.03%</td></tr>
<tr><td>TTFT P95</td><td>9.437 s</td><td>9.869 s</td><td>+4.58%</td></tr>
<tr><td>TPOT P95</td><td>66.17 ms</td><td>110.36 ms</td><td>+66.79%</td></tr>
<tr><td>E2E P95</td><td>72.225 s</td><td>117.941 s</td><td>+63.30%</td></tr>
</tbody>
</table>
<p>
这再次证明 Prefill 会明显干扰正在进行的 Decode。ITL P95 仍约为
62 ms并不与 TPOT 恶化矛盾:少量同步长停顿可能不足全部 token 间隔的 5%
因而会被全局 token 级 ITL P95 隐藏,而请求级 TPOT 和 E2E 会暴露它。
</p>
<h3>10.3 GPU、CPU 与通信</h3>
<ul>
<li>128K Prefill 注入窗口两节点平均 GPU 利用率为 99.78% / 99.34%,平均功耗约 274 W频率稳定在约 2.382.41 GHz没有频率塌陷证据。</li>
<li>显存稳定在每卡约 83.283.4 GiB / 85,651 MiB只剩约 2.3 GiB 余量。</li>
<li>整机 CPU 平均约 8%11%I/O Wait 为 0但注入窗口仍有 5/4 个 Head/Worker 核平均超过 80%,单线程调度或 NUMA 热点尚未排除。</li>
<li>两条 RDMA Rail 流量几乎对称,所有采集到的错误增量均为 0。最高负载为 32K → 1, C=16Head/Worker 总发送约 140.0/139.3 Gbit/s即每条 400G Rail 约 70 Gbit/s。</li>
<li>因此原始 RoCE 带宽没有饱和;但 TP16 collective 的延迟、调度和 Rank 同步开销仍需 Timeline 才能拆开。</li>
</ul>
<h3>10.4 卡间通信路径</h3>
<table>
<thead>
<tr><th>范围</th><th>实际路径</th><th>本轮是否测量</th></tr>
</thead>
<tbody>
<tr><td>单机 8 卡内部</td><td>无 NVLinkNCCL <code>P2P/IPC</code> 走 PCIe。GPU03、GPU47 各自在 PCIe Switch 内为 <code>PIX</code>,两组之间为 <code>SYS</code></td><td>首次 Run 仅有 DCGM PCIe 指标;最终代码已加入所有 GPU 对的 256 MiB P2P 微基准</td></tr>
<tr><td>两机之间</td><td><code>mlx5_0 + mlx5_3</code> 双 Rail <code>NET/IB + GDRDMA</code></td><td>已测量每 Case HCA 流量、均衡性和错误增量</td></tr>
</tbody>
</table>
<p>
最终代码使用当前 SGLang 镜像内的 PyTorch/CUDA/NCCL 实现等价微基准:
两节点 P2P 全矩阵、两组单机 8-rank AllReduce 和三组双机 16-rank
<code>NCCL_CROSS_NIC</code> A/B。结果统一写入
<code>communication_summary.csv</code><code>communication_aggregate.csv</code>
完成 Phase 2 后,后续阶段不重复跑这些微基准。
</p>
<h3>10.5 首轮结论与最终代码修正</h3>
<p class="decision">
当前证据支持把下一步缩到 TP16 的 Kernel、Scheduler、PCIe/RDMA Collective
和 Rank 同步时间线。Phase 3 只分析真实请求中通信出现的位置、耗时和与计算的
重叠关系,不重复 Phase 2 的平均 GPU/CPU/RDMA 采集。原始双 Rail 带宽、整机
CPU 容量和降频都不像首要瓶颈Phase 3 将继续定位具体 Kernel 和同步根因。
</p>
<ul>
<li>Worker DCGM 缺失已改为双节点启动前门禁;最终 Run 不再接受单边 DCGM 数据。</li>
<li>宽泛 Case 窗口已改为正式 benchmark 起点加 duration 的精确窗口,并由 <code>REQUIRE_PRECISE_WINDOWS=1</code> 强制执行。</li>
<li><code>pidstat/mpstat/perf/sar/numastat</code> 已改为进程级 5 秒采样,保留时间戳并降低诊断扰动。</li>
<li>最终 <code>report.md</code> 已按第 5 节逐项输出数值表;缺失采集显示为 <code>-</code>,不会解释为 0。</li>
<li>完整归因见 <a href="./results/dsv4pro-phase2-20260731-130125/analysis.md"><code>analysis.md</code></a>,原始结构化摘要和两端 NCCL 证据已一并归档。</li>
</ul>
<p><a class="back" href="./phase1_exp.html">返回 Phase 1 实验档案</a></p>
<p><a class="back" href="./推理优化计划.html">返回推理优化主计划</a></p>
</main>
</body>
</html>