Repository navigation
[WS1][CUDA][Qwen3-Next] EP4 distributed logprob parity: expert-parallel MoE, full-model L2 bitwise on TP4 x EP4 (RFC #428 stack 10/11) - #557
Conversation
…rouped expert GEMM RFC RL-Align#428 moe_route_combine_contract (CUDA). - Routing: FP32 router through the pinned vLLM batch-invariant GEMM, a fixed-order softmax, stable descending argsort (ties go to the lower expert id) and FP32 renormalisation of the ten selected weights. - Experts: per-route gate_up and down projections through vLLM 0.30.0's fused_moe Triton kernel with the batch-invariant tile passed explicitly (BLOCK 64/64/32, SPLIT_K=1); no routed-weight multiply and no sum inside the kernel, so every (token, route) row has one writer. Bitwise equal to the previous per-expert GEMM loop at TP4 and TP1 widths; fails closed if VLLM_TRITON_USE_TD is enabled. - Combine: ten ordered FP32 multiply-adds and one BF16 cast; no atomics. - Backward: per-expert pinned GEMMs into one dense gradient buffer; dx is summed in ascending expert order. - TP4 block: HF load/export for all 512 experts, replicated router and shared-expert gate, Megatron-style copy/reduce autograd boundaries moved into qwen3_next_tp.py. - Prior-art runner and plot comparing HF transformers, vLLM fused_moe (BI=0/1) and FlashInfer cutlass_fused_moe; TP4 real-weight gate script. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Prep for RFC RL-Align#428 full_model_chain; serves the gdn_state_bridge row. - GDN: packed recurrent prefill/decode through the pinned vLLM 0.30.0 provider with an explicit state boundary (qwen3_next_gdn.py), the depthwise causal convolution with cache handoff (qwen3_next_conv.py), their combined shared core (qwen3_next_provider.py) and the TP4 block with HF GQA-interleaved weight mapping (qwen3_next_tp_gdn.py), now on the shared TP boundaries in qwen3_next_tp.py. - Attention: the deterministic CUDA attention kernel accepts head dim 256 (ROCm stays 128) and validates backward inputs; shared_attention and TP4FullAttention with the KV head replicated on each rank pair, partial RoPE at absolute positions and explicit per-slot KV state. - Real-weight TP4 gates for GDN (layer 0) and attention (layer 3), sharing scripts/qwen3_next_gate_common.py with the MoE gate. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…nce (B200, 2e950f3) Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Compares RL-Kernel's deterministic attention with torch SDPA, vLLM FlashAttention-2 (num_splits auto and 1), vLLM's Triton unified attention and FlashInfer's ragged prefill: batch, chunked-prefill and decode invariance, backward repeatability, accuracy against FP64 and latency. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…nce (B200, 9e6c4f2/6517077) Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
RFC RL-Align#428 full_model_chain (CUDA). - Engine adapters: the native vLLM rollout (exact-prefill scheduler, paged KV, traced response rows), the VIME/Megatron replay and training actor, engine weight import/export and the logprob adapter, all on the shared TP4 providers. - Stage runners (preflight, layers, l2, train1, train2, train) with the validated acceptance contract (version 4) and scripts/qwen3_next_run.py, a launcher that works inside any four-GPU allocation: each stage gets a clean clone, an immutable run.json identified by a fresh execution_id, and refuses a predecessor that did not pass or pins another identity. Scheduler-independent helpers live in scripts/qwen3_next_runtime.py. - Sub-node digests: both engines observe the normed mixer input, the mixer output, the normed MLP input, the selected expert ids and the MoE output of every layer, plus the embedding; the first divergence names the layer and sub-node. - scripts/qwen3_next_chain_report.py writes a ChainGateReport-compatible report (schema qwen3-next-chain-v1, compatible with ws1-c10-c11-v5) with first_drift_family/layer/cell; scripts/qwen3_next_publish_evidence.py publishes a chain's records without site paths, hosts or users. - Native rollout caps raised to 64 sequences, 16384 batched tokens and a 4 GiB KV budget so the batch-invariance matrix runs under the same certified profile; the exact-prefill scheduler still fixes every chunk. - The operator-case registry is regenerated from collection by scripts/qwen3_next_operator_cases.py. - Packaging: transformers may be 5.13.1-5.17.0; a qwen3-next extra pins the audited runtime. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
RFC RL-Align#428 batch_invariance_ci (CUDA). - Stage bi (after a passed l2, same certified profile): 97 pinned cells in rl_engine/testing/qwen3_next_bi_cases.py / qwen3_next_bi_manifest.json. concurrency (1-64 requests, target first or last), interference (companion content, lengths, arrival during prefill/decode, generation lengths), chunk boundaries (prompts 63-1024 around 64/128/528/1024, chunk 64/128, first chunk 1/63/65, compared with the replay recompute) and replay THD packing (1-7 neighbours, target first or last). A quick CI subset runs one cell per family plus 64 concurrent requests. - Native rollout: version 2 requests run one traced target next to untraced companions; the exact-prefill scheduler admits each request at its arrival step, pins the target's planned chunk at every step and fails on any preemption; the trace records only the target. - Replay: packed_layout() packs variable-length neighbours with the traced target in one THD batch. - bi-matrix.json names each failing cell's first divergent layer, sub-node and layer family; the chain report gains a batch_invariance section, and the evidence publisher takes the bi run with --also. - gtest: bitwise judgments compare raw bits (signed zero, NaN/Inf and dtype now count), not allclose with zero tolerance. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…lout profile Batch-invariance chunk cells use prompts such as 129 tokens; the state-shape check only admitted the four L2 lengths, which their own validators already pin. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Editable sources, base interpreters and build directories in package metadata now keep only their last path component; cgroup paths keep only their file name. The leftover check matches absolute paths only, so package paths such as vllm/.../cpu/data/ are not flagged, and any mention of the batch scheduler blocks publication. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…r-art runner - Megatron-core 0.16 MoELayer with Transformer Engine grouped GEMM and fused permute, configured as VIME runs Qwen3-Next (FP32 softmax router, top-10, all-to-all dispatcher), including its backward. - SGLang 0.5.21 Triton fused_moe in default and deterministic-inference mode. sgl_kernel cannot load next to torch 2.13, so its two kernels on this path are replaced by SGLang's own Triton moe_sum_reduce and JIT moe_align_block_size; any other sgl_kernel call fails loudly. - --only and --merge combine reports from different Python environments. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…ured (B200, c58816b) Every library in the reuse table is now measured: Megatron-core 0.16 + TE 2.16 as VIME configures Qwen3-Next, and SGLang 0.5.21 in default and deterministic-inference mode. The TP4 gate evidence is unchanged (2e950f3, identical MoE code). Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…gatron-core attention Add three engines to the attention prior-art runner: - FlashAttention-4 (CuTe DSL) varlen as vendored by vLLM, forward and backward. - Transformer Engine DotProductAttention on THD-packed sequences, in inference mode (cuDNN fused) and in training mode (on SM100 TE has no fused head-dim-256 backward and falls back to its unfused path). - Megatron-core's local DotProductAttention, one launch per sequence. The backward check now compares the target's dq/dk/dv alone against the same sequence packed first and last, so engines with a packed backward are checked for batch invariance rather than repeatability. A prefill/decode crash is recorded per candidate without skipping its other checks. --only and --merge combine reports from different Python environments. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
The decode and forward+backward latency sections are keyed by candidate directly, the prefill section by size first; merge them at any depth. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…p as E402-exempt The gate scripts insert the repo root into sys.path before importing rl_engine and tools, which flake8 reports as E402. Mark those imports with noqa, as the other tools/ scripts do. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…p as E402-exempt The gate scripts insert the repo root into sys.path before importing rl_engine and tools, which flake8 reports as E402. Mark those imports with noqa, as the other tools/ scripts do. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Annotate the latency samples and accept the default-argument lambdas used as timed calls. No behaviour change. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
… runner Annotate the latency samples and accept the default-argument lambdas used as timed calls. No behaviour change. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Merge the migrated TP4 mixers (and through them the refactored test-qwennext) and move the full-model chain into the new layout: - model-level TP4 pieces (embedding, engine weights, logprob, sub-node observations) into rl_engine/models/qwen3_next/; - vLLM adapters and the native rollout into rl_engine/integrations/engines/rollout/vllm/; - VIME providers into rl_engine/integrations/orchestrators/vime/providers/ and the native VIME lifecycle (recompute, training, workers, profile) into rl_engine/integrations/orchestrators/vime/experiments/qwen3_next/; - the provider identity into rl_engine/integrations/common/; - chain report, L2/case fixtures and manifests into rl_engine/validation/models/; - stage runners and the launcher into tools/validation/models/, CI scripts into ci/scripts/, tests into tests/models/qwen3_next/. Imports point at canonical modules; the provider-source digest list names the canonical RMSNorm backend instead of its compatibility alias. Recorded contract identities (e.g. sample_counter_owner) are unchanged. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…nce matrix Merge the migrated full-model chain and move the batch-invariance work into the new layout: the case generator and manifest into rl_engine/validation/models/, the stage runner into tools/validation/models/, its tests into tests/models/qwen3_next/. The raw-bit comparison for bitwise judgments now lives in the canonical rl_engine/validation/operators/forward_invariance.py (the old module is a compatibility alias). The evidence README gives the commands in the current layout and says where the runners were at the measured commit; no measured file changed (SHA256SUMS verified). Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…ouched The published chain evidence is a byte-exact copy covered by SHA256SUMS; a pytest JUnit XML without a final newline would otherwise be rewritten by end-of-file-fixer and break the checksum. Same treatment as reports/archive/. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…ouched Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Same point as 543-S5; not changed. |
Not changed: |
The chain preflight, layers and l2 records were produced with local changes that are not part of the pull request and cannot be checked from any public commit. The RL-Align#557 description no longer reports them. The gates and ep_matrix records, which the description links, are unchanged. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…-Align#543/RL-Align#544, with the two candidate RL-Align#543 patches Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
a203719 parametrised test_cuda_gated_parameter_vjp_contributions_match_the_fold over shape and added four norm tests, but the registry still pinned the old ids, so the preflight operator gate reported them as not found. Regenerated with qwen3_next_operator_cases.py --write. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…ecompute create_actor(training=False) built the full actor through setup_model_and_optimizer, which wraps the model in DDP and allocates optimizer state. The recompute/logprob path never steps, and on 80 GB GPUs that state does not fit. With training=False the model is now built through get_model(wrap_with_ddp=False) and loaded without an optimizer, and (args, model, None, None) is returned. The training=True path is unchanged. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
VIME's load_checkpoint requires skip_load_to_model_and_opt; both create_actor calls omitted it, so the full-model l2 recompute failed with a TypeError before any model work. The stub in the actor test now requires it. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…RL-Align#495 and RL-Align#491 at their current heads Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…t.toml, gate the remaining new tests Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…k, not assert Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
…issing TP group run_qwen3_next_chain_gate.sh read the report with a bare open() and indexed JSON-only keys, so a truncated file, a non-object report or one without first_drift_family raised a traceback and exited 1, the code reserved for a failed gate. Read it in a with-block, require an object and the payload-only first_drift_family, and send every such failure through invalid() (exit 2). The TP reduce dereferenced collective_for_group(...), which returns None for a missing group; raise a clear RuntimeError as the EP exchange does. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
|
Replies to the three OpenCodeReview summary items:
|
Provider job, chain preflight (failed at rollout-smoke), logprob prior art over 13 NCCL settings, EP matrix at 2c699e3, RL-Align#557 gates at f935bf3, RL-Align#549 parity at 30c0e5a, RL-Align#543/RL-Align#544 preflight at their own heads. Redacted. Signed-off-by: Wenbo Ji <36562829+fusheng-ji@users.noreply.github.com>
Flink-ddd
left a comment
There was a problem hiding this comment.
The layout follows #497, but Hopper validation is still incomplete. Please fix the known batch-dependent combine_routes backward reduction, cover the router and combine path in the EP4 gradient tests, and run full EP4 rollout and replay parity on four H100 and H200 GPUs using the submitted code before merging.
What
rl_engine/models/qwen3_next/qwen3_next_ep.py): expert parallel 4 over the TP4 group, expert tensor parallel 1. Rank r owns experts[128r, 128r + 128)at full width; attention, GDN, embedding, LM head and the shared expert stay TP4.all_gather, which is a byte copy with no arithmetic.combine_routes(FP32, route order) locally.enable_expert_parallelwith linear placement;SharedVLLMMoEuses the EP path and checks that vLLM'sexpert_mapis exactly[128r, 128r + 128).expert_model_parallel_size4,expert_tensor_parallel_size1.SharedVIMEEPMoEasserts that the expert-parallel rank equals the TP rank, and the HF loader returns only local experts.qwen3_next_run.py --ep {1,4}records the topology inrun.json, and every later stage checks it. Thelayersstage runs the new EP4 MoE gate (moe_ep) in place of the TP4 MoE gate.docs/operators/qwen3-next-distributed-logprob.md): new EP section, one row per cross-rank point.VllmConfigfrom vLLM's own config classes (tests/models/qwen3_next/vllm_real.py), andcheck_qwen3_next_vllm_real_model.pybuilds RL-Kernel's wrapper around vLLM's actualQwen3NextForCausalLMon four real ranks at EP1 and EP4 (weights allocated, not loaded): drift rejected before any model memory is allocated; 48 layers hold the shared classes and shapes; the routed-expert parameters keep vLLM's native weight loaders; at EP4 vLLM's ownexpert_mapequals[128r, 128r + 128)and every expert is held once. Both run inpreflight's device guards, which previously ran only the multi-GPU guard.tools/validation/models/qwen3_next/, the comparison benchmark in [WS1][CUDA][Qwen3-Next] Comparison benchmarks and plots for the Qwen3-Next stack: MoE, attention, logprob, EP (RFC #428 stack 11/11) #551, raw evidence as a release ZIP.Prior art & reuse decision
Measured on the same layer-0 weights and replicated input, EP4 under 13 NCCL settings and EP1, 4 × B200, 1024 tokens (summary, runner in #551, measured at
45719cb3):qwen3_next_ep(this PR, both engines)fused_experts+expert_map+ vLLM's all-reduce (default orVLLM_BATCH_INVARIANT=1)all_reducecutlass_fused_moewithep_size/ep_rank+ vLLM's all-reduceFusedMoE, EP ranks combined bytensor_model_parallel_all_reduce(fused_moe_triton/layer.py:1597)sgl_kerneldoes not load next to torch 2.13, and the only machine available for the follow-up check has 2 B200 (EP4 needs 4); same structure as vLLMMoELayer, alltoall / allgather dispatchertorch.use_deterministic_algorithms(True), alltoallradixark/miles@23d3d38)miles/kernels/moe/fused_experts{,_bwd}.py(SGLang forward, own backward), imported unmodified; layer-0 weights at EP1, 20 launchesdx,dWgate/up,dWdown equal the first in 1 / 20 launches (BF16tl.atomic_add);dxprobe rows 0 / 10 batch cellsvolcengine/verl@e5a627c)verl/); Megatron-core, HF/FSDP or VeOmni for training, vLLM/SGLang for rolloutBatch invariance: 8 probe tokens alone vs inside batches of 8 / 64 / 256 / 1024 at the first, middle and last position, on the output and (backward) the input gradient.
Decision: write our own EP4 path, reusing the #503 MoE contract and RL-Kernel's fixed-order collective.
tl.atomic_add, and over 20 identical launches on the real weightsdx,dWgate/up anddWdown equal the first in only 1 of 20 (measured, EP1 as Miles has no EP kernel path); at EP1 it is 15× faster than our EP1 path (10 vs 158 ms forward + backward, 1024 tokens); RL-Kernel's EP4 forward + backward is about 53 ms (2.3 + 51).Results (4 × B200, official checkpoint, TP4 × EP4)
EP4 evidence summary and raw results (ZIP) (SHA-256
c7530d29…ba53). Measured at1c4ae265from clean clones in the same environment as the TP4 × EP1 chain.preflightlayersl2Chain-gate report:
config_fingerprint.ep4, 16 cells,passed: true,first_drift: null.EP4 MoE gate, every rank, bitwise:
hf-load-export-each-expert-onceep4-vs-ep1-chunk-reorder-train-{8,64,256,1024}empty-rank-participationEP4 is not TP4 × EP1.
qwen3-next-moe-ep4-v1), and parity means rollout and replay both run it.Status
88c4f02e, on top of [WS1][CUDA][Qwen3-Next] TP distributed logprob parity: fixed reduction order, selected-token logprob exact across NCCL settings (RFC #428 stack 9/11) #549 (3f7cf6ec). CI is green on this head: lint, unit-tests, docs, DCO and the bot review pass;providerandws1-gtestskip because no self-hosted runner is attached.train*stages). The training actor and export fail closed on EP4; follow-up PR.preflightandgpu_probeaccept exactly four B200, H100 or H200 GPUs (a705d287; H200 accepted, not tested). On 4 × H100, ata705d287(not rerun at the head88c4f02e), the Qwen3-Nexttest_*.pyandcheck_*.pysuites (814 passed) and the six gates passed. The full-model EP4 chain has not been run on H100 with this PR's code yet. See "Results (4 × H100; no full-model run)" at the end of this description.90c09164, 4 × B200): preflight, layers (withmoe_ep) and l2 passed, 16/16 raw-bit, and the 32 L2 artifacts are bitwise identical to the1c4ae265run; the standalone EP4 MoE gate passed on all four ranks. The full batch-invariance suite then ran at EP4: 97 / 97 cells bitwise (same cell definitions as at EP1), raw results ZIP, SHA-256b3012ab6…fe4d. EP4 training stays fail-closed, so no EP4 training stage is claimed. At17ba4a4cthe EP4preflightpassed again with the new device guards (27 tests, 0 skipped, 0 failed).History
a705d287and the comparison at58aa50c7. The raw files record2a8f18e4and77d59a59, the ids these commits had before the branches were rewritten; each pair has the same tree. An earlier version also reported a full-model EP4 chain run on H100 that was made with local changes that are not part of this PR; that result is withdrawn, its raw files are removed from thepr_assetsbranch (927b93d6), and the chain is listed as not run on H100. The FlashInfer CUTLASS row of the H100 comparison, which showed [WS1][CUDA][Qwen3-Next] MoE route/combine contract with a batch-invariant grouped expert GEMM (RFC #428 stack 5/11) #503's single-GPU (non-EP) numbers instead of an EP4 measurement, is now marked as not measured. The RL-Kernel backward batch-invariance cell has footnote ¹: the stockcombine_routesbackward was not batch invariant from batch 16 upward in [WS1][CUDA][Qwen3-Next] MoE route/combine contract with a batch-invariant grouped expert GEMM (RFC #428 stack 5/11) #503's H100 diagnostics.pull_requestpaths were missing Qwen3-Next modules that its checks import; merged the additions of [WS1][CUDA][Qwen3-Next] Full-model chain: vLLM rollout vs VIME recompute, raw-bit through two updates and a reload (RFC #428 stack 7/11) #543 and [WS1][CUDA][Qwen3-Next] Batch-invariance matrix for the full model, 97/97 cells bitwise (RFC #428 stack 8/11) #544; this PR's own logprob-kernel and rollout-bridge entries are kept once (88c4f02e). Workflow-only change; shared infrastructure such asrl_engine/opsandrl_engine/platformsstays out of the filter.6dc9d410triggers the provider job oncsrc/cuda/logprob/**and the vLLM rollout bridge. The fixes of the PRs below it are merged in; new head65eaae4d. Kernel arithmetic is unchanged, so the measured results stand.test-qwennextafter [WS1][CUDA][Qwen3-Next] C1: zero-centred RMSNorm references and CUDA kernel (RFC #428 stack 2/4) #467 landed (c382dd4b), through [WS1][CUDA][Qwen3-Next] TP distributed logprob parity: fixed reduction order, selected-token logprob exact across NCCL settings (RFC #428 stack 9/11) #549's new headfe6ed386, in85297004. One conflict, in the provider workflow: the pinned-provider step takes [WS1][CUDA][Qwen3-Next] C6: GDN decode-step goldens and recurrent/conv state contract (RFC #428 stack 4/4) #469's fail-closed skip check, and this PR's four-GPU vLLM model step follows it unchanged.Tests
python -m pytest -q tests/models/qwen3_next/test_qwen3_next_ep.py tests/models/qwen3_next/test_qwen3_next_chain_report.py TRITON_F32_DEFAULT=ieee torchrun --nproc-per-node 4 tools/validation/models/qwen3_next/qwen3_next_ep_moe_check.py \ --checkpoint <ckpt> --output <dir> python tools/validation/models/qwen3_next/qwen3_next_run.py --stage all --until l2 --ep 4 \ --runs-root <runs> --checkpoint <ckpt> --cuda-home <cuda-13.0> --python <venv>/bin/pythonci.yml(includes the newtest_qwen3_next_ep.py) + digest testspreflightdevice guards at EP4 (multi-GPU + contract + real model)mkdocs build --strictScope
CUDA, TP4 × EP4 × ETP1 × CP1, one node of four B200, rollout and replay up to L2. EP training, other EP sizes, ROCm and multi-node are out of scope.
Results (4 × H100; no full-model run)
4 × NVIDIA H100 (driver 580.173.02 in the gates run), torch 2.13.0+cu130, vLLM 0.30.0, NCCL 2.29.7. The comparison run does not record the Megatron-core version.
Tests and gates, at
a705d287on this PR's branch (not rerun at the head88c4f02e).gates/info.txtand the EP4 MoE, TP4 attention, TP4 GDN and TP4 MoE gate files record2a8f18e4, the id of this commit before the branch was rewritten. Both ids have the tree4102963e2673c15b8a5e807f7ea4223173630370;git rev-parse a705d287^{tree}gives it. The GDN engine adapter and TP4 embedding / LM head gate files record no commit.gates/pytest.logis a quiet-mode log (progress marks and the final line) and does not record the command line.tests/models/qwen3_next/test_*.pyandcheck_*.pygates/pytest.log,gates/info.txtadapter,ep_moe,tp_attention,tp_gdn,tp_head,tp_moe(each gate also hasrank-1.jsontorank-3.jsonand a.login the same folder)The EP4 MoE gate (script at
a705d287; its rank files record the pass status and output hashes, not the individual comparisons) checks bit for bit, at 8, 64, 256 and 1024 tokens, that the EP4 routed-expert rows (beforecombine_routes), their input gradientdxand each rank's own experts'dWequal the same experts evaluated on one GPU (EP1) on the same routes. For the EP4 block alone it checks that the full batch equals the concatenated chunks, that reordered tokens give the reordered output, that the first row alone matches, and that the training forward equals the no-grad forward. It does not compare the combined block output with EP1, and it does not differentiate throughcombine_routesor the router.Not run on H100 yet: the full-model EP4 chain (
preflight,layers,l2: vLLM rollout against VIME recompute) with this PR's code.Comparison with existing implementations (same runner and columns as "Prior art & reuse decision" above). Layer-0 MoE with the checkpoint's layer-0 weights on one fixed random 1024-token input: EP4 on four GPUs under each of the 13 NCCL settings of #549, and EP1 on one GPU. Measured at
58aa50c7, which is on the #551 branch (that branch contains this PR's head88c4f02e), with #551's EP benchmark runner (moe_ep_candidates.py,qwen3_next_moe_ep_prior_art.py). The report records77d59a59, the id of this commit before the branch was rewritten. Both ids have the tree67a6c5c8a30c1351b05beb9b9f509976f8dff45c;git rev-parse 58aa50c7^{tree}gives it. The EP4 == EP1 and backwarddx/dWequalities of RL-Kernel appear as flags inep_matrix/report.jsonundercandidates.rl_kernel_ep(ep4_equals_ep1,backward.dx_ep4_equals_ep1,backward.dw_ep4_equals_ep1).In the "Fwd NCCL-setting invariant" column, the number in parentheses is the count of distinct EP4 outputs across the 13 NCCL settings.
qwen3_next_epfused_experts+ vLLM all-reducefused_experts+ vLLM all-reduce,VLLM_BATCH_INVARIANT=1(identical outputs)fused_experts+ NCCLall_reduceMoELayer, alltoallMoELayer, allgatherMoELayer, alltoall,torch.use_deterministic_algorithms(True)MoELayer, allgather,torch.use_deterministic_algorithms(True)¹ Measured on one fixed random input with the stock
combine_routes, which is unchanged from58aa50c7to the head. Its autograd backward reduces the route-weight gradient over the hidden dimension withtorch.sum. In #503's H100 diagnostics (results.txt), on synthetic inputs, that reduction was not batch invariant from batch 16 upward and madedxdiffer. This cell therefore holds for the measured input only.² The JIT build of FlashInfer 0.6.18.post1's
fused_moe_90module failed at link time on the H100 machine (/usr/bin/ld: cannot find -lnvrtc,Ninja build failed; seecandidates.flashinfer_cutlass_ep.unavailableinep_matrix/report.json). #503's H100 run linked it through alibnvrtc.sosymlink, which this run did not have. The candidate is therefore recorded as unavailable, and this table has no FlashInfer numbers.Batch invariance: the probe of 8 tokens is compared alone and inside batches of 8, 64, 256 and 1024 tokens, at the first, middle and last position (cell
1024@508is the 1024-token batch with the probe at position 508), on the output and, for the backward, on the input gradient. With vLLM's all-reduce (default andVLLM_BATCH_INVARIANT=1), vLLM differs only in the 1024-token batch (cells1024@0,1024@508and1024@1016); with NCCLall_reduceit differs in every cell except8@0.Latency: the benchmark runner takes one warm-up call, then the median of 20 timed forward calls at 1024 tokens and, for the second number, the median of 5 timed forward + backward calls. The EP4 value in the table is the largest of the four ranks' values (
latency_us_ep4in the report), in the run with default NCCL settings. The published run files hold rank 0 only. Report:ep_matrix/report.json; run files:ep_matrix/runs/.