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

436 lines
22 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 2 CodeDSV4-Pro 双机 Pro6000D SGLang 硬件竞争归因</title>
<style>
:root {
--canvas: #eef3f4;
--paper: #ffffff;
--ink: #182126;
--muted: #5a6970;
--line: #d4dee1;
--navy: #17363d;
--teal: #087c72;
--teal-soft: #e8f5f3;
--amber: #a64c14;
--amber-soft: #fff1e7;
--code-bg: #17252b;
--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;
}
header { color: #f6fbfb; background: var(--navy); border-bottom: 5px solid #d2692b; }
.header-inner, main { width: min(100% - 36px, 1120px); margin: 0 auto; }
.header-inner { padding: 34px 0 30px; }
.eyebrow { margin: 0 0 6px; color: #9edbd5; font-size: 13px; font-weight: 700; }
h1 { margin: 0; font-size: clamp(28px, 4vw, 42px); line-height: 1.25; }
.meta { margin-top: 15px; color: #d6e5e7; font-size: 14px; }
main {
margin-top: 30px;
margin-bottom: 70px;
padding: 38px 48px 58px;
background: var(--paper);
border: 1px solid var(--line);
border-radius: 6px;
box-shadow: 0 12px 30px rgba(27, 45, 51, 0.07);
}
h2 {
margin: 46px 0 15px;
padding-bottom: 8px;
font-size: 25px;
line-height: 1.35;
border-bottom: 2px solid #adbbc0;
}
h2:first-of-type { margin-top: 18px; }
h3 { margin: 29px 0 10px; color: #21454d; font-size: 19px; }
h4 { margin: 22px 0 8px; font-size: 16px; }
p, ul, ol { margin-top: 0; margin-bottom: 16px; }
li + li { margin-top: 5px; }
a { color: var(--teal); text-underline-offset: 3px; }
code {
padding: 2px 5px;
color: #85380d;
background: var(--amber-soft);
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, monospace;
overflow-wrap: anywhere;
}
pre {
margin: 14px 0 22px;
padding: 16px 18px;
overflow: auto;
color: var(--code-ink);
background: var(--code-bg);
border-radius: 5px;
font: 13px/1.62 "SFMono-Regular", Consolas, monospace;
}
pre code { padding: 0; color: inherit; background: transparent; }
table { width: 100%; margin: 16px 0 26px; border-collapse: collapse; font-size: 14px; }
th, td {
padding: 9px 11px;
vertical-align: top;
text-align: left;
border: 1px solid var(--line);
overflow-wrap: anywhere;
}
th { color: #153b41; background: #eaf2f2; }
tbody tr:nth-child(even) { background: #fafcfc; }
.callout { margin: 18px 0 26px; padding: 14px 18px; background: var(--teal-soft); border-left: 4px solid var(--teal); }
.warning { margin: 18px 0 26px; padding: 14px 18px; background: var(--amber-soft); border-left: 4px solid var(--amber); }
.toc { columns: 2; column-gap: 38px; margin: 16px 0 24px; padding-left: 22px; }
.toc li { break-inside: avoid; }
.path { font-family: "SFMono-Regular", Consolas, monospace; font-size: 13px; }
footer { margin-top: 48px; padding-top: 18px; color: var(--muted); border-top: 1px solid var(--line); }
@media (max-width: 760px) {
main { padding: 28px 20px 42px; }
.toc { columns: 1; }
table { display: block; overflow-x: auto; }
}
</style>
</head>
<body>
<header>
<div class="header-inner">
<p class="eyebrow">Standalone Code Walkthrough / Phase 2</p>
<h1>DSV4-Pro 双机 Pro6000D SGLang 硬件竞争归因:代码详解</h1>
<div class="meta">
行号基线:<code>39fc2ba565a3</code> 
完成时间2026-07-31 17:22:20 CST 
唯一入口:<code>run_hardware_contention_attribution.sh all</code>
</div>
</div>
</header>
<main>
<p>
<a href="./推理优化计划.html">返回推理优化主计划</a> ·
<a href="./phase2_exp.html">打开 Phase 2 实验档案</a>
</p>
<div class="callout">
<strong>文档边界:</strong>本文只解释提交 <code>39fc2ba565a3</code> 的 Phase 2
代码和文件调用关系。Phase 1 负责模型服务与请求Phase 2 负责通信基线、
两节点监控、精确时间切片和逐指标报告。最终正式 Run
<code>dsv4pro-phase2-20260731-163620</code> 使用提交
<code>5f24b7d22f98108f6cc234edba6768d55ea0a962</code>,代码树包含本页所述修复。
</div>
<h2 id="read">1. 阅读导航</h2>
<ul class="toc">
<li><a href="#flow">总体控制流</a></li>
<li><a href="#files">文件职责与调用关系</a></li>
<li><a href="#config">配置来源</a></li>
<li><a href="#communication">通信微基准</a></li>
<li><a href="#collectors">采集器实现</a></li>
<li><a href="#alignment">精确测量窗口</a></li>
<li><a href="#report">逐指标报告</a></li>
<li><a href="#index">函数行号索引</a></li>
</ul>
<h2 id="flow">2. 总体控制流</h2>
<pre><code>main "$@" → run_all
├─ validate_config
├─ preflight_node_tools
│ └─ 两节点 dcgmi discovery -l 必须成功
├─ preflight_clock_sync + preflight_gpus_idle
├─ run_communication_baseline
│ ├─ 两节点 CUDA P2P 全矩阵
│ ├─ 两节点各自 8-rank AllReduce
│ └─ 16-rank AllReduceCROSS_NIC=0/1/2
├─ start_service → Phase 1 start
├─ capture_static_snapshots before
├─ start_collectors → Head/Worker 同时采集
├─ idle → fixed cases → mixed A/B → cooldown
├─ check_collectors + stop_collectors
├─ capture_static_snapshots after
├─ stop_service → Phase 1 stop
├─ summarize_results
│ └─ 按正式 benchmark 窗口生成第 5 节逐项数据表
└─ finish_manifest</code></pre>
<p>
<code>all</code> 是唯一正式入口。<code>communication</code><code>summarize</code>
<code>stop</code> 是排错/恢复 action不需要在正常执行前手工调用。
</p>
<h2 id="files">3. 文件职责与调用关系</h2>
<table>
<thead><tr><th>文件</th><th>行数</th><th>职责</th></tr></thead>
<tbody>
<tr><td class="path">config.env</td><td>61</td><td>节点、Case、分层采样周期、通信尺寸、NCCL 选择和 fail-closed 策略。</td></tr>
<tr><td class="path">run_hardware_contention_attribution.sh</td><td>1036</td><td>唯一 Shell 编排器预检、通信文件分发、通信基线、Phase 1 委托、采集器、Case 和清理。</td></tr>
<tr><td class="path">communication_baseline.py</td><td>227</td><td>CUDA P2P 全矩阵及 PyTorch/NCCL AllReduce 正确性、延迟和带宽测试。</td></tr>
<tr><td class="path">hardware_contention_attribution.py</td><td>1480</td><td>解析所有原始采集器,按 Case 切片,聚合通信并生成 CSV/JSON/report.md。</td></tr>
<tr><td class="path">tests/test_hardware_contention_attribution.py</td><td>322</td><td>9 项纯 Python 单元测试,覆盖 worker 无仓库依赖、精确窗口、解析器、RDMA 单位和通信聚合。</td></tr>
</tbody>
</table>
<pre><code>用户
└─ Phase2/run_hardware_contention_attribution.sh all
├─ source Phase2/config.env
├─ docker/torchrun → Phase2/communication_baseline.py
├─ env ... bash Phase1/run_quick_map.sh start/fixed/mixed/stop
│ └─ Phase1/quick_map_results.py 写 benchmark meta
├─ Shell 采集 Head/Worker 原始时间序列
└─ Phase2/hardware_contention_attribution.py summarize
├─ 读取 Phase1 bench/cases/*/meta.json
├─ 读取 Head/Worker 原始监控
├─ 读取 communication/COMM_RESULT
└─ 输出逐 Case、逐节点、逐指标表和 report.md</code></pre>
<h3>3.1 Phase 1 与 Phase 2 的边界</h3>
<table>
<thead><tr><th>问题</th><th>由哪个文件负责</th><th>证据</th></tr></thead>
<tbody>
<tr><td>模型路径、镜像、TP16、EP、显存比例</td><td>Phase 1 <code>config.env</code> + <code>run_quick_map.sh</code></td><td><code>service/head_server_cmd.txt</code><code>worker_server_cmd.txt</code></td></tr>
<tr><td>ISL/OSL/C、random 请求和 mixed A/B</td><td>Phase 1 场景表与 benchmark 函数</td><td><code>bench/*/bench_cmd.txt</code><code>bench.json</code></td></tr>
<tr><td>通信基线、监控周期、Case 选择</td><td>Phase 2 <code>config.env</code></td><td>Phase 2 <code>manifest.json</code></td></tr>
<tr><td>硬件归因和数值报告</td><td>Phase 2 Python 汇总器</td><td><code>case_*_summary.csv</code><code>report.md</code></td></tr>
</tbody>
</table>
<h2 id="config">4. 配置来源</h2>
<table>
<thead><tr><th>行号</th><th>配置组</th><th>关键变量</th></tr></thead>
<tbody>
<tr><td><code>config.env:L3-L16</code></td><td>入口与节点</td><td><code>PHASE1_ENTRY</code>、Head/Worker、端口和容器名。</td></tr>
<tr><td><code>L18-L21</code></td><td>诊断 Case</td><td>五个 fixed Case、mixed A/B 开关。</td></tr>
<tr><td><code>L23-L39</code></td><td>采样与严格性</td><td>GPU/DCGM/RDMA 1 秒CPU/进程/网络/NUMA/perf 5 秒;精确窗口和采集器 fail-closed。</td></tr>
<tr><td><code>L40-L55</code></td><td>通信基线</td><td>镜像、消息尺寸、迭代次数、P2P 大小、CROSS_NIC 列表、Socket/HCA。</td></tr>
<tr><td><code>L57-L61</code></td><td>路径与模式</td><td><code>RESULT_BASE</code>、Runtime、Dry-run、是否允许部分采集器。</td></tr>
</tbody>
</table>
<p>
<code>MEM_FRACTION_STATIC</code> 不在 Phase 2 重复定义。它仍来自 Phase 1
最终展开为 SGLang 的 <code>--mem-fraction-static</code>。判断某次 Run 的真实值,
应读取 <code>service/head_server_cmd.txt</code>,不能只看默认配置。
</p>
<h2 id="communication">5. 通信微基准</h2>
<h3>5.1 Shell 如何编排</h3>
<p>
<code>run_hardware_contention_attribution.sh:L281-L513</code> 负责源码暂存、
Docker 命令、两节点同步和清理。所有命令先写入 <code>commands/*.txt</code>
</p>
<ul>
<li><code>L281-L319</code>:从 Head 将当次通信脚本暂存到两节点并校验 SHA256。</li>
<li><code>L321-L374</code>构造容器命令Head/Worker 各跑一次 P2P。</li>
<li><code>L376-L404</code>Head/Worker 各跑一次 8-rank AllReduce。</li>
<li><code>L406-L467</code>:每个 CROSS_NIC 值先启动 Worker rank再运行 Head rank。</li>
<li><code>L469-L513</code>:只清理本实验前缀的通信容器和本次 `/tmp` 暂存目录。</li>
</ul>
<p>
Docker 使用和 SGLang 一致的 CUDA 13 nightly 镜像,并显式透传
<code>rdma_cm</code><code>uverbs0</code><code>uverbs3</code>
<code>NCCL_DEBUG=INFO</code> 只在微基准中打开,用于证明 NET/IB/GDRDMA 路径。
Worker 不要求存在 Git 仓库;容器只读挂载自动分发的
<code>/tmp/.../&lt;RUN_ID&gt;/communication_baseline.py</code>。结果目录同时保存
当次源码副本和 SHA256避免两个节点 checkout 不一致造成版本漂移。
</p>
<h3>5.2 P2P 代码</h3>
<p>
<code>communication_baseline.py:L45-L106</code> 遍历所有源 GPU 和目标 GPU
先调用 <code>torch.cuda.can_device_access_peer</code>,再对 256 MiB FP16 Tensor
做预热和 CUDA Event 计时。输出包括方向、P50/P95 latency 和 GB/s。
汇总器按拓扑拆成同 PCIe Switch 的 PIX 与跨 NUMA 的 SYS。
</p>
<h3>5.3 AllReduce 代码</h3>
<p>
<code>communication_baseline.py:L107-L198</code> 初始化 NCCL process group
对 1 MiB、64 MiB、1 GiB 分别预热和重复测量。每轮先把各 rank latency
gather 到 rank 0使用最慢 rank 作为 collective 完成时间,并检查归约结果:
</p>
<pre><code>algbw = message_bytes / latency
busbw = algbw × 2 × (world_size - 1) / world_size
wrong_values = count(output != expected_sum)</code></pre>
<p>
这样不会用某个提前返回 rank 的时间美化结果;<code>wrong_values=0</code>
才算正确完成。
</p>
<h2 id="collectors">6. 两节点采集器</h2>
<h3>6.1 启动前门禁</h3>
<p>
Shell <code>L67-L199</code> 完成配置、工具、时钟和 GPU 空闲检查。
<code>preflight_node_tools</code> 不只检查 <code>dcgmi</code> 文件存在,
还实际运行 <code>dcgmi discovery -l</code>;两节点任一 Host Engine 不可用即退出。
</p>
<h3>6.2 采集器包装</h3>
<p>
<code>start_stream_collector</code> 位于 Shell <code>L517-L551</code>
它保存完整命令、PID、唯一进程 tag 和日志;<code>check_collectors</code>
<code>L722-L740</code> 检查采集器是否提前退出,默认不允许部分成功。
</p>
<table>
<thead><tr><th>采集器</th><th>Shell 位置</th><th>周期</th><th>输出</th></tr></thead>
<tbody>
<tr><td><code>nvidia-smi</code></td><td><code>L552-L565</code></td><td>1 秒</td><td><code>gpu_samples.csv</code></td></tr>
<tr><td>RDMA HCA counters</td><td><code>L566-L592</code></td><td>1 秒</td><td><code>rdma.csv</code></td></tr>
<tr><td>DCGM</td><td><code>L645-L655</code></td><td>1 秒</td><td><code>dcgm_dmon.log</code></td></tr>
<tr><td><code>mpstat</code></td><td><code>L656-L663</code></td><td>5 秒</td><td><code>mpstat.log</code></td></tr>
<tr><td><code>pidstat -durw</code></td><td><code>L664-L671</code></td><td>5 秒,进程级</td><td><code>pidstat.log</code></td></tr>
<tr><td><code>sar -n DEV,EDEV</code></td><td><code>L672-L678</code></td><td>5 秒</td><td><code>sar_net.log</code></td></tr>
<tr><td><code>perf stat</code></td><td><code>L680-L689</code></td><td>5 秒</td><td><code>perf_stat.log</code></td></tr>
<tr><td><code>numastat</code></td><td><code>L618-L644</code></td><td>5 秒</td><td><code>numa_samples.csv</code></td></tr>
</tbody>
</table>
<p>
CPU、进程、perf 和 sar 的每行均由 Shell 增加
<code>wall_time_ns TAB node TAB payload</code>。NUMA 直接转成结构化 CSV
避免旧版线程级 1 秒日志过大,也让所有指标能按 Case 切片。
</p>
<h2 id="alignment">7. 精确测量窗口</h2>
<h3>7.1 Phase 1 如何标记主测量</h3>
<p>
Phase 1 <code>run_quick_map.sh:L547-L564</code> 每 100 ms 观察 bench 日志;
发现 <code>Starting main benchmark run</code> 后调用
<code>quick_map_results.py mark-measurement-start</code>
<code>quick_map_results.py:L340-L385</code> 用这个起点和
<code>bench.json.duration</code> 生成:
</p>
<pre><code>measurement_started_at
measurement_ended_at
measurement_duration_s
measurement_window_source = bench_main_marker_plus_duration</code></pre>
<h3>7.2 Phase 2 如何使用</h3>
<p>
<code>hardware_contention_attribution.py:L509-L560</code> 优先读取上述字段。
只有兼容旧结果时才可能使用进程级窗口;正式配置
<code>REQUIRE_PRECISE_WINDOWS=1</code> 会拒绝任何 fallback。
<code>L561-L841</code> 对 GPU、DCGM、CPU、进程、perf、NUMA、netdev 和 RDMA
使用同一个 <code>started_ns ≤ sample ≤ ended_ns</code> 条件。
</p>
<h2 id="report">8. 逐指标报告</h2>
<p>
Python <code>summarize</code> 位于
<code>hardware_contention_attribution.py:L1036-L1378</code>
它不只生成一个抽象结论,而是按 Phase 2 第 5 节依次写出:
</p>
<table>
<thead><tr><th>指标</th><th>解析函数</th><th>Case 汇总文件</th></tr></thead>
<tbody>
<tr><td>GPU</td><td><code>summarize_gpu_rows L377-L413</code></td><td><code>case_gpu_summary.csv</code><code>case_gpu_node_summary.csv</code></td></tr>
<tr><td>DCGM</td><td><code>parse_dcgm L167-L192</code></td><td><code>case_dcgm_summary.csv</code></td></tr>
<tr><td>CPU</td><td><code>parse_mpstat L193-L222</code></td><td><code>case_cpu_summary.csv</code></td></tr>
<tr><td>进程</td><td><code>parse_pidstat L223-L289</code></td><td><code>case_process_summary.csv</code></td></tr>
<tr><td>perf</td><td><code>parse_perf L290-L310</code></td><td><code>case_perf_summary.csv</code></td></tr>
<tr><td>NUMA</td><td>结构化 CSV + <code>summarize_case_metrics</code></td><td><code>case_numa_summary.csv</code></td></tr>
<tr><td>Linux netdev</td><td><code>parse_sar_net L311-L358</code></td><td><code>case_netdev_summary.csv</code></td></tr>
<tr><td>RDMA</td><td><code>summarize_rdma_rows L424-L484</code></td><td><code>case_rdma_summary.csv</code></td></tr>
<tr><td>P2P/NCCL</td><td><code>load_communication_rows</code> + <code>aggregate_communication_rows L842-L928</code></td><td><code>communication_summary.csv</code><code>communication_aggregate.csv</code></td></tr>
</tbody>
</table>
<p>
<code>report.md</code> 对每组都打印有效样本数、Mean/P95/Max、Head/Worker
或 Case 间比较和源文件。解析不到的值保留为 <code>-</code>,不会被写成 0。
</p>
<h2 id="outputs">9. 结果目录</h2>
<pre><code>results/&lt;RUN_ID&gt;/
manifest.json
commands/
communication/
service/
bench/&lt;phase1-sub-run&gt;/
head/
gpu_samples.csv
dcgm_dmon.log
mpstat.log
pidstat.log
perf_stat.log
sar_net.log
numa_samples.csv
rdma.csv
collector_commands/
worker/
...同上...
case_windows.csv
bench_summary.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
communication_summary.csv
communication_aggregate.csv
summary.json
report.md</code></pre>
<h2 id="index">10. 函数行号索引</h2>
<h3>10.1 Shell 编排器</h3>
<table>
<thead><tr><th>行号</th><th>函数组</th><th>职责</th></tr></thead>
<tbody>
<tr><td>L26-L66</td><td>日志、远端执行、命令证据</td><td>基础设施。</td></tr>
<tr><td>L67-L199</td><td>配置、工具、时钟、GPU 空闲门禁</td><td>正式运行前 fail-fast。</td></tr>
<tr><td>L200-L268</td><td>Manifest、marker、Phase 1 委托</td><td>运行身份与复用边界。</td></tr>
<tr><td>L281-L513</td><td>通信基线</td><td>按 Run 分发源码、P2P、8/16-rank AllReduce、CROSS_NIC A/B 与清理。</td></tr>
<tr><td>L448-L516</td><td>服务和静态快照</td><td>启停 Phase 1 双机服务并保存环境。</td></tr>
<tr><td>L517-L710</td><td>采集命令与启动</td><td>两节点分层采样。</td></tr>
<tr><td>L711-L772</td><td>采集器检查和停止</td><td>fail-closed 与残留清理。</td></tr>
<tr><td>L782-L835</td><td>fixed/mixed Case</td><td>代表负载编排。</td></tr>
<tr><td>L836-L858</td><td>汇总、Manifest、trap</td><td>结果收口。</td></tr>
<tr><td>L859-L928</td><td><code>run_all</code></td><td>完整状态机。</td></tr>
<tr><td>L929-L968</td><td>辅助 action 与 main</td><td><code>communication/all/summarize/stop</code> 分发。</td></tr>
</tbody>
</table>
<h3>10.2 Python 文件</h3>
<table>
<thead><tr><th>文件/行号</th><th>职责</th></tr></thead>
<tbody>
<tr><td><code>communication_baseline.py:L20-L44</code></td><td>尺寸解析、分位数和 JSON 结果协议。</td></tr>
<tr><td><code>L45-L106</code></td><td>CUDA P2P 全矩阵。</td></tr>
<tr><td><code>L107-L198</code></td><td>NCCL AllReduce 与正确性。</td></tr>
<tr><td><code>hardware_contention_attribution.py:L76-L166</code></td><td>时间、CSV、数字统计基础函数。</td></tr>
<tr><td><code>L167-L358</code></td><td>DCGM、mpstat、pidstat、perf、sar 解析器。</td></tr>
<tr><td><code>L359-L508</code></td><td>通信、GPU、RDMA、bench 读取与汇总。</td></tr>
<tr><td><code>L509-L841</code></td><td>精确窗口和全部 Case 指标切片。</td></tr>
<tr><td><code>L842-L1035</code></td><td>通信聚合、CSV、Marker、Manifest。</td></tr>
<tr><td><code>L1036-L1378</code></td><td>全部输出表和逐指标 <code>report.md</code></td></tr>
<tr><td><code>L1379-L1480</code></td><td>CLI 子命令。</td></tr>
</tbody>
</table>
<h2 id="evidence">11. 最终 Run 证据</h2>
<p>
正式 Run 完成 8/8 benchmark、8/8 精确测量窗口和 18/18 采集器启停。
代码产生的各类输出与实验结论一一对应:
</p>
<ul>
<li><a href="./results/dsv4pro-phase2-20260731-163620/manifest.json"><code>manifest.json</code></a>Run 身份、提交、时间和最终状态。</li>
<li><a href="./results/dsv4pro-phase2-20260731-163620/bench_summary.csv"><code>bench_summary.csv</code></a>8 个端到端结果。</li>
<li><a href="./results/dsv4pro-phase2-20260731-163620/case_windows.csv"><code>case_windows.csv</code></a>:正式 benchmark 精确时间窗。</li>
<li><a href="./results/dsv4pro-phase2-20260731-163620/collector_status.csv"><code>collector_status.csv</code></a>:两节点采集器生命周期。</li>
<li><a href="./results/dsv4pro-phase2-20260731-163620/report.md"><code>report.md</code></a>:逐指标自动报告。</li>
<li><a href="./results/dsv4pro-phase2-20260731-163620/analysis.md"><code>analysis.md</code></a>:阶段归因与 Phase 3 入口。</li>
</ul>
<footer>
本文只描述提交 <code>39fc2ba565a3</code>。Nsight Systems、SGLang Profiler 和
Kernel Timeline 属于 Phase 3不加入 Phase 2避免重复采集和职责混淆。
</footer>
</main>
</body>
</html>