Skip to content

feat(logprob): add deterministic ROCm vocab-parallel path - #328

Open
hihaluemen wants to merge 58 commits into
RL-Align:testfrom
hihaluemen:work/ws2-logprob-rocm
Open

feat(logprob): add deterministic ROCm vocab-parallel path#328
hihaluemen wants to merge 58 commits into
RL-Align:testfrom
hihaluemen:work/ws2-logprob-rocm

Conversation

@hihaluemen

@hihaluemen hihaluemen commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Add deterministic ROCm backend for WS2 vocab-parallel logprob path (#241)

This adds a ROCm HIP path for the WS2 vocab-parallel logprob implementation. The native part is deliberately small: it only handles the rank-local vocab-tile statistics. The existing TP merge order and logprob/entropy contract are left alone, which keeps the training and inference paths on the same definition.

Changes

  • Add deterministic_logp_tile_stats for FP32 max/sumexp partials. It uses a fixed block reduction and no atomicAdd, and the same source is used for CUDA and HIP builds.
  • Keep the existing WS2 all-gather and global tile-order merge. CP still sits outside the numerical merge axis.
  • Leave selected-token logprob, entropy, masking, and backward on the shared Python path. There is no need to duplicate that logic in the ROCm kernel.
  • Add the rocm-vocab-parallel-logp-ws2 backend. It is registered only when the ROCm extension and symbol load successfully. An explicit request for the native backend fails immediately if the extension is missing; it will not silently turn into the PyTorch reference path.
  • Gate CUDA-only IPC/PTX registrations so they are not pulled into a ROCm build.

Benchmark results

Operator-only benchmark — seeded logits, no checkpoint, tokenizer, or serving engine.
Qwen3 vocabulary V=151936 split into 64 tiles of 2374 columns. Tables show 2048 tokens, BF16.

Platform Hardware Runtime
mi300x AMD Instinct MI300X ×8 torch 2.12.0+rocm7.14.0a20260608, HIP 7.14.60850
h100 NVIDIA H100 80GB HBM3 ×8 torch 2.13.0+cu130, CUDA 13.0
cpu x86_64, 192 cores torch 2.13.0+cu130

Single device (BF16, 2048 tokens)

Platform Backend Fwd median (ms) Fwd p95 (ms) Fwd peak MiB Fwd+bwd median (ms) Fwd+bwd peak MiB Speedup vs native (fwd / fwd+bwd)
mi300x native 6.1387 6.3072 1245.0 12.3477 7715.7 1.00× / 1.00×
mi300x triton 0.9514 0.9801 2.0 2.1619 1187.1 6.45× / 5.71×
mi300x hip 0.8923 0.9162 2.0 2.0359 1187.1 6.88× / 6.07×
h100 native 7.0518 7.0676 1246.0 16.6661 7722.7 1.00× / 1.00×
h100 triton 0.7990 0.8314 2.0 2.0525 1189.1 8.83× / 8.12×
h100 cuda 0.7388 0.7532 2.0 1.9692 1188.1 9.55× / 8.46×
cpu native 421.5963 430.5902 1277.0 1442.7101 7714.4 1.00× / 1.00×

Distributed (BF16, 2048 tokens, forward / forward+backward median ms)

Topology mi300x native mi300x triton mi300x hip h100 native h100 triton h100 cuda
tp2 3.5877 / 7.1482 0.8894 / 1.7667 0.8487 / 1.5938 3.9802 / 9.2129 0.9375 / 1.8345 0.8378 / 1.6801
tp4 2.2691 / 4.4688 0.9221 / 1.7425 0.8714 / 1.5001 2.5194 / 6.2461 0.9734 / 2.2329 0.8695 / 1.8626
tp8 1.8066 / 3.3579 1.1149 / 1.8465 1.0631 / 1.6417 1.8771 / 3.9526 1.0773 / 1.8571 0.9952 / 1.6378
tp2_cp2 3.3882 / 5.6092 0.8308 / 1.4580 0.7809 / 1.3925 3.5072 / 6.5482 0.8173 / 1.5814 0.7557 / 1.4018
tp4_cp2 2.2382 / 3.7864 0.8831 / 1.6803 0.8386 / 1.4029 2.3596 / 4.4815 0.8932 / 1.6965 0.8499 / 1.4813
tp2_cp4 3.5261 / 4.9700 0.8240 / 1.5611 0.7547 / 1.3959 3.5499 / 5.6437 0.8201 / 1.6327 0.7561 / 1.4039

Single-GPU latency

Single-GPU peak memory

Distributed latency

Full report ·
Raw JSON

ryankert01 and others added 30 commits August 2, 2026 22:51
Implements PR 1 of issue RL-Align#241: a typed contract for vocab-parallel
selected-token logprob, mirroring the WS2 attention contract pattern.

- rl_engine/kernels/logprob_contract.py: LogprobContract, ShardingSpec
  (per-rank vocab shard bounds, padded-vs-real vocab, TP/CP rank
  metadata, owner_rank resolution), MaskSpec (active-token mask,
  ignore_index), ReductionSpec (fp32 (max, sumexp) merge in fixed
  global vocab-shard index order, all-gather transport, CP declared a
  non-merge axis), and LogprobBackendCapability.
- KernelRegistry.get_logprob_op(contract): contract-aware dispatch that
  only selects backends with a declared capability; incompatible or
  undeclared candidates are rejected with explicit reasons and never
  used as a silent fallback. Existing WS1 batch-invariant logp backends
  are declared truthfully as single-shard references, so strict WS2
  requests fail loudly until the deterministic vocab-parallel TP
  reference (PR 3) lands. Legacy get_op() behavior is unchanged.
- Design doc, runtime-dispatch and operator doc updates, and CPU-safe
  contract/dispatch tests covering the Qwen3-8B TP=2 BF16 target and
  the TP=1/2/4 sweep shapes. Tolerance values remain owned by RL-Align#108.
- docs: correct the TP-invariance claim — fixed merge order gives
  determinism per TP degree; cross-degree bitwise equality additionally
  requires a TP-degree-independent local tile decomposition (PR 3
  obligation), otherwise RL-Align#108 tolerances apply
- contract: store backend_id stripped so id-based dispatch matches;
  summarize the active mask in to_dict() provenance instead of copying
  every per-token boolean; sort __all__ per RUF022
- registry: add public register_logprob_backend() seam for PR 3 and
  tests; delegate _platform() to _platform_for_device(None); reuse
  _get_or_create_backend() in get_op so WS2 and legacy dispatch share
  one cache/blacklist code path
- tests: use the registration seam instead of poking private state,
  pin _even_bounds' last bound for non-divisible vocabularies, assert
  candidate-list decoupling in both directions, cover registration
  replace semantics and backend_id normalization
- docs: state that cross-TP bitwise equality needs a global tile-level
  merge structure independent of TP partitioning (per-shard tiles alone
  leave different grouping at shard boundaries), and that padded columns
  are masked to -inf before the local (max, sumexp) partials
- registry: scope logprob capabilities per platform so the same backend
  enum can declare different support on cuda/rocm/cpu; validate the
  platform argument of register_logprob_backend against known platforms
- contract: derive IMPLEMENTATION_KINDS from RESERVED_DISPATCH_POLICIES
  and use it for the kind check; wrap non-iterable roles/dtypes in
  LogprobContractError for consistent error handling
- tests: cover per-platform capability scoping, unknown-platform
  rejection, and non-iterable roles/dtypes
…typed contract

Address external review: the cross-TP bitwise guarantee lived only in
prose, so a fixed-topology-deterministic backend could pass dispatch as
fully conformant.

- DeterminismScope (fixed_topology | cross_tp_bitwise): requested via
  ReductionSpec (default cross_tp_bitwise, the RL-Align#241 PR 3 target),
  declared per backend via determinism_scopes, enforced by dispatch;
  replaces the deterministic_tp_merge bool
- MaskMode (explicit_active_mask | ignore_index) replaces
  supports_inactive_tokens: the contract permits inactive targets that
  do not hold ignore_index, so ignore-index-only backends are rejected
  for contracts with inactive tokens
- LogprobOutputSpec pins the output surface: fp32 selected logprob and
  fp32 vocab LSE, replicated across the TP group
- implementation_kind is now a tier (reference | production);
  determinism is no longer conflated with it, and requesting
  "deterministic" as a policy raises a loud error pointing at
  determinism_scope
- fallback provenance: policy evaluation now precedes capability
  checks, so a candidate excluded by the caller's own policy never
  counts as a fallback even when it also lacks capabilities
- docs: define the (-inf, 0) identity partial for padding-only or
  all--inf shards; document that requested_backend="auto" is not
  distributed-safe and specify the preflight fingerprint agreement
- LogprobContract.cross_rank_fingerprint(): rank-independent identity
  for that preflight; provenance now records active_mask_sha256 so
  masks with equal active counts remain distinguishable
Fold the normative reduction semantics (padded-column masking, fp32
(max, sumexp) merge formulas, the (-inf, 0) identity partial, and the
cross-TP tile-structure requirement) into the ReductionSpec and
DeterminismScope docstrings, and repoint the runtime-dispatch and
batch-invariant-logp doc references at the module. The contract summary
moves to the PR description.
Shrink class docstrings toward the attention-contract one-liner style and
cut design-rationale comments; the normative reduction semantics stay in
the ReductionSpec and DeterminismScope docstrings.
The guard added per review rejects requested_backend="auto" whenever
tp_world_size > 1, so TP-sharded dispatch tests now name an explicit
policy and auto-policy tests use TP=1 contracts.  Add coverage for the
guard itself and document the restriction in get_logprob_op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PyjQEqDJwy9Cos4Sb9QBK
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 87ac9fd9-99af-44d6-ac91-d292539a65ca

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hihaluemen
hihaluemen changed the base branch from main to test August 21, 2026 21:00
@hihaluemen
hihaluemen force-pushed the work/ws2-logprob-rocm branch 2 times, most recently from 1a2eb62 to df1e637 Compare August 21, 2026 21:08
@hihaluemen
hihaluemen force-pushed the work/ws2-logprob-rocm branch from df1e637 to f79602c Compare August 21, 2026 21:11
zhangj1an added a commit that referenced this pull request Aug 24, 2026
…patch

Adds the dispatch seam and Vime adapter that make the strict ROCm attention
core reachable as an explicit, fail-closed backend, mirroring the WS2
logprob provider in #328.

Registry:
- register_attention_backend / get_attention_op over AttentionContract, with
  a candidate list kept separate from the legacy attn/attention priority maps
  and empty on every platform by default. A WS2 caller can therefore never be
  served by an SDPA-shaped wrapper that does not export attention-domain LSE,
  and the strict core can never be selected by a legacy get_op caller.
- aiter.rocm.ck_dense_mha is registered only when aiter.ops.mha genuinely
  imports and exposes mha_fwd/mha_bwd. An explicit request for it fails
  loudly when the vendor stack is absent instead of degrading to a different
  backend.

Vime adapter (rl_engine/integrations/vime/attention.py):
- Structural request in, (out, lse) plus provenance out; RL-Kernel never
  imports Vime, and native fallback is signalled through an
  attention_provider_unavailable marker rather than an imported type.
- Each logical batch row is materialized on its own. Raw AITER mha_fwd is
  batch-composition sensitive in BF16 at some shapes (S=256 B=4, S=512
  B=2/B=4; up to 1.5625e-02) while invariant at others, so batching would
  otherwise change the bits for a subset of shapes only.
- Position identity is derived and validated, so a training-side full
  sequence and a rollout-side chunk provably describe the same tokens.
- CP>1 and decode fail closed rather than being served by a core that does
  not own the cross-rank merge or KV-cache identity.

Contract:
- AttentionContract.cross_rank_fingerprint() for cross-rank preflight,
  mirroring LogprobContract; auto dispatch stays rejected under CP>1.

Benchmarks (MI300X, gfx942, torch 2.12.0+rocm7.14, BF16, Qwen3-8B heads):
forward is within 4% of SDPA and ~1.9x faster than the Triton FlashAttention
backend at S=4096. The deterministic backward costs ~2.4x time and scales
O(S^2) in memory (16.8 GiB at S=4096); that cost is AITER's mha_bwd, not the
integration - raw AITER peaks at 16657 MiB against the provider's 16722 MiB.

Tests: 38 new cases; the full suite is unchanged against the pre-change
baseline (27 failed / 224 skipped / 607 errors both before and after, all
from the unbuilt native extension), with passed rising 1068 -> 1102.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfN3b2ep5DvxVDir7J36j3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants