本目录保留四种方法的原始观测,并正式生成三类比较:
| 资源模式 | 基线 | 容错版 |
|---|---|---|
| 1 GPU | single_gpu_baseline |
single_gpu_fault_tolerant |
| N GPU / N MPI rank(N ≥ 2) | multi_gpu_baseline |
multi_gpu_fault_tolerant |
同资源模式的两行分别回答单卡和多卡容错开销;此外还比较
multi_gpu_baseline 与 single_gpu_baseline,回答分区、NCCL/MPI 和增加 GPU
后的整体变化。后一比较不是容错开销,分析表会以独立 comparison_id 标识。
prepare_run.py → run_item.py → collect_results.py
├─ performance → analyze_overhead.py → plot_overhead.py
└─ profiling → stop after collect
prepare_run.py:冻结 schema v9 配置、metadata/BFS source 快照、数据集和二进制指纹、Git/环境信息、Release 构建配置与 Slurm work item。run_item.py:每条 warmup 或测量命令都写独立 UUID raw JSON、stdout 和 stderr;同一次run_item调用写出的记录共享一个attempt_session_id。每次执行前后都会检查冻结的数据集(size/mtime)和二进制(含 SHA256),发生漂移即保留为input_drift,不进入结果。进程结束后另行采集实际 worker 的 GPU 型号、UUID、驱动和显存,不计入process_wall_ms;超时会终止整个 launcher process group,避免遗留 MPI/GPU rank。collect_results.py:先把work_items.json与 manifest 中的 algorithm×dataset 笛卡尔积、编号和计划 invocation 总数交叉验证,再验证 raw JSON 的 schema、session UUID、identity、结果摘要、每-rank timing 与 profiling trace;trace 的每个文件还以 size/SHA256 绑定到 attempt。之后才选择 latest-success observation;不合格 raw 仍留在磁盘和报告中,但绝不会静默进入 CSV 或配对。analyze_overhead.py:只接受 performance run,建立单卡容错、多卡容错和多卡/单卡 baseline 三类 pair;最终结果 hash 不同、attempt_session_id不同或执行环境不兼容的样本进入excluded_pairs.csv。迭代数差异只作为诊断字段,不再错误地当成正确性判据。plot_overhead.py:只读取analysis/,不触碰 raw 数据。
一次运行目录如下:
outputs/experiments/<run-tag>/
├── manifest/ # config、输入快照、数据/二进制 SHA256、环境、work items
├── raw/ # immutable attempt JSON 与 stdout/stderr
├── collected/ # observations、每-rank timing、逐轮 activity/timing 与 QC report
├── analysis/ # performance run:三类 paired samples、excluded pairs、summary/report
├── figures/ # performance run:runtime、paired overhead、speedup、components/manifest
└── slurm/ # array/collector scheduler logs
raw/attempts/**/*.json 与对应 stdout/stderr 是完整证据源。第三方无需采用本项目画图脚本;schema v9 的主要可重建表为:
| 表 | 关键原始列 |
|---|---|
attempts.csv / observations.csv |
attempt_id, attempt_session_id, record_id,algorithm/dataset/method/profile/repeat/rank 配置,算法参数,实际 worker GPU/驱动、状态和进程环境,process_wall_ms,迭代数、结果 hash、检测标志及首次异常轮次,以及 24 个 timing 的 rank-max/rank-mean 列 |
rank_timings.csv |
attempt/session/record identity、参数、rank, source,以及该 rank 的 24 个规范 timing 原值 |
iteration_activity.csv |
attempt/session identity、rank/iteration/kernel、work/owned/active/launched/tail/inactive 计数、idle 比例、critical 与实际冗余任务数 |
iteration_timings.csv |
attempt/session identity、rank/iteration/stage/elapsed_ms |
attempts.csv 保留所有通过 raw 结构验证的 attempt,observations.csv 是每个 identity 的选中投影。attempt 专属逐轮 CSV/JSON 仍位于 raw/profiling/<attempt_uuid>/:这些轻量 trace 文件只带本地算法/method/rank 等 identity,必须通过目录中的 attempt UUID 关联 raw/attempts/ 下的 attempt JSON,并结合 trace 目录内的 per-rank summary 才能恢复完整实验上下文。collected/iteration_activity.csv 与 collected/iteration_timings.csv 才是补齐 attempt/session/record、phase/repeat 和参数、可交给第三方按行独立分析的 merged 表。collected/ 可从 raw 重建,不替代 raw 中的 argv、完整 provenance 和输入 guard。
旧的 outputs/experiments/local-overhead-v* 不会被移动或删除,也不能和新 schema 混合。
脚本默认用本仓库由 uv 创建的 .venv:
cd /workplace/home/jiangnan/Projects/GraphAlgorithms
uv venv .venv
uv pip install --python .venv/bin/python matplotlib修改 dataset/*.mtx 后,先刷新 metadata 和 BFS source:
.venv/bin/python scripts/utils/select_source_nodes.py --top-k 10然后构建四算法 × 四方法的正式性能二进制:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNCCL_ROOT=/path/to/nccl
cmake --build build --target performance -j正式 benchmark 必须使用 Release 构建;prepare 阶段会拒绝其他构建类型,并在 manifest 中保存 CMake cache 的编译器/flags、CMakeCache.txt 与 compile_commands.json 指纹。需要逐轮统计或 Nsight Systems/NVTX 分析时,在同一个 Release build 中另行构建 cmake --build build --target profiling -j。两类 target 使用同一份算法源码,但输出不同的可执行文件,例如 build/bin/bfs/bfs_single_gpu_baseline_performance 与 ..._profiling。
完整图集、固定参数和 launcher 写在 configs/overhead.json;它显式列出数据集,避免 dataset/ 中临时文件改变正式实验范围。instrumentation_profile 默认为 performance,并会进入 manifest、raw attempt、pair id 和 observations;一个 run 目录只允许一种 profile。
schema v9 的 TIMING_METRICS 包含 24 个规范字段:
main_loop_ms, gpu_compute_ms, graph_kernel_ms, graph_update_ms,
score_and_mark_ms, tolerance_graph_update_ms, tolerance_gpu_bookkeeping_ms,
d2h_summary_ms, cpu_validation_ms, checker_end_to_end_ms,
cpu_check_drain_ms, nccl_exchange_ms, mpi_sync_ms,
postcheck_total_ms, postcheck_mpi_ms, communication_ms, algorithm_total_ms,
single_gpu_setup_ms, distributed_setup_ms, initial_ghost_exchange_ms,
final_result_copy_ms, final_result_gather_ms, e2e_unattributed_ms,
algorithm_e2e_ms
其中三个不能混用的顶层口径是:
algorithm_total_ms = main_loop_ms + cpu_check_drain_ms + postcheck_total_ms
algorithm_e2e_ms = setup + initial exchange + algorithm_total
+ final copy/gather + unattributed boundary overhead
process_wall_ms = runner 启动命令到整个子进程组退出的外层时间
algorithm_total_ms 是排除 setup/final gather 的稳态算法时间;
algorithm_e2e_ms 从进入算法函数一直到完整结果回到 host,single/multi 都在该边界内
完成逻辑设备选择和 CUDA 上下文初始化;多 GPU 的 NCCL 初始化也计入 setup。多 GPU 的实际完成延迟
分别使用 rank_algorithm_total_max_ms 和 rank_algorithm_e2e_max_ms;rank mean 只
表示平均工作量。single_gpu_setup_ms 与 final_result_copy_ms 用于单卡,
distributed_setup_ms、initial_ghost_exchange_ms 与 final_result_gather_ms 用于多卡。
上述加法在每条 rank-local 记录上成立,rank mean 也可加;各阶段的
rank max 可能来自不同 rank,不能把它们相加成虚构的最慢-rank 时间线。
process_wall_ms 还包含 launcher、PMIx、各 rank 读图和进程退出,独立保存,不能与
上述内部口径混成同一指标。runner 默认设置 CUDA_MODULE_LOADING=EAGER,防止一次性
lazy function loading 被 CUDA event 误算为首个 kernel 时间;该设置也写入 provenance。
graph_kernel ⊂ gpu_compute ⊂ main_loop,且 postcheck_mpi ⊂ postcheck_total。
score-and-mark、容错主 kernel、bookkeeping、D2H、CPU validation/checker latency、
NCCL 与 MPI 是解释性子集或工作量,不能再堆叠到总时间。详细字段和融合 kernel 的
测量边界见 Profiling 与原始计时数据。
performance 二进制不做逐任务 checker event/CPU 时钟采集,因此其 d2h_summary_ms、
cpu_validation_ms、checker_end_to_end_ms 为 0;cpu_check_drain_ms 仍记录算法结束
后必须等待的真实尾部。profiling 二进制才填充前三个详细字段并写逐轮 timing 表,
其受扰动的时间不能替代 performance 绝对数据。
逻辑 pair identity 是:
algorithm + dataset + repeat + GPU mode + instrumentation profile
此外两侧必须具有相同的非空 attempt_session_id,从而证明来自同一次 run_item
会话。只有两侧 observation 都成功、执行上下文符合该比较、且非空最终结果 hash 相等时,
才计算差值和百分比。同 GPU 模式还要求卡映射与 MPI rank 数一致;单/多 baseline
比较允许这两项按定义不同。迭代轮次可能因分区和原地更新顺序不同,始终保存
iteration_delta/iterations_match,但不单独据此排除。其余 pair 的绝对时间仍保留,
并带明确原因进入 analysis/excluded_pairs.csv。
图中的 † 表示至少一个 pair 存在迭代轮次不一致;这只是行为提示。开销图仅用结果
hash 与环境检查合格的 pair,并标注有效样本数 n。统计使用每个 repeat 配对结果的
中位数与 IQR。
每个算法按单 GPU baseline 时间自动分组,每组生成:
*_runtime.png:四柱绝对内部完成时间(1 GPU baseline/fault-tolerant,N GPU baseline/fault-tolerant);N GPU柱是max(rank internal total)。*_paired_overhead.png:1 GPU 和配置的 N GPU的严格配对容错开销百分比。这是主结论图。*_baseline_speedup.png:multi_gpu_baseline相对single_gpu_baseline的algorithm_e2e与稳态algorithm_total两类严格配对加速比。*_components.png:核心 kernel 的 paired overhead、多 GPU NCCL/MPI 调用、容错 CPU drain/post-check、rank imbalance。所有 baseline-vs-tolerance panel 只使用严格合格 pair;CPU tail 是 tolerance-only 的绝对观测。各 panel 非堆叠。
Smoke test:
CUDA_VISIBLE_DEVICES=6,7 \
CUDA_DEVICE_ORDER=PCI_BUS_ID \
CONFIG=scripts/experiments/configs/smoke.json \
MPI_RANKS=2 \
RUN_TAG=smoke_$(date +%Y%m%d_%H%M%S) \
bash scripts/experiments/run_local.sh这里的两张卡只是示例,不是框架限制。要运行 N 个 rank,设置 MPI_RANKS=N 并让
CUDA_VISIBLE_DEVICES 列出至少 N 张卡;多 GPU 方法要求 N ≥ 2,并按一卡一 rank
执行。例如三卡可用 CUDA_VISIBLE_DEVICES=5,6,7 MPI_RANKS=3 ...。
将 INSTRUMENTATION_PROFILE=profiling 加到命令中即可改用 profiling 二进制;未设置时读取配置文件,正式配置默认使用 performance。
完整实验:
CUDA_VISIBLE_DEVICES=6,7 \
CUDA_DEVICE_ORDER=PCI_BUS_ID \
MPI_RANKS=2 \
RUN_TAG=overhead_$(date +%Y%m%d_%H%M%S) \
REPEAT=5 WARMUP=1 TIMEOUT=3600 \
bash scripts/experiments/run_local.sh选择子集:
CUDA_VISIBLE_DEVICES=6,7 \
DATASETS="cit-HepPh tech-RL-caida" \
ALGORITHMS="bfs cc" \
REPEAT=5 \
bash scripts/experiments/run_local.sh使用 PREPARE_ONLY=1 只检查输入和创建 manifest。默认对图做 SHA256;开发 smoke 时可加 FAST_FINGERPRINTS=1,只记录文件大小和 mtime。
如需恢复中断任务,使用同一个 RUN_ROOT;本地 runner 的 --resume 会保留并跳过完整的已有 identity。加 RETRY_FAILED=1 后,若某个 measured repeat 的 selected methods 中任一方法缺失、最新 attempt 失败、缺 session ID 或来自不同 session,则该 repeat 的全部 selected methods 都会在新的同一 attempt_session_id 下重跑,避免把新旧 attempt 混成 pair。没有 RETRY_FAILED=1 时,保留的测量失败仍会让 runner 以非零状态结束,不能被误报为成功。
collect_results.py --strict 只以正式 measurement 的完整性作为退出条件;失败 warmup 会完整保留并标为 warning,不会丢弃已完成的正式测量。analyze_overhead.py --strict 则额外要求没有任何被排除的 pair,适合作为论文数据的最终 QC。performance run 可从 raw 重做完整派生层:
.venv/bin/python scripts/experiments/collect_results.py --run-dir outputs/experiments/<run-tag>
.venv/bin/python scripts/experiments/analyze_overhead.py --run-dir outputs/experiments/<run-tag>
.venv/bin/python scripts/experiments/plot_overhead.py --run-dir outputs/experiments/<run-tag> --formats png pdfprofiling run 只执行 collect_results.py;本地和 Slurm 流水线在 collect 后都会跳过
performance-only 的 analyze_overhead.py 与 plot_overhead.py。profiling 的解释性
trace 应与单独的 matching performance run 通过下述等价性检查关联。
performance 与 profiling 必须写入两个 run 目录。两边 collect 后,用结果、迭代和检测 摘要检查编译 profile 没有改变语义:
.venv/bin/python scripts/experiments/check_profile_equivalence.py \
--performance-run-dir outputs/experiments/<performance-tag> \
--profiling-run-dir outputs/experiments/<profiling-tag>两边的 dataset/metadata/BFS source、参数、方法、rank、launcher、Git 工作树证据和 Release 构建配置必须一致。工具逐 measurement key 比较结果 hash、迭代数以及全部检测字段(含首次异常轮次);缺失、重复、失败或 profile 标记错误都会使检查失败。
MPLCONFIGDIR=/tmp/matplotlib-graphalg 只是把 Matplotlib 的字体/配置缓存放到一个可写、
稳定的位置;只有画图环境的默认配置目录不可写或希望隔离缓存时才需要,运行算法和
生成原始数据本身不依赖它。
最终检查可使用 collect_results.py --strict 和 analyze_overhead.py --strict。
提交器先 prepare,再提交 dataset × algorithm array;所有 worker 结束后,afterany collector 对 performance run 执行 collect → analyze → plot,对 profiling run 只执行 collect。PROJECT_ROOT、RUN_ROOT、.venv、build/ 和 dataset/ 必须以同一路径出现在所有分配节点与 collector 节点上(通常是共享文件系统):
RUN_TAG=overhead_$(date +%Y%m%d_%H%M%S) \
ARRAY_CONCURRENCY=1 \
REPEAT=5 WARMUP=1 TIMEOUT=3600 \
bash scripts/experiments/slurm/submit_overhead.sh提交器不默认添加账号或分区。集群要求时才显式设置:
PARTITION=<partition> ACCOUNT=<account> QOS=<qos> \
RUN_TAG=overhead_$(date +%Y%m%d_%H%M%S) \
bash scripts/experiments/slurm/submit_overhead.sh若出现账户/分区组合错误,先运行:
sacctmgr show assoc user="$USER" format=Account,Partition
sinfo -o "%P %a %l %G"默认 launcher 模板是 mpirun -np N --bind-to none,其中 N 来自冻结的 mpi_ranks。若集群必须使用 srun,复制 JSON 配置、修改 launcher 后再提交;实际设置会冻结到 manifest。
mpi_ranks 是多 GPU 方法的 MPI rank 数,也是本框架假定的一卡一 rank 数;框架没有写死为 2。可直接编辑 JSON 的 mpi_ranks,也可通过 MPI_RANKS=N 覆盖。run_local.sh 会把该值传给 manifest;submit_overhead.sh 还会在单节点 worker 上提交 --ntasks=N --gpus-per-node=N。请让 CUDA_VISIBLE_DEVICES 包含至少 N 张卡;多 GPU 方法要求 N >= 2。
提交器会从冻结 manifest 计算一个 work item 的保守时限:(warmup + repeat) × 方法数 × timeout_s + 10 分钟,并通过 --time 覆盖 worker 脚本默认值。默认正式配置是 1-00:10:00;若集群时限更短,可显式设置 WORKER_TIME=HH:MM:SS,但这会降低超时覆盖裕量。COLLECTOR_TIME 默认为一小时。
重试 Slurm run(manifest 不会重建,旧 raw 不会覆盖):
RUN_ROOT=outputs/experiments/<run-tag> \
RESUME=1 RETRY_FAILED=1 ARRAY_CONCURRENCY=1 \
bash scripts/experiments/slurm/submit_overhead.sh自动 collector 会执行 collect_results.py --strict:正式 measurement 不完整时 job 明确失败。performance 流水线也会执行 analyze_overhead.py --strict,所以 result hash、session 或环境不匹配造成的 excluded pair 会让任务返回非零;仍会留下 collection、analysis 和诊断 figures。profiling run 只留下 collection 与逐轮 trace。不要手工修改 collected/、analysis/ 或 figures/ 来“修数据”;应该从 raw/ 重跑对应派生步骤。