Skip to content

CollectiveX: add a per-component bandwidth view to the sweep summary / CollectiveX:为扫描摘要新增按组件的带宽视图 - #2434

Open
Oseltamivir wants to merge 2 commits into
mainfrom
collectivex-bandwidth
Open

CollectiveX: add a per-component bandwidth view to the sweep summary / CollectiveX:为扫描摘要新增按组件的带宽视图#2434
Oseltamivir wants to merge 2 commits into
mainfrom
collectivex-bandwidth

Conversation

@Oseltamivir

Copy link
Copy Markdown
Collaborator

Why

Every case-attempt artifact already carries per-component bytes and latency, but nothing turns the pair into a rate. The sweep summary reports how long a dispatch took and never says how close it came to moving that payload at link speed — so ## CollectiveX EP results has one axis, latency, and no second one.

What changed

  1. A bandwidth consumer (bandwidth.py), deliberately built as a structural twin of summarize.py: same --results-dir/--runner/--ts flags, same render() -> markdown, same print(render(...)), reading the same case documents. It is a pure consumer — no harness, runtime, or GPU change — and it gates nothing.

    Two views. Effective bandwidth divides byte_provenance[c].total_logical_bytes by the component's latency at each ladder point, per GPU. The alpha/beta fit regresses per-component p50 latency on bytes across the ladder, latency(bytes) ~= alpha + bytes/beta, separating the bandwidth term from the fixed per-call launch/sync/rendezvous floor that dominates small token counts. The regression is against emitted bytes rather than T, because routed_copies is sub-linear in T at high fan-out, and rungs that failed the correctness gate are excluded — a rung whose payload came back corrupt is not a measurement of how fast correct data moves.

  2. Wired into the sweep summary (.github/workflows/collectivex-sweep.yml). Called next to summarize.py, over the same staged results directory and into the same $GITHUB_STEP_SUMMARY, so every leg reports how fast it moved its payload alongside how long the point took. Both are pure renderers over already-written documents and neither gates the leg, which is why both keep || true: a formatting failure must not turn a green sweep red or mask a real one. Without this the consumer only exists for whoever runs the script by hand — the workflow's only other output is the neutral per-case JSON, so an unwired renderer in this repo cannot reach a reader at all.

Fit quality is part of the result, not a footnote

A straight line through a ladder is a MODEL, and on real data it is sometimes a bad one. Two rungs of noise once yielded beta = 1018 GB/s per GPU on a B200 — above the link's per-direction peak, i.e. physically impossible — from a fit with R² = 0.29, printed indistinguishably from a neighbouring R² = 0.99 fit. R² alone is not enough either: a dispatch ladder that stayed latency-bound throughout came back at 3763 GB/s with R² = 0.92, because alpha explained nearly everything and the slope was not identifiable. So Fit carries r2, point count and bandwidth_share as results rather than diagnostics, beta is withheld unless both R² and the transfer term's share of the top rung clear their thresholds, and alpha is marked as an extrapolation when the ladder never approaches zero bytes (prefill ladders start at T=1024).

Deliberately NOT added: a per-SKU peak-bandwidth table to check beta against. That would put hardware knowledge belonging to platform_config.json into a consumer, where it would age badly and mislead in its own right; the fit-quality gate is what actually caught the pathology above.

Reading the numbers

Documented in the module docstring: logical bytes include the copies a rank routes to itself, which never cross the interconnect, so wire traffic is (1 - routing.locality.local_rank_fraction) × the reported figures; and beta is a marginal rate that legitimately sits above every measured point.

One deliberate choice worth flagging now that #2422 has landed: GB/s is derived from components[c] — the cross-rank MAX latency — so it stays consistent with the latency table it sits beside. #2422's cross_rank_min_us is the skew-excluded companion, and a skew-inflated point therefore reads as lower bandwidth here. Switching the rate to MIN, or emitting both, is a follow-up rather than something this PR assumes.

Verification

  • 76 CollectiveX tests pass (the 10 new ones are math-only — unit conversion, the fit, and each gate — and need no GPU; the existing unittest discover picks them up).
  • Workflow YAML re-parses and the new step is present in jobs.sweep.steps.
  • End-to-end smoke test: no artifact fixtures exist in the repo, so both renderers were run against a synthetic case-attempt document. summarize.py renders its table and bandwidth.py renders per-point GB/s per component plus the fit line, confirming the render path, percentile keying, locality suffix and fit gate all execute against the real document shape.
  • Rebased onto current main (includes CollectiveX: stop charging rank entry skew to component latency / CollectiveX:不再把 rank 进入时间差计入组件延迟 #2422), so the additions-only perf-changelog.yaml gate is satisfied.

中文说明

为什么

每个 case-attempt 产物本就同时带有各组件的字节数与延迟,但没有任何代码把这两者换算成速率。扫描摘要只报告 dispatch 耗时多少,从不说明它离以链路速率搬运该载荷还有多远 —— 也就是说 ## CollectiveX EP results 只有延迟这一个坐标轴,没有第二个。

改了什么

  1. 带宽消费者(bandwidth.py),有意做成 summarize.py 的结构孪生:相同的 --results-dir/--runner/--ts 参数、相同的 render() -> markdown、相同的 print(render(...)),读取同一批 case 文档。它是纯消费者 —— 不改动 harness、runtime 或 GPU 路径 —— 也不作为任何门禁。

    两种视图。有效带宽在每个 ladder 测点上用 byte_provenance[c].total_logical_bytes 除以该组件的延迟,按每 GPU 报告。alpha/beta 拟合在整个 ladder 上对各组件 p50 延迟与字节数做回归,latency(bytes) ≈ alpha + bytes/beta,从而把带宽项与小 token 数下占主导的固定单次调用开销(launch/sync/rendezvous)分离开。回归对的是产物中的字节数而非 T,因为在高 fanout 下 routed_copies 相对 T 是次线性的;未通过正确性门禁的档位会被排除 —— 载荷已损坏的档位,不构成「正确数据搬运速度」的测量。

  2. 接入扫描摘要(.github/workflows/collectivex-sweep.yml)。接在 summarize.py 旁边调用,读取同一个已落盘的结果目录、写入同一份 $GITHUB_STEP_SUMMARY,这样每条 leg 在报告测点耗时的同时,也报告它搬运载荷的速率。两者都是对既有产物的纯渲染器,都不作为门禁,这也是二者都保留 || true 的原因:格式化失败既不能把绿色扫描变红,也不能掩盖真正的失败。若不接入,这个消费者只对手动执行脚本的人存在 —— 该工作流唯一的其他输出是中立的按 case JSON 产物,因此一个没有接线的渲染器在本仓库里根本无法触达任何读者。

拟合质量属于结果本身,而非脚注

穿过 ladder 的直线是一个模型,而在真实数据上有时是个糟糕的模型。曾经有两档噪声在 B200 上拟合出 beta = 1018 GB/s 每 GPU —— 高于链路单向峰值,物理上不可能 —— 而其 R² 仅为 0.29,却与相邻 R² = 0.99 的拟合打印得一模一样。单看 R² 也不够:一条全程受延迟主导的 dispatch ladder 拟合出 3763 GB/s 而 R² = 0.92,因为 alpha 解释了几乎全部变化、斜率不可辨识。因此 Fitr2、点数与 bandwidth_share 作为结果而非诊断信息返回;beta 仅在 R² 与顶档传输项占比双双达标时才输出;而当 ladder 从未接近零字节时(预填充 ladder 从 T=1024 起步)alpha 会被标记为外推值。

有意不做的事:不引入按 SKU 的峰值带宽表来校验 beta。那会把本属于 platform_config.json 的硬件知识塞进消费者,既容易过时又会自行产生误导;真正抓住上述异常的是拟合质量门禁。

数值该怎么读

写在模块 docstring 中:逻辑字节包含 rank 路由给自己、根本不过互联的那部分拷贝,因此实际上链流量为 (1 - routing.locality.local_rank_fraction) × 报告值;而 beta 是边际速率,理应高于每一个实测点。

#2422 已合入,因此有一处取舍值得在此说明:GB/s 由 components[c](即跨 rank MAX 延迟)推导,以便与紧邻的延迟表格保持一致。#2422cross_rank_min_us 是排除 skew 后的对照量,所以一个被 skew 抬高的测点在这里会表现为更低的带宽。把速率改用 MIN、或同时输出两者,属于后续工作,本 PR 不预设该结论。

验证

  • CollectiveX 76 项测试通过(新增 10 项只覆盖数学部分 —— 单位换算、拟合与各道门禁 —— 无需 GPU;现有的 unittest discover 会自动收集)。
  • 工作流 YAML 重新解析通过,新步骤存在于 jobs.sweep.steps 中。
  • 端到端冒烟测试:仓库中没有产物样例,因此用一份合成的 case-attempt 文档分别跑了两个渲染器。summarize.py 正常渲染表格,bandwidth.py 正常渲染各组件的逐点 GB/s 与拟合行,确认渲染路径、百分位键、locality 后缀与拟合门禁都能在真实文档结构上执行。
  • 已 rebase 到当前 main(含 CollectiveX: stop charging rank entry skew to component latency / CollectiveX:不再把 rank 进入时间差计入组件延迟 #2422),因此满足 perf-changelog.yaml 仅允许新增的校验。

Every case-attempt artifact already carries per-component bytes and latency, but nothing
turns the pair into a rate, so the sweep reports how long a dispatch took without saying
how close it came to moving that payload at link speed. Add a consumer that reads only
committed artifacts -- no harness, runtime, or GPU change, and it gates nothing.

Two views. Effective bandwidth divides byte_provenance[c].total_logical_bytes by the
component's latency at each ladder point, reported per GPU. The alpha/beta fit regresses
per-component p50 latency on bytes across the ladder, latency(bytes) ~= alpha + bytes/beta,
which separates the bandwidth term from the fixed per-call launch/sync/rendezvous floor
that dominates small token counts. The regression is against emitted bytes rather than T,
because routed_copies is sub-linear in T at high fan-out, and rungs that failed the
correctness gate are excluded -- a rung whose payload came back corrupt is not a
measurement of how fast correct data moves.

A straight line through a ladder is a MODEL, and on real data it is sometimes a bad one, so
the fit reports its own trustworthiness rather than leaving that to the reader. Two rungs of
noise once yielded beta = 1018 GB/s per GPU on a B200 -- above the link's per-direction peak,
i.e. physically impossible -- from a fit with R^2 = 0.29, printed indistinguishably from a
neighbouring R^2 = 0.99 fit. R^2 alone is not enough either: a dispatch ladder that stayed
latency-bound throughout came back at 3763 GB/s with R^2 = 0.92, because alpha explained
nearly everything and the slope was not identifiable. So Fit carries r2, point count and
bandwidth_share as results rather than diagnostics, beta is withheld unless both R^2 and the
transfer term's share of the top rung clear their thresholds, and alpha is marked as an
extrapolation when the ladder never approaches zero bytes (prefill ladders start at T=1024).

Deliberately NOT added: a per-SKU peak-bandwidth table to check beta against. That would
put hardware knowledge belonging to platform_config.json into a consumer, where it would
age badly and mislead in its own right; the fit-quality gate is what actually caught the
pathology above.

The interpretation the numbers need is documented in the module docstring: logical bytes
include the copies a rank routes to itself, which never cross the interconnect, so wire
traffic is (1 - routing.locality.local_rank_fraction) x the reported figures, and beta is a
marginal rate that legitimately sits above every measured point.

Tests are math-only (unit conversion, the fit, and each gate) and need no GPU; the existing
unittest discovery picks them up.

中文:新增按组件统计的带宽消费者(consumer)。

每个 case-attempt 产物本就带有各组件的字节数与延迟,但没有任何代码把这两者换算成速率,
于是扫描只报告 dispatch 耗时多少,却不说明它离以链路速率搬运该载荷还有多远。本提交新增一个
只读取已落盘产物的消费者 —— 不改动 harness、runtime 或 GPU 路径,也不作为任何门禁。

两种视图。有效带宽在每个 ladder 测点上用 byte_provenance[c].total_logical_bytes 除以该组件的
延迟,按每 GPU 报告。alpha/beta 拟合则在整个 ladder 上对各组件 p50 延迟与字节数做回归,
latency(bytes) ≈ alpha + bytes/beta,从而把带宽项与小 token 数下占主导的固定单次调用开销
(launch/sync/rendezvous)分离开。回归对的是产物中的字节数而非 T,因为在高 fanout 下
routed_copies relative T 是次线性的;未通过正确性门禁的档位会被排除 —— 载荷已损坏的档位,
不构成「正确数据搬运速度」的测量。

穿过 ladder 的直线是一个模型,而在真实数据上有时是个糟糕的模型,所以拟合结果自己携带可信度,
而不是把判断留给读者。曾经有两档噪声在 B200 上拟合出 beta = 1018 GB/s 每 GPU —— 高于链路单向
峰值,物理上不可能 —— 而其 R² 仅为 0.29,却与相邻 R² = 0.99 的拟合打印得一模一样。单看 R²
也不够:一条全程受延迟主导的 dispatch ladder 拟合出 3763 GB/s 而 R² = 0.92,因为 alpha 解释了
几乎全部变化、斜率不可辨识。因此 Fit 把 r2、点数与 bandwidth_share 作为结果而非诊断信息返回,
beta 仅在 R² 与顶档传输项占比双双达标时才输出,而当 ladder 从未接近零字节时(预填充 ladder 从
T=1024 起步)alpha 会被标记为外推值。

有意不做的事:不引入按 SKU 的峰值带宽表来校验 beta。那会把本属于 platform_config.json 的硬件
知识塞进消费者,既容易过时又会自行产生误导;真正抓住上述异常的是拟合质量门禁。

数值解读所需的前提写在模块 docstring 中:逻辑字节包含 rank 路由给自己、根本不过互联的那部分
拷贝,因此实际上链流量为 (1 - routing.locality.local_rank_fraction) × 报告值;而 beta 是边际
速率,理应高于每一个实测点。

测试只覆盖数学部分(单位换算、拟合、各道门禁),无需 GPU;现有的 unittest discovery 会自动收集。
The consumer added in the previous commit was never invoked, so the second axis existed
only for whoever ran the script by hand. The sweep's summary step renders the latency
table and nothing else, and since the workflow's only other output is the neutral per-case
JSON, an unwired renderer in this repo cannot reach a reader at all.

Invoke it next to summarize.py, over the same staged results directory and into the same
step summary, so every leg reports how fast it moved its payload alongside how long the
point took. Both are pure renderers over already-written documents and neither gates the
leg, which is why both keep `|| true`: a formatting failure must not turn a green sweep red
or mask a real one.

中文:在扫描摘要中渲染带宽视图。

上一个提交加入的消费者从未被调用,因此这条「第二坐标轴」只对手动执行脚本的人存在。扫描的摘要
步骤只渲染延迟表格;而该工作流唯一的其他输出是中立的按 case JSON 产物,所以一个没有接线的
渲染器在本仓库里根本无法触达任何读者。

把它接在 summarize.py 旁边调用,读取同一个已落盘的结果目录、写入同一份 step summary,这样每条
leg 在报告测点耗时的同时,也报告它搬运载荷的速率。两者都是对既有产物的纯渲染器,都不作为门禁,
这也是二者都保留 `|| true` 的原因:格式化失败既不能把绿色扫描变红,也不能掩盖真正的失败。

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bugs found, but this adds a fairly substantial new statistical-fitting consumer (OLS regression, R²/bandwidth-share gating with several hand-picked thresholds, MAX-vs-MIN latency choice interacting with #2422) — worth a human sanity check on the modeling choices even though the code itself is well-isolated and tested.

What was reviewed: the OLS fit and R²/bandwidth-share gating math, the correctness-gate row exclusion, the alpha-extrapolation flag, and the workflow wiring (|| true, non-gating). Checked and ruled out: the bandwidth_share denominator using ladder-wide max latency rather than the top rung's own latency — this matches the documented intent (share of the largest measured latency, not necessarily the max-bytes rung) rather than being a bug; the correctness-gate rung-exclusion note undercounting when a component's fit fails entirely; and the new script not being listed in the README's core-path list.

Extended reasoning...

Overview

This PR adds experimental/CollectiveX/bandwidth.py, a new pure-renderer consumer that computes per-component effective bandwidth (bytes/latency) and an alpha/beta OLS fit (latency ~= alpha + bytes/beta) from existing case-attempt JSON documents, plus wiring it into .github/workflows/collectivex-sweep.yml next to the existing summarize.py step, and 10 new unit tests covering the math.

Security risks

None. This is a read-only renderer over already-written JSON artifacts in an experimental CI sweep workflow — no new inputs, no auth/crypto/permissions surface, no network or credential handling. The workflow step is appended with || true so it cannot affect job pass/fail status.

Level of scrutiny

Low production risk (non-gating, experimental tooling), but the file itself carries real quantitative logic: an OLS regression, two hand-tuned gating thresholds (FIT_MIN_R2=0.9, FIT_MIN_BANDWIDTH_SHARE=0.25), an extrapolation heuristic, and a documented design choice to derive bandwidth from cross-rank MAX latency (consistent with the adjacent latency table) rather than the MIN latency introduced by #2422. None of this is mechanical — it's the kind of statistical-modeling choice where a domain expert's sign-off adds value beyond what a correctness-focused bug hunt can verify, even though no bugs were found in the implementation.

Other factors

Test coverage is strong: 10 new tests directly target the two real failure modes described in the PR body (a noisy fit with low R² that must withhold beta, and a latency-bound ladder with high R² but non-identifiable slope that must also withhold beta via the bandwidth-share gate), plus the correctness-gate exclusion and alpha-extrapolation flag. The bug-hunting system's candidate findings around the bandwidth_share denominator and dead-code rung exclusion were examined and found to match documented intent rather than being bugs. Given the design tradeoffs called out explicitly in the PR description (MAX vs MIN latency, no per-SKU peak table), this reads like a PR the author already expects a human to weigh in on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant