Skip to content

[WS2] feat: add TP=1 logprob comparison harness - #262

Open
hihaluemen wants to merge 6 commits into
RL-Align:mainfrom
hihaluemen:feat/ws2-logprob-single-gpu-harness-pr2
Open

[WS2] feat: add TP=1 logprob comparison harness#262
hihaluemen wants to merge 6 commits into
RL-Align:mainfrom
hihaluemen:feat/ws2-logprob-single-gpu-harness-pr2

Conversation

@hihaluemen

@hihaluemen hihaluemen commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Add the TP=1 logprob comparison harness requested by PR2 of #241.

The harness registers the existing WS1 batch-invariant PyTorch logprob path as the reference and compares the supported single-GPU backends against it before tensor-parallel communication is introduced. It reports direct vocabulary-LSE drift and active-token-only selected-logprob drift, while recording enough backend provenance to detect accidental fallback.

The existing production operator contract remains unchanged. The new forward_with_lse methods are diagnostic entry points used by the comparison harness and tests.

Implements PR2 of #241.

Scope

This PR covers the single-GPU registration and regression guard described in PR2:

  • Use the merged WS1 batch-invariant PyTorch implementation as the reference.
  • Require the TP=1 PyTorch path to remain bitwise equal to that reference.
  • Compare the PyTorch, Triton, and CUDA SM90 batch-invariant logprob backends.
  • Report vocabulary-LSE drift over all logical token rows.
  • Report selected-token dlogp drift over active response/action tokens only.
  • Record tp_world=1 and communication=none in the report.
  • Fail closed when an explicitly requested backend is unavailable or falls back to another implementation.

This PR does not implement vocab sharding, collective communication, fixed-order cross-rank LSE merging, CP reconstruction, or distributed artifact generation. Those remain part of the later PRs in #241.

Changes

Single-GPU comparison harness

Add rl_engine/testing/logprob_comparison.py with:

  • Structured comparison inputs, candidates, reports, and backend provenance.
  • Exact backend selection for pytorch, triton, and cuda-sm90.
  • Direct comparison against the existing batch-invariant PyTorch reference.
  • Bitwise equality reporting for selected logprobs.
  • Max, mean, p95, and p99 absolute-drift statistics.
  • Active-token masking for selected-logprob drift.
  • Validation for target shape, dtype, range, and ignore_index usage.
  • A generic operator-comparison registration for batch_invariant_logp.

Diagnostic LSE entry points

Add a diagnostic-only method to each supported backend:

op.forward_with_lse(logits, target_ids, ignore_index=-100) -> (logp, lse)

The normal production call remains:

op(logits, target_ids, ignore_index=-100) -> logp

The diagnostic path exposes the LSE computed by the backend itself. The harness does not reconstruct LSE from selected logprobs, which keeps the LSE comparison independent and useful for later TP work.

For an explicit cuda-sm90 request, the diagnostic path requires the compiled SM90 extension and compatible Hopper inputs. It does not use the production operator's fallback behavior.

Command-line comparison tool

Add scripts/compare_logprob.py for reproducible local and GPU comparisons.

Example:

python scripts/compare_logprob.py \
  --candidate triton \
  --candidate cuda-sm90 \
  --device cuda \
  --dtype bf16 \
  --batch 2 \
  --seq 16 \
  --vocab 151936

The command writes a structured JSON report to stdout. RL-Kernel diagnostic logs are routed to stderr so redirected stdout remains valid machine-readable JSON.

Comparison contract

For each logical token row, the compared values are:

LSE  = logsumexp(logits[..., vocab])
logp = selected_logit - LSE

LSE drift is measured over every logical token row. Selected-logprob drift is measured only where the active-token mask is true. Each drift report contains:

active_count
max_abs
mean_abs
p95_abs
p99_abs

The report also records:

  • Requested and actual backend.
  • Concrete implementation class.
  • Direct-LSE provenance.
  • Input shape and dtype.
  • Active-token count.
  • TP world size.
  • Communication mode.
  • Bitwise selected-logprob status.

Tests

Add focused coverage for:

  • TP=1 PyTorch bitwise regression.
  • Direct LSE identity.
  • Active-token-only percentile calculation.
  • The zero-active-token case.
  • Invalid active ignore_index usage.
  • Structured report serialization.
  • Operator-harness registration.
  • Exact Triton and CUDA SM90 diagnostic paths.
  • Fail-closed backend provenance.
  • Machine-readable CLI stdout when RL-Kernel emits diagnostic logs.

Validation

Windows CPU

python -m pytest tests/test_logprob_comparison.py tests/test_operator_inputs.py tests/test_op_checks.py -q

Result:

39 passed, 2 skipped

The skipped cases require CUDA/Triton backends.

WSL Triton

Focused Triton validation:

11 passed, 1 skipped

The skipped case requires a compiled CUDA SM90 extension.

NVIDIA H800 / SM90

Validated on:

GPU: NVIDIA H800 PCIe
Compute capability: 9.0
Python: 3.11.15
PyTorch: 2.11.0+cu128
CUDA toolkit / nvcc: 12.8
Triton: 3.6.0

The editable CUDA extension built successfully with the SM90 kernel enabled:

batch_invariant_logp_sm90=True

Test results:

PR2 focused tests: 41 passed in 3.25s
Complete batch-invariant logprob suite: 67 passed in 4.60s

Observed BF16 SM90 drift against the PyTorch reference:

Shape LSE max abs dlogp max abs
[2, 8, 1024] 4.76837158203125e-07 4.76837158203125e-07
[2, 16, 151936] 9.5367431640625e-07 9.5367431640625e-07

Both comparisons used tp_world=1, communication=none, and the requested cuda-sm90 implementation without fallback.

Additional checks:

Python compileall: passed
git diff --check: passed

Notes for review

  • The main addition is the comparison and reporting harness; this PR does not change distributed logprob mathematics.
  • Production calls still return only selected logprobs.
  • forward_with_lse exists to expose backend-native diagnostics without changing production callers.
  • Explicit backend requests intentionally fail instead of silently falling back, because backend provenance is part of the regression contract.
  • The PyTorch bitwise check is the TP=1 regression guard requested by [WS2] TP-aware deterministic logprob for cross-config alignment (Qwen3-8B TP=2 CP=2 BF16) #241; numerical drift is expected for independently implemented Triton and CUDA reductions.

Summary by CodeRabbit

  • New Features

    • Added a single-GPU log-probability comparison harness for PyTorch, Triton, and Hopper SM90 CUDA backends.
    • Added diagnostic log-probability and log-sum-exp outputs.
    • Added a configurable command-line tool supporting device, data type, shapes, prompt length, seed, and backend selection.
    • Added JSON reports with backend provenance and drift statistics.
  • Documentation

    • Added usage, validation, prerequisites, and SM90-specific testing guidance.
  • Tests

    • Added coverage for comparisons, validation, diagnostics, CLI behavior, and supported accelerator backends.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hihaluemen, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 29 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d5b945c-2784-4081-b38b-a74df6b8c48b

📥 Commits

Reviewing files that changed from the base of the PR and between c028b5b and 7ba09b5.

📒 Files selected for processing (1)
  • rl_engine/testing/logprob_comparison.py
📝 Walkthrough

Walkthrough

Added single-GPU logprob/LSE diagnostics for PyTorch, Triton, and SM90 CUDA backends. Added comparison APIs, a JSON CLI harness, validation tests, and maintainer documentation.

Changes

Single-GPU logprob comparison

Layer / File(s) Summary
Operator LSE diagnostics
rl_engine/kernels/ops/{pytorch,triton,cuda}/loss/batch_invariant_logp.py
Added forward_with_lse paths that return log-probabilities and row-wise LSE values. Triton now shares launch and validation helpers. SM90 rejects unsupported inputs without fallback.
Comparison API and provenance
rl_engine/testing/logprob_comparison.py, rl_engine/testing/__init__.py
Added backend candidates, input and report dataclasses, fail-closed execution checks, provenance, drift statistics, and public exports.
CLI comparison harness
scripts/compare_logprob.py
Added seeded input generation, backend selection, device and dtype options, prompt masking, stderr logging, and JSON report output.
Validation coverage and maintainer workflow
tests/test_logprob_comparison.py, docs/design/ws2-logprob-single-gpu-harness.md, docs/design/ws2-logprob-sm90-validation.md
Added regression, validation, CLI, operator-suite, CUDA, Triton, and SM90 coverage. Added harness and SM90 validation guides.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant ComparisonAPI
  participant PyTorchReference
  participant CandidateBackend
  participant JSONReport
  CLI->>ComparisonAPI: submit seeded inputs and selected backends
  ComparisonAPI->>PyTorchReference: compute reference logprob and LSE
  ComparisonAPI->>CandidateBackend: execute selected diagnostic backend
  CandidateBackend-->>ComparisonAPI: return logprob and LSE tensors
  ComparisonAPI->>JSONReport: calculate drift and provenance
  JSONReport-->>CLI: return serialized comparison report
Loading

Possibly related PRs

Suggested labels: needs-gpu-ci

Suggested reviewers: ethanzero2hero, inaniloquentee, kjldefeated, flink-ddd

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding a TP=1 logprob comparison harness.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
rl_engine/testing/logprob_comparison.py (1)

132-146: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Disable autograd during diagnostic execution.

If inputs.logits.requires_grad is true, the reference creates two autograd graphs, and a PyTorch candidate can create another graph. The later detach() calls occur after full-vocabulary intermediates are retained. Run diagnostic calls under torch.no_grad().

Proposed change
-    reference_logp, reference_lse = _run_ws1_reference(
-        inputs.logits, effective_targets, inputs.ignore_index
-    )
+    with torch.no_grad():
+        reference_logp, reference_lse = _run_ws1_reference(
+            inputs.logits, effective_targets, inputs.ignore_index
+        )
@@
-        logp, lse = _run_candidate(
-            candidate,
-            inputs.logits,
-            effective_targets,
-            inputs.ignore_index,
-        )
+        with torch.no_grad():
+            logp, lse = _run_candidate(
+                candidate,
+                inputs.logits,
+                effective_targets,
+                inputs.ignore_index,
+            )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rl_engine/testing/logprob_comparison.py` around lines 132 - 146, The
diagnostic execution creates autograd graphs during _run_ws1_reference and
_run_candidate calls which retain full-vocabulary intermediates in memory, even
though detach() is applied later. Wrap the _validate_inputs call, the
_run_ws1_reference invocation, and the candidate iteration loop (containing the
_run_candidate calls) in a torch.no_grad() context manager to disable autograd
tracking entirely during these diagnostic operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rl_engine/testing/logprob_comparison.py`:
- Around line 178-185: Run Black and isort formatters on the specified Python
files to match repository formatting standards. Apply isort to the imports and
Black to the code formatting at the following locations:
rl_engine/testing/logprob_comparison.py lines 178-185 (apply both isort to the
NativeBatchInvariantLogpOp import and Black to the op() and forward_with_lse()
call formatting), scripts/compare_logprob.py lines 19-22 (apply isort to the
package import), tests/test_logprob_comparison.py lines 15-17 (apply both isort
and Black to the import), and tests/test_logprob_comparison.py lines 197-199
(apply Black to the function call formatting).
- Around line 216-224: Update _candidate_provenance so candidate.provenance is
merged before the canonical requested_backend, actual_backend, tp_world,
communication, and lse_source fields. Ensure these canonical fields remain
authoritative and cannot be overwritten in the serialized report.

---

Nitpick comments:
In `@rl_engine/testing/logprob_comparison.py`:
- Around line 132-146: The diagnostic execution creates autograd graphs during
_run_ws1_reference and _run_candidate calls which retain full-vocabulary
intermediates in memory, even though detach() is applied later. Wrap the
_validate_inputs call, the _run_ws1_reference invocation, and the candidate
iteration loop (containing the _run_candidate calls) in a torch.no_grad()
context manager to disable autograd tracking entirely during these diagnostic
operations.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fae2870a-cf06-4b8c-9dbb-30a35b083445

📥 Commits

Reviewing files that changed from the base of the PR and between 0b12d34 and 115d86c.

📒 Files selected for processing (9)
  • docs/design/ws2-logprob-single-gpu-harness.md
  • docs/design/ws2-logprob-sm90-validation.md
  • rl_engine/kernels/ops/cuda/loss/batch_invariant_logp.py
  • rl_engine/kernels/ops/pytorch/loss/batch_invariant_logp.py
  • rl_engine/kernels/ops/triton/loss/batch_invariant_logp.py
  • rl_engine/testing/__init__.py
  • rl_engine/testing/logprob_comparison.py
  • scripts/compare_logprob.py
  • tests/test_logprob_comparison.py

Comment thread rl_engine/testing/logprob_comparison.py Outdated
Comment thread rl_engine/testing/logprob_comparison.py
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.

1 participant