CollectiveX MVP v0.1#2004
Open
Oseltamivir wants to merge 262 commits into
Open
Conversation
758fa52 to
1c5b901
Compare
7e5f80a to
28cbac4
Compare
4ff5841 to
bffd8d6
Compare
mi325x-mori legs failed at ep_mori.py: 'MoRI has no pinned GPU architecture for runner mi325x'. The re-add (36b0830) restored the capability/config/ launcher/common.sh groupings but missed the per-runner MoRI GPU-arch pin — mi325x is gfx942 (like mi300x), so add it to that branch. Comment updated to note MI300X/MI325X both use the AsyncLL XGMI kernel.
…perator network overlay)
…EP16 Track per-SKU scale-out RDMA fabric selectors in configs/network-config.json (un-gitignored) and overlay them onto the base operator config via config.py:_network_overlay — SKUs absent there fall back to the network secret, a missing/invalid file is a no-op. mi355x internode selectors (rdma0-7, gid 1, eno0) were hardware-validated (16/16 ranks, 4 QP/PE), so drop the mi355x×mori×16 override — its EP16 leg is now schedulable. mi300x/mi325x EP16 stay walled (no validated internode selectors: mi300x debug cluster node-boot broken, mi325x GHA nodes on an unreachable pod cluster). 26 tests green.
…twork-config.json) mi355x internode selectors now ship in configs/network-config.json and are overlaid by config.py, so the mi355x×mori×16 override is no longer needed — its EP16 leg is schedulable. mi300x/mi325x EP16 remain walled (no validated internode selectors).
Replace deepep-hybrid with a vendor-neutral nccl-ep backend and finish the backend-registry consolidation into configs/backends.json. nccl-ep (bench/ep_nccl_ep.py): the collective-library EP baseline on RCCL (AMD) / NCCL (NVIDIA). Not the naive equal-split all_to_all_single — a routing-aware token-rank variable-split all-to-all that satisfies the oracle contract: dispatch sends each token once per distinct destination rank (top-k row masked to that rank), combine is the reverse variable all-to-all + unweighted rank-sum. No source pin or build; runs on any image collective. Schedulable on both vendors, all SKUs, EP8/EP16. deepep-hybrid removed entirely: adapter, capability entry + b200 EP16 override, run_ep choices/import, backends.json entry, config.py registry emit, common.sh (registry names, probe, source-pin, canonical unset), prepare_backend.sh (both build fns + dispatch + persisted env), and the ep_harness label map + tests. Backend source pins + images now live in configs/backends.json, validated by config.py backend-registry and consumed by common.sh (fail-closed). 30 tests + 6 subtests green; no hybrid refs remain in code.
The per-SKU launchers rejected nccl-ep at setup (unsupported EP backend): add nccl-ep to the allowlists (single-slurm + gb-nv: deepep-v2|nccl-ep; mi-amds: mori|nccl-ep) and keep pinned-source staging deepep-v2-only (nccl-ep has no source build).
… provenance The commit uniquely determines its tree and submodule gitlinks, so rev-parse HEAD at source fetch already enforces them — the separate tree pin was redundant. The nccl-check upstream_commit was provenance-only (the patch is applied by stage.py rewrite-deepep-v2, which needs no hash). Remove both from configs/backends.json, the config.py registry emit, common.sh (required names + source-pin format repo|commit|fmt), prepare_backend.sh (cache key + DEEPEP_TREE export/persist), and the registry test. 30 tests + 6 subtests green.
Rip out the nccl-ep backend entirely (adapter, capability entry, run_ep choices/import + dead dist-init branch, launcher gates, common.sh + prepare_backend probes, ep_harness label map, workflow choice, tests). The matrix is back to deepep-v2 + mori. Plus dead-code/slop removal (reviewed, no remaining consumers): drop unused EPBackend oracle_layout/payload_unit + redundant WorkloadSpec fields (routing/seed/hidden/topk/experts/num_logical) + RankInputs.global_tokens; mori dead label/tuning vars; routing SOURCE_ID_CONTRACT + token_offset; probe verify_cache_mount; numpy from requirements; README/methodology trim. 30 tests + 6 subtests green; matrix generates deepep-v2 (9) + mori (4).
Move the timing profile, routing seed, and measured + conditioning token ladders out of ep_harness constants into configs/suites.yaml as the sole source; the matrix bakes them into every scheduled case and run_ep receives them only through required CLI args (no module defaults, no cross-check guards). Decode ladder extended to 512 tok/rank; prefill densified to 256..2048. Dedupe the oracle expert-transform coefficients and column pattern between transform and expectation; unify the oracle report shape.
…e operator-config merge Drop artifact fields nothing consumes: the top-level case object, outcome.validity, shape, realized_placement/device_count, oracle ordering_contract/order_stable, and the scheduled-case canonical/timing/samples/warmup_semantics duplicates (all still required fields survive; the frontend keys off identity.case_factors.case). Move the operator-config overlay merge from the workflow's inline python into config.py merge-operator-config (one tested codec); drop backends.json documentation-only schema_version/notes/platforms/patches and the stage ownership tag. Update methodology.md to the emitted shape.
…step The merge-operator-config step runs before the launcher cd's into $COLLX_SOURCE_ROOT, in a cwd where the relative experimental/CollectiveX path does not exist (the sweep source is staged under $COLLX_SOURCE_ROOT, not checked out into the workspace). The prior inline heredoc needed no file path; the extracted subcommand did. Reference config.py by its absolute staged path, matching the Extract step.
…ioning ladder The untimed warm-up now walks the measured token ladder ascending (8 rounds per shape, cold-jump-safe) and reuses those problems in Pass 1, instead of a distinct conditioning ladder. Removes conditioning_ladders from suites.yaml, the --conditioning-ladder argv (config.py both codecs), the WorkloadSpec conditioning fields, and retain_global (every shape retains its global trace). create_buffer sizes from the measured maximum (the old value folded in the ramp but never exceeded that max, so the JIT dir is stable). Docs + tests updated.
… h100 deepep-v2 EP8 Consolidate per-SKU platform identity, placement, operator-field requirements, canonical policy, and scale-out RDMA selectors into one tracked configs/platform_config.json. capability.py, runtime/config.py, the launchers, and prepare_backend.sh all derive from it; suites.yaml 'platforms: all' resolves to the registry. Removes the separate network-config.json (selectors move to the mi355x 'network' block) and the gitignored platforms.yaml indirection. Falsify the h100-dgxc deepep-v2 wall for EP8 (on-node ElasticBuffer + dispatch/ combine validated 2026-07-11): schedule EP8 (LSA), keep EP16 walled pending a GIN-over-RoCE 2-node test. MoRI kernel type is now derived by the adapter from (arch, scope) rather than an env var; block/warp tuples unchanged. Routing counter uses keyed BLAKE2b to match the documented oracle. Shared launcher prologue/tail + registry-driven topology validation.
Adding .shards to stage.py EXCLUDES (in 2424fb4) dropped the per-leg control JSON (COLLX_SHARD_FILE=.shards/<leg>.json) from the compute-visible staged tree. The cross-node preflight then failed 'test -r shard' -> exit 11 -> repository-stage failure on EVERY leg (all SKUs, both backends). Remove .shards from EXCLUDES; keep .venv/.pytest_cache (local cruft, absent on runners). Extend StageTests to assert .shards/<file> is staged.
…platform_config.json Operator decision (2026-07-12): move runner-local operator config (partition, account, qos, squash_dir, exclude_nodes) and scale-out RDMA selectors from the GHA secrets into the tracked platform_config.json 'operator'/'network' blocks, derived from live cluster state + a recovered manual config. An operator config document, when present, still overrides per field; path '-' (no document, known SKU) uses the registry baseline. collx_load_operator_config falls back to the registry-only sentinel when no operator file exists but the SKU is known. Complete baselines (run without secrets): h100, h200, b200, b300, mi355x. Still secret-fed: gb200 (no path), mi300x/mi325x (unreachable at derivation), gb300 (squash_dir/enroot_cache_path not recoverable). audit_salt stays secret.
The 'run without secrets' path requires this: deleting the base secret makes
COLLECTIVEX_OPERATOR_CONFIG_CONTENT empty, and json.loads('') crashed merge
before the registry-baseline fallback was reached, failing every leg at merge.
Treat an empty/blank base as {"runners":{}} so the merged file is still written
(launcher finds a config -> uses the tracked platform_config.json operator
baseline). A present base is still parsed strictly; overlays already skip empty.
Verified: complete-baseline SKUs emit full config; gb200/gb300 fail-closed with
validation-missing markers.
…implification
Add tracked operator baselines (discovered on-cluster via SSH) so these run
without secrets:
gb200 partition=batch, account=benchmark, storage_roots=[/mnt/lustre01/users-public/sa-shared]
gb300 + squash_dir=/data/home/sa-shared/collectivex/containers,
enroot_cache_path=/data/home/sa-shared/collectivex/enroot-cache
mi300x partition=compute, squash_dir=/home/bshan/collectivex/containers
mi325x stays secret-fed (no SSH access target; runners offline).
Runtime simplification (staged-tree cross-node preflight, canonical_policy, and
registry run_timeout/master_port removed; run_timeout/master_port now defaulted
in launchers/common.sh; shard control read from source, so .shards is excluded
from the staged copy). Docs/tests reconciled: README documents the operator
baseline + per-field secret overlay; skips_secret_fed_sku test points at mi325x.
…300); revert mi300x baseline
b300: the runtime refactor dropped the per-path 'git -c safe.directory' guard the
old collx_git wrapper applied, so on b300's UID-mapped Actions shell (NFS realizes
the staged source dir as UID 0) git aborts the DeepEP fetch with 'dubious
ownership'. Restore a job-scoped 'git config --global --add safe.directory *' in
collx_prepare_deepep_source (ephemeral per-job HOME; reaches the fmt submodule
child git; runner UID is inside the trusted cluster boundary).
mi300x: revert the operator baseline. The reconstructed squash_dir was wrong
(compute nodes have root-owned /home; only the submit host had /home/bshan), and
the cluster's node-boot blocker ('nodes still not ready') prevents probing a
compute node for the correct path. mi300x stays secret-fed/blocked like mi325x.
…anual-run fallbacks Every case now arrives from the matrix with fully-specified argv, so make --mode/--phase/--routing/--case-id/--suite/--workload-name/--gpus-per-node/ --scale-up-domain required and remove the getattr/'or ep_size'/'manual' fallbacks in run_ep/ep_harness/ep_deepep_v2/ep_mori/ep_backend (drop --timestamp). Simplify collx_run_shard/collx_launcher_cleanup control flow (equivalent). Verified: 28-case emit->parse seam all valid with non-zero placement; 32 tests.
…atform_config backends field
Each SKU now declares its runnable {backend: [ep_sizes]} in platform_config.json;
sweep_matrix derives topology from placement and resolves runnable/unsupported
from that map, replacing capability.py (deleted) and the separate backends.json
(deleted; DeepEP source pins + image tags now inline constants in common.sh).
Drop vendor/machine/operator_fields (unused after this); launchers self-declare
the operator fields they require via collx_require_vars; ep_mori derives arch
from platform_config directly.
Verified: 28 tests; py_compile/bash -n; all collx_* + config.py subcommands
resolve; matrix scheduling matches each SKU backends map (h100/b300/mi300x/mi325x
EP8-only, rest full); 28-case emit->parse argv seam valid; operator emit + launcher
require_vars guard cleanly. Docs (README/methodology) repointed off backends.json.
…/point
Fully de-secrete the runner config: drop the 7 COLLECTIVEX_*_CONFIG_CONTENT
secret env vars + COLLECTIVEX_OPERATOR_CONFIG_REQUIRED and the whole
merge-operator-config machinery (workflow step, config.py merge_operator_config +
OPERATOR_ENV, common.sh secret-content/ephemeral handling). collx_load_operator_config
now falls straight to the tracked platform_config.json baseline ("-" sentinel)
under canonical GHA instead of requiring a merged secret file; a baseline-less SKU
emits nothing and fails cleanly at the launcher's require_vars. Verified end-to-end
by simulating a real leg (h200 -> baseline emitted, no die; mi325x -> clean miss).
suites.yaml timing trials_per_point 128->256 (2048 obs/component, tighter p99);
README + methodology updated to match. NOTE: version left at 1 (operator's call).
prepare_backend.sh: cosmetic reformatting only.
Frontend (separate repo, already pushed) verified to ingest this artifact contract:
tsc clean, 46 tests pass, all required fields present.
b300 nodes have the gdrdrv module + nvidia_peermem loaded and a complete RoCE network block (bond0 + RoCE rdma_devices + gid 3), so NVSHMEM-IBGDA can register GPU memory via peermem; the prior '/dev/gdrdrv absent' exclusion may be stale. Un-wall b300 EP16 (backends [8,16], drop its CELL_EXCLUSIONS entry) so the sweep validates it on the metal. h100/mi300x/mi325x EP16 stay walled (no network block / infra-blocked); mi355x EP16 remains a real ionic MR-cap hardware wall.
…ll, BF16-faithful combine oracle Config/runtime: - Move container image pins out of common.sh into per-SKU image/image_platform in platform_config.json, plumbed via config.py PLATFORM_FIELDS and required by every launcher; registry-only SKUs (mi325x) still receive image config. - collx_apply_network_profile pins NCCL_IB_MERGE_NICS=0 on every scale-out (dual- port fusion disables NCCL GIN -> deep_ep EP16 asserts railedGinType==NONE) and NCCL_CROSS_NIC=0 when rail_isolated=1. Un-walls h100 + b300 deepep-v2 EP16 (validated 16/16 on-metal). h100 gains RoCE selectors + network block; b300 gains rail_isolated. - Bump container-import lock wait 900s->2700s with an explicit timeout message (a concurrent leg holds it ~18min for the b300 squash). - CELL_EXCLUSIONS: only mi300x/mi325x mori EP16 remain (deleted network block + cluster/access), with detailed reasons and a walled-vs-red doc. Correctness oracle: - Derive the expected combine from the values actually communicated: cast each destination rank's FP32 aggregate to BF16 before summing, exactly as the adapter does, so the gate models real BF16 accumulation instead of hiding it in a wide allclose. Tighten to max elementwise relative error < 8*2^-8 (mag floor 0.02); drop the loose rtol=0.05/atol=0.02 allclose and the global max_rel. - benchmark_stage drains dispatch with an untimed combine when dispatch_needs_combine_cleanup (MoRI), matching benchmark_dispatch. - Multi-point run summary (ladder ends + interior) instead of one mid-ladder row. - methodology.md + tests updated (27 pass).
…EP16 passes the tight gate The tightened combine oracle (2433548) cast each destination rank's aggregate to BF16 and summed in FP32, but omitted the per-scale-up-domain BF16 cast the backend applies to each node's partial before the scale-out send. Single-domain groups (every EP8 case + MNNVL EP16, where ep_size <= scale_up_domain) were unaffected, but multi-node RoCE EP16 (ep_size 16 > scale_up_domain 8, two domains) landed a deterministic 0.0478 off the expectation -- above the 8*2^-8=0.03125 gate -- reddening h100/h200/b200/b300 EP16 that are otherwise correct (run 29203408512: all four failed 'semantic correctness', identical max_rel, while gb200/gb300 EP16 over MNNVL passed at ~0.005). _expected_transformed_combine now reproduces the two-level reduction: per-rank BF16 output, FP32 accumulation within a scale-up domain, a BF16 cast of each domain aggregate for the scale-out send, then FP32 summation of the domain partials. Thread scale_up_domain through _run_expert_oracle. A numpy BF16 simulation confirms the multi-domain residual drops ~7x below the gate while single-domain cases become near-exact; the bound is unchanged. Docs updated.
… wall model, MoRI IntraNode-only on CDNA Config: replace configs/suites.yaml (YAML) with a flattened configs/sweep.json (single suite/workload), dropping the PyYAML dependency (requirements.txt gone) and the workflow's install step and 'suites' input. All readers are stdlib json. Prefill ladder moves to [1024,2048,4096,8192] (docs updated to match); version stays 1. Matrix: rewrite sweep_matrix.py (~400->223 lines). Walls are now purely the per-SKU 'backends' map -- a cell not in the map is 'unsupported' (never scheduled), a cell in the map schedules and is allowed to red. Removes the CELL_EXCLUSIONS table; test_matrix.py now asserts unsupported == exactly the (sku,backend,ep) absent from backends. config.py derives COLLX_* env names by convention (behavior-preserving) and splits OPERATOR_FIELDS/NETWORK_FIELDS. MoRI: drop the AsyncLL path; every CDNA scale-up cell uses the direct IntraNode kernel, scale-out EP16 uses InterNodeV1 (no low-latency-family kernel on either vendor). Size the communicator buffer to max(512, max_tokens_per_rank) instead of asserting <=512, so the larger prefill ladder runs. prepare_backend.sh: restructure into discovery/lifecycle/container-boundary helpers; same persisted rank-env contract (RANK_ENV_VARS), querying package roots via the venv interpreter, and persisting the deepep-v2 EP_SUPPRESS_NCCL_CHECK unset into the node env. 27 tests pass.
…tic heap The mi355x EP16 failure was misclassified as a physical ~1 GiB single-MR Pollara/Ionic wall. Falsified: the NICs advertise multi-GiB max_mr_size, direct uncached GPU-mem registration succeeds through 6-8 GiB, and Ionic multiplexes several firmware-command failures onto EINVAL (so the log proves only that the 6 GiB *static* registration failed during InterNodeV1 init, not an MR-size violation). MoRI already ships the supported path: VMM_HEAP backs the heap with on-demand 64 MiB DMA-BUF chunks (PR #155, validated on MI355X + AI NIC). Set MORI_SHMEM_MODE=VMM_HEAP in create_buffer, before shmem_torch_process_group_init, gated on self._inter_node — scale-out EP16 only, so scale-up EP8 (intranode, no RDMA registration) keeps the default static heap and its measurement basis is unchanged. The prefill ladder never enlarged the MR (MORI_SHMEM_HEAP_SIZE stays 6G, a virtual reservation under VMM). 27 tests pass.
…the registry
Addresses functionstackx's PR review: capture the NIC and switch per SKU so
same-GPU clusters on different fabrics stay distinct registry entries (e.g. a
second b200 cluster on a different NIC becomes its own entry with its own fabric
+ network block; the matrix already keys shards by SKU). Each platform now has a
fabric {nic, switch} block, sourced from the gpu-specs page and cross-checked
against the existing network selectors (h200 = Quantum-2 InfiniBand -> no RoCE
gid; b200/b300 = Ethernet -> gid 3; mi355x = Pollara). test_runtime asserts every
entry carries a non-empty nic and switch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CollectiveX v1 expert-parallel communication benchmark and its isolated GitHub Actions publication path.
Benchmark and publication contract
release_tag=unversioned; only an explicitv1tag with the locked full-matrix digest emits a release marker.Validation
f1ca85f9689922b90edd5767b9ff2a902f6b896f32f68b2ca086dde3fd2157d0.8e262178f770b0cdde12b7ec71604afd87251fa55685d4594f29717153ad6bbd.git diff --checkpass.中文说明
本 PR 完成 CollectiveX v1 专家并行通信基准测试及隔离式 GitHub Actions 发布链路。代码已准备执行三轮完整、无 canary 的 V1 运行;本 PR 目前不声称已有完成 promotion 的实测结果。
基准测试与发布约定
release_tag=unversioned;只有显式选择v1且匹配固定完整矩阵摘要时才生成 release marker。cxpublication-v1-*NDJSON。验证
f1ca85f9689922b90edd5767b9ff2a902f6b896f32f68b2ca086dde3fd2157d0。8e262178f770b0cdde12b7ec71604afd87251fa55685d4594f29717153ad6bbd。git diff --check通过。