WS2 Attention Cross-Configuration Integration¶
Implements PR4 of #235: wiring the CP attention path into the cross-configuration planner/runtime for the Qwen3-8B TP=2 CP=2 BF16 target.
Builds on #236 (attention contract and dispatch metadata), #238 (deterministic CP reference) and #230 (cross-configuration framework).
What "bind to the same contract" means here¶
The PR4 acceptance criteria say rollout and training descriptors must "bind to the
same semantic attention contract". Under the frozen deployment the two sides can
never produce identical AttentionContract instances:
| training (Megatron) | rollout (vLLM) | |
|---|---|---|
| mode | full-sequence prefill | chunked prefill, later decode |
| CP | context_parallel_size, whole forward |
prefill_context_parallel_size, prefill only |
| KV | no paging | paged KV with a block table |
| backend vocabulary | AttnBackend{flash,fused,unfused,local,auto} |
AttentionBackendEnum |
Read literally, the criterion is unsatisfiable. It is therefore implemented as three
tiers, in rl_engine/alignment/cross_config/attention_binding.py:
| tier | fields | rule | failure |
|---|---|---|---|
IDENTICAL |
checkpoint/model/token identity plus complete TP/CP GQA head and sequence ownership | equal bit for bit | comparable=False; no drift number from the pair means anything |
SEMANTIC |
reduction contract, dtype, exported LSE, first-class Split-KV request, complete actual Split-KV plan sets, CUDA QK-Norm/RoPE identity, cross-side determinism | both sides equal and equal to the WS2 mandate | fail closed |
RECORDED |
mode, backend_id, reduction.engine, RoPE materialization state and fusion boundary, KV-cache paging |
free to differ | recorded into provenance and measured |
Two placements are load-bearing:
reduction.engineisRECORDED, notSEMANTIC. Training may run the in-op deterministic reference while rollout runs a Transformer Engine merge oracle. Forcing them equal would defeat the oracle comparison that #235 PR2/PR3/PR5/PR6 depend on.- TP/CP topology is
IDENTICAL, notRECORDED. TP selects local Qwen3 GQA head ownership and CP selects local sequence ownership. Different topology is a different local attention problem, not a backend detail. reduction.order,reduction.acc_dtype, and actual Split-KV schedules areSEMANTIC. Both runtimes must export the complete batch x TP x CP x KV-owner plan set, including logical boundaries, merge order, FP32 accumulation, final downcast, and fallback state. Configured policy alone never passes strict binding.
comparable and passed are separate flags. A pair with mismatched identity is not
comparable. A pair that is comparable but violates the reduction mandate is still
rejected -- the drift would be real but attributable to the wrong thing.
H100 Attention input and projection boundary¶
Megatron/TE and vLLM/FlashInfer are the first-choice implementations.
H100AttentionPreprocessor runs a same-input H100 bitwise probe against the
deterministic RL-Kernel path. An unavailable native callable, a native exception,
or a failed probe switches both sides to RMSNormCudaOp + RoPESM90Op and
records the fallback reason and probe ID. The launcher passes the returned
backend evidence into AttentionRuntimeReadback:
from rl_engine.kernels.attention_preprocess import H100AttentionPreprocessor
prepared = H100AttentionPreprocessor(device)(
q, k, q_norm_weight, k_norm_weight, position_ids
)
readback = AttentionRuntimeReadback(
# contract, knobs, Split-KV plan set, source, and scope fields omitted here
**prepared.readback_fields(),
)
Strict binding rejects a missing or unknown backend and rejects mixed native / fallback execution. If both sides fall back, they must report the same deterministic backend IDs and policy ID. Printing a configured backend without executing the probe is not evidence.
The Attention boundary includes QKV projection, Q/K RMSNorm, RoPE, core
attention, KV-cache access, CP (Out, LSE) communication/merge, and o_proj.
AttentionProjectionOp freezes QKV/o_proj to BF16 input and output, FP32
accumulation, ascending-K reduction, and Split-K disabled. Native projection
callables are accepted only after a bitwise probe against DetGemmOp; otherwise
both sides use the deterministic fallback. Its collective contract records QKV
column-parallel plus backward TP all-reduce, o_proj row-parallel partial output,
and the SP all-gather/reduce-scatter directions. The model input RMSNorm and
residual add remain outside this Attention experiment.
Determinism is not one thing¶
rl_engine/alignment/cross_config/determinism.py probes both sides and compares
them, because the two frameworks mean different things by "deterministic":
Megatron deterministic_mode |
vLLM VLLM_BATCH_INVARIANT |
|
|---|---|---|
NCCL_ALGO |
asserts membership in a five-value set | hard-sets allreduce:tree |
NCCL_PROTO, channels, threads |
not managed | hard-set (Simple, 1, 1) |
| TF32 | not managed at all | disabled (fp32_precision="ieee") |
| BF16 reduced-precision reduction | not managed | disabled |
| cuBLAS workspace / BLAS library | not managed | :4096:8, cuBLASLt |
| GEMM | cuBLAS / TE | Triton matmul_persistent |
| FlashAttention | forbidden | permitted |
NCCL_ALGO, NCCL_PROTO and CUBLAS_WORKSPACE_CONFIG change arithmetic, so a
mismatch there is blocking. The remaining differences -- including the TF32 and
BF16-reduction asymmetry, which under a pure BF16 GEMM path does not fire -- are
recorded so the asymmetry appears in every artifact rather than being invisible.
Runtime adapters¶
Before this PR the only RuntimeMaterializer in the repository was
CpuSmokeMaterializer over a synthetic CPU model, and every named scenario
(S1/S2/S3) was planning-only. This PR adds the first two framework-shaped
adapters:
adapters/megatron.py--MegatronProvenanceAdapter(construction and distributed-context fingerprints, determinism probe, frozen-scope assertions) andMegatronAttentionMaterializer.adapters/vllm.py--VllmProvenanceAdapter(including diagnostic vLLM split limits) andVllmRolloutMaterializer.AttentionRuntimeReadback-- the explicit handoff from an executed engine. It carries the reconstructed actual contract, actual knob values, frozen-scope verification, executed CUDA QK-Norm/RoPE identities and fallback state, and the complete Split-KV runtime plan set.
Constructing a contract is not runtime verification. Without a readback, adapter
applications are UNOBSERVABLE; only matching values reconstructed from a real
Megatron or vLLM execution are APPLIED. bind_attention_runtime_readbacks is the
strict public entry point used after both framework launchers collect that evidence.
Neither module imports megatron or vllm; configs are duck-typed, so the binding
rules are exercised on CPU in CI rather than only on a 2-node cluster.
Fail closed, never substitute¶
unsupported_reduction_reason rejects requests that #236 cannot express, instead of
collapsing them onto the supported value:
| request | status | why |
|---|---|---|
attention.reduction_order=arrival |
UNSUPPORTED |
the control group must stay distinguishable from the treatment |
attention.reduction_downcast_at=per_block |
UNSUPPORTED |
DowncastPoint declares only final_write |
attention.reduction_engine=te_oracle |
UNSUPPORTED |
the TE merge oracle lands in #235 PR2/PR3; PR4's TE plan is provenance only |
attention.reduction_acc_dtype=bf16 |
UNSUPPORTED |
the CP (out, lse) merge accumulates in FP32 |
| configured contract without runtime readback | UNOBSERVABLE |
requested values do not prove what executed |
rollout.context_parallel_size>1 with effective decode CP=1 |
ERROR/FALLBACK |
strict TP=2/CP=2 acceptance rejects the topology change |
| missing/mismatched/fallback Split-KV plan set | binding failure | Split-KV provenance must cover every batch/TP/CP/owner coordinate |
| missing/unknown QK-Norm or RoPE backend, or mixed native/fallback sides | binding failure | both sides must execute the same verified native policy or the common RL-Kernel CUDA fallback |
Knobs¶
adapters/knobs.py extends V1_KNOBS additively. Added: training-side
tensor_parallel_size / context_parallel_size / deterministic_mode /
cp_comm_type, rollout.batch_invariant / rollout.kv_block_size, and the
reduction axis (acc_dtype, order, downcast_at, engine) plus
attention.fusion_boundary and attention.split_kv_policy.
training.attention_backend keeps its path but its value domain is replaced with
Megatron's AttnBackend; the HuggingFace names have no Megatron counterpart, so this
is a replacement rather than a mapping.
Not done here, because both change V1_KNOBS itself and would break existing
cross-config tests: removing training.sharding (Megatron has no such concept, and
DP=1 makes it moot) and renaming rollout.context_parallel_size to reflect that it
binds to prefill_context_parallel_size.
Scenario¶
examples/cross_config_qwen3_8b_megatron_tp2_cp2_vllm.json supersedes
cross_config_s1_distributed_smoke.json and
cross_config_s3_qwen3_8b_tp4_cp4_bf16.json, whose training sides used sdpa /
flash_attention_2 and sharding: fsdp -- none of which exist under Megatron -- and
whose TP=4/CP=4 topology does not match the target.
cross_config_s2_vllm_tp_vs_fsdp.json has no Megatron-only counterpart and should be
retired rather than rewritten.
Out of scope¶
Deliberately not in this PR:
- launching
torchrun, initializing process groups, or executing core attention; - pre-attention model RMSNorm and residual add;
- decode-mode materialization, which needs the validated
KVCacheSpecfrom #235 PR6 and is refused with that reference rather than stubbed; - distributed drift benchmarks and report artifacts (#235 PR5);
- fused production backend alignment (#235 PR7) and backward (#235 PR8).