30664faa41f8 的 Phase 2
代码和文件调用关系。Phase 1 负责模型服务与请求;Phase 2 负责通信基线、
两节点监控、精确时间切片和逐指标报告。
1. 阅读导航
2. 总体控制流
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 AllReduce,CROSS_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
all 是唯一正式入口。communication、summarize
和 stop 是排错/恢复 action,不需要在正常执行前手工调用。
3. 文件职责与调用关系
| 文件 | 行数 | 职责 |
|---|---|---|
| config.env | 61 | 节点、Case、分层采样周期、通信尺寸、NCCL 选择和 fail-closed 策略。 |
| run_hardware_contention_attribution.sh | 968 | 唯一 Shell 编排器:预检、通信基线、Phase 1 委托、采集器、Case 和清理。 |
| communication_baseline.py | 227 | CUDA P2P 全矩阵及 PyTorch/NCCL AllReduce 正确性、延迟和带宽测试。 |
| hardware_contention_attribution.py | 1480 | 解析所有原始采集器,按 Case 切片,聚合通信并生成 CSV/JSON/report.md。 |
| tests/test_hardware_contention_attribution.py | 308 | 8 项纯 Python 单元测试,覆盖精确窗口、解析器、RDMA 单位和通信聚合。 |
用户
└─ 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
3.1 Phase 1 与 Phase 2 的边界
| 问题 | 由哪个文件负责 | 证据 |
|---|---|---|
| 模型路径、镜像、TP16、EP、显存比例 | Phase 1 config.env + run_quick_map.sh | service/head_server_cmd.txt、worker_server_cmd.txt |
| ISL/OSL/C、random 请求和 mixed A/B | Phase 1 场景表与 benchmark 函数 | bench/*/bench_cmd.txt、bench.json |
| 通信基线、监控周期、Case 选择 | Phase 2 config.env | Phase 2 manifest.json |
| 硬件归因和数值报告 | Phase 2 Python 汇总器 | case_*_summary.csv、report.md |
4. 配置来源
| 行号 | 配置组 | 关键变量 |
|---|---|---|
config.env:L3-L16 | 入口与节点 | PHASE1_ENTRY、Head/Worker、端口和容器名。 |
L18-L21 | 诊断 Case | 五个 fixed Case、mixed A/B 开关。 |
L23-L39 | 采样与严格性 | GPU/DCGM/RDMA 1 秒;CPU/进程/网络/NUMA/perf 5 秒;精确窗口和采集器 fail-closed。 |
L40-L55 | 通信基线 | 镜像、消息尺寸、迭代次数、P2P 大小、CROSS_NIC 列表、Socket/HCA。 |
L57-L61 | 路径与模式 | RESULT_BASE、Runtime、Dry-run、是否允许部分采集器。 |
MEM_FRACTION_STATIC 不在 Phase 2 重复定义。它仍来自 Phase 1,
最终展开为 SGLang 的 --mem-fraction-static。判断某次 Run 的真实值,
应读取 service/head_server_cmd.txt,不能只看默认配置。
5. 通信微基准
5.1 Shell 如何编排
run_hardware_contention_attribution.sh:L269-L447 负责 Docker 命令、
两节点同步和清理。所有命令先写入 commands/*.txt:
L300-L323:Head/Worker 各跑一次 P2P。L324-L353:Head/Worker 各跑一次 8-rank AllReduce。L354-L416:每个 CROSS_NIC 值先启动 Worker rank,再运行 Head rank。L417-L447:只清理本实验前缀的通信容器。
Docker 使用和 SGLang 一致的 CUDA 13 nightly 镜像,并显式透传
rdma_cm、uverbs0、uverbs3。
NCCL_DEBUG=INFO 只在微基准中打开,用于证明 NET/IB/GDRDMA 路径。
5.2 P2P 代码
communication_baseline.py:L45-L106 遍历所有源 GPU 和目标 GPU,
先调用 torch.cuda.can_device_access_peer,再对 256 MiB FP16 Tensor
做预热和 CUDA Event 计时。输出包括方向、P50/P95 latency 和 GB/s。
汇总器按拓扑拆成同 PCIe Switch 的 PIX 与跨 NUMA 的 SYS。
5.3 AllReduce 代码
communication_baseline.py:L107-L198 初始化 NCCL process group,
对 1 MiB、64 MiB、1 GiB 分别预热和重复测量。每轮先把各 rank latency
gather 到 rank 0,使用最慢 rank 作为 collective 完成时间,并检查归约结果:
algbw = message_bytes / latency
busbw = algbw × 2 × (world_size - 1) / world_size
wrong_values = count(output != expected_sum)
这样不会用某个提前返回 rank 的时间美化结果;wrong_values=0
才算正确完成。
6. 两节点采集器
6.1 启动前门禁
Shell L67-L199 完成配置、工具、时钟和 GPU 空闲检查。
preflight_node_tools 不只检查 dcgmi 文件存在,
还实际运行 dcgmi discovery -l;两节点任一 Host Engine 不可用即退出。
6.2 采集器包装
start_stream_collector 位于 Shell L517-L551。
它保存完整命令、PID、唯一进程 tag 和日志;check_collectors 在
L722-L740 检查采集器是否提前退出,默认不允许部分成功。
| 采集器 | Shell 位置 | 周期 | 输出 |
|---|---|---|---|
nvidia-smi | L552-L565 | 1 秒 | gpu_samples.csv |
| RDMA HCA counters | L566-L592 | 1 秒 | rdma.csv |
| DCGM | L645-L655 | 1 秒 | dcgm_dmon.log |
mpstat | L656-L663 | 5 秒 | mpstat.log |
pidstat -durw | L664-L671 | 5 秒,进程级 | pidstat.log |
sar -n DEV,EDEV | L672-L678 | 5 秒 | sar_net.log |
perf stat | L680-L689 | 5 秒 | perf_stat.log |
numastat | L618-L644 | 5 秒 | numa_samples.csv |
CPU、进程、perf 和 sar 的每行均由 Shell 增加
wall_time_ns TAB node TAB payload。NUMA 直接转成结构化 CSV,
避免旧版线程级 1 秒日志过大,也让所有指标能按 Case 切片。
7. 精确测量窗口
7.1 Phase 1 如何标记主测量
Phase 1 run_quick_map.sh:L547-L564 每 100 ms 观察 bench 日志;
发现 Starting main benchmark run 后调用
quick_map_results.py mark-measurement-start。
quick_map_results.py:L340-L385 用这个起点和
bench.json.duration 生成:
measurement_started_at
measurement_ended_at
measurement_duration_s
measurement_window_source = bench_main_marker_plus_duration
7.2 Phase 2 如何使用
hardware_contention_attribution.py:L509-L560 优先读取上述字段。
只有兼容旧结果时才可能使用进程级窗口;正式配置
REQUIRE_PRECISE_WINDOWS=1 会拒绝任何 fallback。
L561-L841 对 GPU、DCGM、CPU、进程、perf、NUMA、netdev 和 RDMA
使用同一个 started_ns ≤ sample ≤ ended_ns 条件。
8. 逐指标报告
Python summarize 位于
hardware_contention_attribution.py:L1036-L1378。
它不只生成一个抽象结论,而是按 Phase 2 第 5 节依次写出:
| 指标 | 解析函数 | Case 汇总文件 |
|---|---|---|
| GPU | summarize_gpu_rows L377-L413 | case_gpu_summary.csv、case_gpu_node_summary.csv |
| DCGM | parse_dcgm L167-L192 | case_dcgm_summary.csv |
| CPU | parse_mpstat L193-L222 | case_cpu_summary.csv |
| 进程 | parse_pidstat L223-L289 | case_process_summary.csv |
| perf | parse_perf L290-L310 | case_perf_summary.csv |
| NUMA | 结构化 CSV + summarize_case_metrics | case_numa_summary.csv |
| Linux netdev | parse_sar_net L311-L358 | case_netdev_summary.csv |
| RDMA | summarize_rdma_rows L424-L484 | case_rdma_summary.csv |
| P2P/NCCL | load_communication_rows + aggregate_communication_rows L842-L928 | communication_summary.csv、communication_aggregate.csv |
report.md 对每组都打印有效样本数、Mean/P95/Max、Head/Worker
或 Case 间比较和源文件。解析不到的值保留为 -,不会被写成 0。
9. 结果目录
results/<RUN_ID>/
manifest.json
commands/
communication/
service/
bench/<phase1-sub-run>/
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
10. 函数行号索引
10.1 Shell 编排器
| 行号 | 函数组 | 职责 |
|---|---|---|
| L26-L66 | 日志、远端执行、命令证据 | 基础设施。 |
| L67-L199 | 配置、工具、时钟、GPU 空闲门禁 | 正式运行前 fail-fast。 |
| L200-L268 | Manifest、marker、Phase 1 委托 | 运行身份与复用边界。 |
| L269-L447 | 通信基线 | P2P、8/16-rank AllReduce、CROSS_NIC A/B。 |
| L448-L516 | 服务和静态快照 | 启停 Phase 1 双机服务并保存环境。 |
| L517-L710 | 采集命令与启动 | 两节点分层采样。 |
| L711-L772 | 采集器检查和停止 | fail-closed 与残留清理。 |
| L782-L835 | fixed/mixed Case | 代表负载编排。 |
| L836-L858 | 汇总、Manifest、trap | 结果收口。 |
| L859-L928 | run_all | 完整状态机。 |
| L929-L968 | 辅助 action 与 main | communication/all/summarize/stop 分发。 |
10.2 Python 文件
| 文件/行号 | 职责 |
|---|---|
communication_baseline.py:L20-L44 | 尺寸解析、分位数和 JSON 结果协议。 |
L45-L106 | CUDA P2P 全矩阵。 |
L107-L198 | NCCL AllReduce 与正确性。 |
hardware_contention_attribution.py:L76-L166 | 时间、CSV、数字统计基础函数。 |
L167-L358 | DCGM、mpstat、pidstat、perf、sar 解析器。 |
L359-L508 | 通信、GPU、RDMA、bench 读取与汇总。 |
L509-L841 | 精确窗口和全部 Case 指标切片。 |
L842-L1035 | 通信聚合、CSV、Marker、Manifest。 |
L1036-L1378 | 全部输出表和逐指标 report.md。 |
L1379-L1480 | CLI 子命令。 |