Skip to main content

parallelism

Recommend a VALID ParallelismConfig and REJECT unsupported EP/CP/TP/ETP/PP combos BEFORE a GPU run. Auto-fire whenever the user: asks how to configure or pick a parallelism mode (expert/context/tensor/expert-tensor/pipeline parallelism, EP/CP/TP/ETP/PP, DDP/FSDP/HSDP); sets or asks about --expert_parallel_size / --context_parallel_size / --tensor_parallel_size / --expert_tensor_parallel_size / --pipeline_parallel_size / --pipeline_split / --pipeline_schedule / ep_scope / ep_size / cp_size / tp_size / expert_tp_size / pp_size / nvlink_domain_size; asks "which parallelism for this MoE / dense model / seq len / 8-GPU node / multi-node" or how to compute data_parallel_size; or hits a parallelism error/hang (DeepEP combine deadlock, NCCL hang, "must divide", "node-local", "not supported", ep4 "DeepEP dispatch groups" rejection, ParallelismConfig raise). Wrong advice wastes multi-GPU runs — this is the gate.

Source facts

Repository
whitecircle/halo
Last source activity
October 9, 2026 at 16:22
Detected SKILL.md language
English
Stars
823
Forks
39

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
parallelism
description
Recommend a VALID ParallelismConfig and REJECT unsupported EP/CP/TP/ETP/PP combos BEFORE a GPU run. Auto-fire whenever the user: asks how to configure or pick a parallelism mode (expert/context/tensor/expert-tensor/pipeline parallelism, EP/CP/TP/ETP/PP, DDP/FSDP/HSDP); sets or asks about --expert_parallel_size / --context_parallel_size / --tensor_parallel_size / --expert_tensor_parallel_size / --pipeline_parallel_size / --pipeline_split / --pipeline_schedule / ep_scope / ep_size / cp_size / tp_size / expert_tp_size / pp_size / nvlink_domain_size; asks "which parallelism for this MoE / dense model / seq len / 8-GPU node / multi-node" or how to compute data_parallel_size; or hits a parallelism error/hang (DeepEP combine deadlock, NCCL hang, "must divide", "node-local", "not supported", ep4 "DeepEP dispatch groups" rejection, ParallelismConfig raise). Wrong advice wastes multi-GPU runs — this is the gate.
allowed-tools
["Read","Grep","Glob"]
# Parallelism configuration gate Recommend a **valid** `ParallelismConfig` (`src/distributed/parallelism_config.py`) and **reject** unsupported combos before the user burns GPU hours. Be exact — the full reference matrix with per-row source citations is in **[matrix.md](matrix.md)**. Read it before giving a verdict on any non-trivial combo. ## PP is not available in this release The verdict on any `pipeline_parallel_size > 1` request is **not runnable — shard with EP/TP/CP**. The schedule engine is not shipped: `parallelism_config_from_args` (`src/training/parallelism_args.py`) rejects `pipeline_parallel_size > 1` at config time, before any rank math, and `PipelineRuntime` raises `NotImplementedError` on construction. A trainer without `_supports_pp` is refused one check earlier, by its own gate (below). What ships is the seam — the config surface, the rank math, the validators, the trainer gates, the stage/loss/checkpoint contracts — so every PP row below describes a validator or contract, never a launchable topology (`agent-docs/parallelism/pipeline-parallelism.md`). ## Source of truth - `src/distributed/parallelism_config.py` — the dataclass + `__post_init__` → `_validate()` sub-validators. Every config-time `raise` lives here or in `parallelism_config_from_args` (`src/training/parallelism_args.py`): `lowp_*` knobs or `init_from_scratch` outside SFT, a trainer's `_supports_cp` / `_supports_pp`, the PP release gate. - `CLAUDE.md` "## Parallelism" table + DeepEP / trainer-support notes. - User docs to cross-link: `agent-docs/parallelism/{expert,context,tensor,expert-tensor}-parallelism.md`, `agent-docs/parallelism/{data-parallelism,multi-node,data-loading}.md`. ## Key facts (from the code) - **The allowlist decides.** `SUPPORTED_AXIS_SETS` is checked first, before any rank math: plain DP, each axis alone (**EP, ETP, TP, CP, PP**), and **EP+TP, EP+CP, EP+ETP, PP+EP, PP+ETP**. Anything else is rejected with the mechanism from `AXIS_SET_MECHANISMS`, or a generic "no validated composition" when unlisted. The three PP entries are admitted by the allowlist but unreachable — the release gate above rejects `pp_size > 1` first. - `ep_group_size = ep_size * expert_tp_size` (the full EP process group). - `data_parallel_size = world_size // max(tp_size, cp_size, expert_tp_size)`; if that max is 1, `dp_size = world_size`. **EP alone never reduces DP** (EP is orthogonal to DP). Only TP, CP and ETP reduce distinct-batch count. - Locality unit is `nvlink_domain_size` (auto = `NVLINK_DOMAIN_SIZE`, else `gpus_per_node`; set it only on NVL72/MNNVL). "Node-local" below means "within one NVLink domain". - `ep_scope` defaults to `"auto"` in both the dataclass and on the CLI/YAML (`src/args/distributed_args.py`). `"auto"` resolves to `node` if `ep_group_size <= nvlink_domain_size`, else `global`. ## How to pick a mode 1. **Dense (non-MoE) model?** EP/ETP do nothing — use **TP** (memory/large hidden) or **CP** (long seq), else plain FSDP2 DDP (`dp_size = world_size`). 2. **MoE model?** Start with **EP** (orthogonal to DP, keeps full DP). For finer expert sharding add **ETP** (EP+ETP); EP+TP shards attention only, and TP+ETP is rejected. For long sequences on MoE use **EP+CP** (node-local EP only). 3. **Long sequence, OOM on activations?** add **CP** (Ulysses, node-local). CP reduces DP by `cp_size`. 4. **Weights/attention too big for one GPU?** **TP** (node-local DTensor). TP reduces DP by `tp_size`. 5. **Model too deep for one rank, across nodes?** **PP** is the axis for it — the outermost one, the only one meant to cross NVLink domains — but it is **not available in this release** (above). Shard the depth with **EP** (MoE) or **TP**, widen the job, or cut `max_length`; never advise a `pipeline_parallel_size > 1` run. 6. Compute `dp_size` with the formula above and sanity-check it's >= 1 and the global batch is what you intend. ## Hard node-local / divisibility constraints - TP, CP, ETP must be **NVLink-local**: each must divide `nvlink_domain_size` and be `<= nvlink_domain_size`. - `ep_group_size` must divide its scope and not exceed it: `nvlink_domain_size` for `node`; for `global`, `world_size`, which it must also tile as equal contiguous per-domain blocks. - EP+TP: `ep_size` must be a multiple of `tp_size`, so each EP group spans whole TP groups. On one NVLink domain that means `ep_size=2` (`ep2+tp2` on 8 = four 2-rank EP groups) or an EP group that fills the domain (`ep8+tp2` on 8); `ep4+tp2` on 8 is the racy topology and is rejected. Above one domain multi-group EP+TP is rejected — use one global EP group (`ep_size == world_size`, `ep_scope: global`). - `world_size % gpus_per_node == 0` and `world_size % nvlink_domain_size == 0`. ## REJECT THESE (verdict = do not run) - **TP + CP** (`tp_size>1 and cp_size>1`) — both partition the same contiguous rank blocks, and Ulysses redistributes heads TP has already split. - **TP + ETP**, with or without EP (`tp_size>1 and expert_tp_size>1`) — attention-TP and expert-TP are mutually exclusive; use EP+TP **or** pure/EP+ETP. - **ETP + CP** (`expert_tp_size>1 and cp_size>1`) — expert-TP partners sum their outputs element-wise in token space, while CP hands each rank a different sequence chunk. - **Multi-group >2-rank EP on one NVLink domain** — the predicate is `num_nvlink_domains == 1 and ep_size > 2 and nvlink_domain_size > ep_group_size` (`is_racy_single_domain_multigroup_ep`): DeepEP combine barriers race FSDP2's DP-wide NCCL. **Rejected at config time** (`_validate_single_domain_multigroup_ep`), hand-built configs included. Use `ep_size=2`, `ep_size = nvlink_domain_size` (the **domain**, not the node — they differ on NVL72), or `ep4 + etp2` — the supported 4-way expert split across 8 GPUs, since `ep_group_size = ep_size * expert_tp_size` fills the domain. `ep4 + tp2` lands on the same rejection: attention TP leaves `ep_group_size` untouched. The measured evidence and the two buffer failure modes live once, in **[matrix.md](matrix.md)** → row *Multi-group >2-rank EP on one NVLink domain*; every other skill links there instead of restating them. - **EP+CP with `ep_scope="global"`** (cross-NVLink-domain EP) — incompatible with CP. - **Node-local EP+CP where `ep_group_size != nvlink_domain_size`** — EP must be NVLink-orthogonal to CP. - **Any node-local size exceeding / not dividing the NVLink domain** (TP, CP, ETP, or `ep_group_size`) — "must divide" / "cannot exceed domain" raises. - **TP with indivisible attention heads** (`num_attention_heads % tp_size != 0`, or non-MLA GQA `num_key_value_heads % tp_size != 0`) — a split inside a head corrupts attention; `validate_against_model_config` raises from `config.json` before any weight is read. The same gate rejects `num_experts % ep_size != 0`, an `expert_tp_size` not dividing the expert FFN width, and a declared per-rank token budget past DeepEP's dispatch ceiling (matrix.md). - **HSDP with TP / ETP / EP** — `_validate_hsdp` rejects all three. HSDP wraps the standard DP path only: **pure DP or CP**. (EP already shards over the EP group.) - **Any PP** (`pp_size > 1`) — the trainer gate, then the release gate (above), refuse it before `ParallelismConfig` validates. The allowlist's PP rules and the PP-specific validators fire only on a directly constructed config; matrix.md lists them. - **`fsdp_shard_ep1_experts=False` with TP or CP**, and **`fsdp_reshard_after_forward=True` wherever an expert-distribution group exists** — the gate is `is_ep_mode` (`ep_group_size > 1`), so pure ETP (`ep_size=1`, `expert_tp_size>1`) is rejected alongside EP — **or with TP at `dp_size>1`**; **`fsdp_defer_grad_sync=True` with TP at `dp_size==1`** (no FSDP2 wrap to defer) **or with `fsdp_reshard_after_forward=True`** (the held unsharded gradient is the state ZeRO-3 shards — defer under ZeRO-2). `_validate_fsdp_settings` raises; QLoRA refuses every FSDP shaping knob at trainer construction. - **Expert LoRA with ETP** (`expert_tp_size > 1` + `expert_lora`) — `_validate_expert_tp` raises: the replicated adapter half would take a partial, never-synced gradient. - **LoRA/PEFT with TP** (`tp_size > 1`) — adapters are plain tensors outside the TP graph, so the replicated matrix diverges per rank and the sharded one is corrupted by the TP grad sync. `_validate_lora_tp_compatibility` (`src/trainers/mixins/validation.py`) raises at trainer construction, **native EP expert LoRA included** (its own message: the expert adapters live on the EP-distributed weights, which every TP gate skips by param identity). CP and pure ETP leave attention unsharded, so LoRA is fine there. - **Offline GRPO + CP + adapters** — offline CP is full fine-tuning only; PEFT and native expert LoRA are rejected. SFT and SMPO keep their CP adapter support. - **CP with a trainer that doesn't support it** (DPO, KTO, GRPO online/env/SDPG, reward, classification, distillation, embedding) — see matrix.md trainer table. - **PP with a trainer that doesn't support it** (online/env GRPO, SDPG, both distillation trainers, embedding) — `_supports_pp`. This gate runs first, ahead of the release gate, so these trainers report `<Trainer> does not support Pipeline Parallelism (PP)` rather than "not yet available". When rejecting, cite the exact reason and offer the nearest valid alternative (e.g. "ep_size=4 on 8 GPUs is rejected at config time → use ep_size=2, ep_size=8 (the whole domain), or ep4+etp2; EP+TP does not help — attention TP leaves `ep_group_size` at 4"). ## Sources of truth `matrix.md` + `agent-docs/parallelism/` document the supported/rejected combos. The code is the **ultimate** authority: `src/distributed/parallelism_config.py` (the validators) and `src/trainers/mixins/ep_introspection.py` (the runtime EP-GC setup guard) decide what actually raises or deadlocks — when a doc, this skill, or memory disagrees, or you are unsure, read those files before advising. (`CLAUDE.md`: docs-first, the code wins.) Related skill: `data` (sharded dataset ↔ `data_parallel_size`).
View on GitHub