Skip to content

Commit 118af6f

Browse files
dyurk-lilaclaude
andcommitted
chore(megatron): reconcile fused-logprob onto upstream #1841
Rebase re-scoped this PR to layer only its distinguishing additions (the Triton backend + its GPU tests) on top of the merged #1841 fused-linear-cross-entropy. The torch chunked-logprob core is now upstream's FusedLinearChunkedDistributedLogprob; this commit fixes the vendored Triton adapter's docstrings, the benchmark, and the GPU test to reference the upstream class / the reconciled fused_lm_head_logprob_backend selector instead of this PR's now-dropped FusedLinearLogprob / _fused_linear_logprob_apply. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4e5a7fd commit 118af6f

4 files changed

Lines changed: 27 additions & 20 deletions

File tree

skyrl/backends/skyrl_train/distributed/megatron/fused_linear_logprob_triton.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
# fallback; we additionally surface a module-level flag and a hard guard.)
2626
# * Added ``FusedLinearLogprobTriton`` autograd Function — a SkyRL-shaped
2727
# adapter around verl's ``LinearCrossEntropy``/kernels that matches the
28-
# pure-torch ``FusedLinearLogprob`` contract in ``model_utils.py`` exactly
29-
# (see that class for the contract). This is NEW code, not from verl.
28+
# pure-torch ``FusedLinearChunkedDistributedLogprob`` contract in
29+
# ``model_utils.py`` exactly (see that class for the contract). This is NEW
30+
# code, not from verl.
3031
#
3132
# NOTE on the #2656 out-of-bounds-vocab fix: verl's forward mainloop masks
3233
# out-of-bounds vocab columns to -inf for the LSE max/exp/denominator while
@@ -68,7 +69,7 @@
6869
This module vendors verl's online-softmax fused linear-cross-entropy Triton
6970
kernels (with the #2656 TP all-reduce / OOB-vocab masking scheme) and exposes
7071
:class:`FusedLinearLogprobTriton`, an autograd Function that matches the exact
71-
interface of the pure-torch ``FusedLinearLogprob`` in ``model_utils.py``.
72+
interface of the pure-torch ``FusedLinearChunkedDistributedLogprob`` in ``model_utils.py``.
7273
7374
Triton/CUDA is required to actually *run* the kernel, but the module is import-safe
7475
without triton (``TRITON_AVAILABLE`` will be ``False`` and the adapter raises a
@@ -1888,8 +1889,9 @@ def backward(ctx, dlogprobs: torch.Tensor, dentropy: torch.Tensor) -> list:
18881889

18891890

18901891
# ======================================================================================
1891-
# SkyRL adapter (NEW code — not from verl). Matches the pure-torch FusedLinearLogprob
1892-
# contract in model_utils.py exactly so the two backends are interchangeable.
1892+
# SkyRL adapter (NEW code — not from verl). Matches the pure-torch
1893+
# FusedLinearChunkedDistributedLogprob contract in model_utils.py exactly so the two
1894+
# backends are interchangeable.
18931895
# ======================================================================================
18941896

18951897
# A label sentinel guaranteed to fall in NO shard's column range after re-keying. verl
@@ -1905,11 +1907,13 @@ def _verl_logprob_kernel_available() -> bool:
19051907
class FusedLinearLogprobTriton(torch.autograd.Function):
19061908
"""Triton fused LM-head + vocab-parallel log-prob of the target token.
19071909
1908-
Drop-in, interface-identical replacement for the pure-torch ``FusedLinearLogprob``
1909-
in ``model_utils.py`` (same ``apply`` signature, same return/grad contract), backed by
1910-
verl's online-softmax fused linear-cross-entropy Triton kernels (vendored above).
1910+
Drop-in, interface-identical replacement for the pure-torch
1911+
``FusedLinearChunkedDistributedLogprob`` in ``model_utils.py`` (same ``apply`` signature, same
1912+
return/grad contract), backed by verl's online-softmax fused linear-cross-entropy Triton
1913+
kernels (vendored above). Selected via ``fused_lm_head_logprob_backend="triton"`` and dispatched
1914+
from ``model_utils._fused_lm_head_logprob_apply``.
19111915
1912-
Contract (must match ``FusedLinearLogprob`` exactly):
1916+
Contract (must match ``FusedLinearChunkedDistributedLogprob`` exactly):
19131917
* ``apply(hidden, weight, target, vocab_start_index, vocab_end_index, chunk_size,
19141918
tp_group, inference_only=False) -> log_probs``.
19151919
* ``hidden``: ``[B, S, H]`` (TP-replicated / SP-already-gathered by the caller).
@@ -1942,7 +1946,7 @@ class FusedLinearLogprobTriton(torch.autograd.Function):
19421946
label-logit all-reduce. verl's epilogue would then return ``max + log(accu) - 0`` = the
19431947
LSE (garbage, not 0). We therefore force ``log_prob = 0`` for fully-OOV positions in
19441948
forward. We do NOT touch the backward gradient at those positions: the references (stock
1945-
``DistributedLogprob`` and the pure-torch ``FusedLinearLogprob``) still emit
1949+
``DistributedLogprob`` and the pure-torch ``FusedLinearChunkedDistributedLogprob``) still emit
19461950
``-softmax * grad_output`` there (the chosen-position term is absent because no shard owns
19471951
the column), and verl's kernel already reproduces that exact value for sentinel-keyed
19481952
labels. Zeroing the backward gradient at those positions would drop that term and diverge
@@ -1964,8 +1968,8 @@ def forward( # pyrefly: ignore[bad-override]
19641968
if not _verl_logprob_kernel_available():
19651969
raise RuntimeError(
19661970
"FusedLinearLogprobTriton requires Triton (and a CUDA device), but triton is not "
1967-
"importable. Install the optional 'triton' extra, or use the pure-torch "
1968-
"FusedLinearLogprob backend instead."
1971+
"importable. Install the optional 'triton' extra, or use the default pure-torch "
1972+
"'torch' backend (FusedLinearChunkedDistributedLogprob) instead."
19691973
)
19701974

19711975
B, S, H = hidden.shape
@@ -1983,7 +1987,7 @@ def forward( # pyrefly: ignore[bad-override]
19831987

19841988
# hidden must be contiguous & 2D for the kernel. The kernel's ``tl.dot(hidden, weight.T)``
19851989
# requires BOTH operands to share a dtype, so cast hidden to the LM-head weight dtype here
1986-
# — exactly what the pure-torch ``FusedLinearLogprob`` does (``hidden.to(weight.dtype) @
1990+
# — exactly what the pure-torch ``FusedLinearChunkedDistributedLogprob`` does (``hidden.to(weight.dtype) @
19871991
# weight.t()``) and what ``ColumnParallelLinear`` does before its bf16 GEMM. On a real
19881992
# hybrid model the pre-head hidden is fp32 (final norm) while the head is bf16, so without
19891993
# this the kernel asserts "Both operands must be same dtype. Got fp32 and bf16". When the
@@ -2047,7 +2051,7 @@ def backward(ctx: Any, *grad_outputs: torch.Tensor) -> tuple:
20472051
# would otherwise return the LSE there (no shard contributes the label logit). But the
20482052
# GRADIENT contract is set by the references this backend must match exactly — stock
20492053
# ``DistributedLogprob`` / ``ChunkedDistributedLogprob`` and the pure-torch
2050-
# ``FusedLinearLogprob`` in model_utils.py. Those compute, for every position
2054+
# ``FusedLinearChunkedDistributedLogprob`` in model_utils.py. Those compute, for every position
20512055
# (in-shard, out-of-shard, or fully-OOV alike):
20522056
# d_logits = grad_output * (onehot(target) - softmax)
20532057
# and only ADD the ``onehot`` (chosen-position) term where the target is owned by this

skyrl/backends/skyrl_train/distributed/megatron/model_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# See the License for the specific language governing permissions and
1717
# limitations under the License.
1818

19+
import warnings
1920
from typing import Any, Optional
2021

2122
import megatron.core.parallel_state as mpu

skyrl/benchmarks/bench_fused_ce_h100.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
nvidia : same logits, then ``fused_vocab_parallel_cross_entropy``
1212
(@jit_fuser fused CE — fuses the softmax/CE stages, still
1313
materializes the logits).
14-
fused-torch : FusedLinearLogprob (pure-torch chunked fused linear logprob —
15-
folds the projection into the chunked TP log-prob; logits never
14+
fused-torch : FusedLinearChunkedDistributedLogprob (pure-torch chunked fused linear
15+
logprob — folds the projection into the chunked TP log-prob; logits never
1616
materialized).
1717
fused-triton : FusedLinearLogprobTriton (vendored verl/bytedance flash kernel).
1818
@@ -49,14 +49,16 @@ def _loss(mode, hidden, weight, target, vocab_local, chunk_size, tp_group):
4949
from megatron.core.tensor_parallel.cross_entropy import vocab_parallel_cross_entropy
5050

5151
from skyrl.backends.skyrl_train.distributed.megatron.model_utils import (
52-
FusedLinearLogprob,
52+
FusedLinearChunkedDistributedLogprob,
5353
)
5454
from skyrl.backends.skyrl_train.distributed.megatron.fused_linear_logprob_triton import (
5555
FusedLinearLogprobTriton,
5656
)
5757

5858
if mode == "fused-torch":
59-
lp = FusedLinearLogprob.apply(hidden, weight, target, 0, vocab_local, chunk_size, tp_group, False)
59+
lp = FusedLinearChunkedDistributedLogprob.apply(
60+
hidden, weight, target, 0, vocab_local, chunk_size, tp_group, False
61+
)
6062
return (-lp).sum()
6163
if mode == "fused-triton":
6264
lp = FusedLinearLogprobTriton.apply(hidden, weight, target, 0, vocab_local, chunk_size, tp_group, False)
@@ -138,7 +140,7 @@ def main():
138140
print(f"Device {torch.cuda.get_device_name(device)} | TP(world)={world} | hidden={HIDDEN} | chunk={args.chunk_size}")
139141
print(
140142
"baseline = vocab_parallel_cross_entropy (eager) | nvidia = fused_vocab_parallel_cross_entropy | "
141-
"fused-torch = FusedLinearLogprob | fused-triton = FusedLinearLogprobTriton"
143+
"fused-torch = FusedLinearChunkedDistributedLogprob | fused-triton = FusedLinearLogprobTriton"
142144
)
143145
print("all: hidden+weight -> per-token CE -> sum -> backward\n")
144146
print("correctness (TP=%d, vocab=%d):" % (world, vocab_shards[0]))

tests/backends/skyrl_train/gpu/gpu_ci/megatron/test_fused_linear_logprob_triton.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _direct_fused_logprobs(hidden, weight_shard, target_shifted, vstart, vend, c
5656
5757
The public entry point shifts targets internally; here we pass an ALREADY-shifted target and
5858
compare against a directly-driven stock reference, so the Triton kernel is exercised in
59-
isolation from the ``_fused_linear_logprob_apply`` backend selection.
59+
isolation from the ``_fused_lm_head_logprob_apply`` backend selection.
6060
"""
6161
leaf_h = hidden.detach().clone().requires_grad_(True)
6262
leaf_w = weight_shard.detach().clone().requires_grad_(True)

0 commit comments

Comments
 (0)