Repository navigation
perf(megatron): autotune fused LM-head Triton kernels - #2373
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors and optimizes the Triton fused linear log-prob kernel, introducing cached and bucketed autotuning to prevent recompilation churn, compiling out entropy-only operations when unused, and ensuring numerical stability for strongly negative logits by using the true row maximum for epilogue shifts. It also updates the benchmark suite and adds comprehensive tests. The review feedback correctly identifies an optimization opportunity in the backward pass: when vocab_size is smaller than 9504, hardcoding vocab_per_split to 9504 causes unnecessary memory allocation and redundant copy overhead. Implementing the suggested min(vocab_size, 9504) limit will improve efficiency.
| vocab_per_split = 9504 | ||
| num_splits = (vocab_size + vocab_per_split - 1) // vocab_per_split | ||
| d_logits = torch.empty((num_tokens, vocab_per_split), device=hidden.device, dtype=hidden.dtype).contiguous() |
There was a problem hiding this comment.
When vocab_size is smaller than 9504 (which is very common per rank when using Tensor Parallelism), hardcoding vocab_per_split = 9504 leads to allocating a significantly larger d_logits staging buffer than necessary. Furthermore, because split_width will be less than vocab_per_split, the code will fall into the else block and perform an expensive .contiguous() copy to slice the tensor, before freeing the original.\n\nBy setting vocab_per_split = min(vocab_size, 9504), we can avoid allocating excess columns, eliminate the redundant copy overhead, and significantly reduce peak GPU memory usage.
vocab_per_split = min(vocab_size, 9504)\n num_splits = (vocab_size + vocab_per_split - 1) // vocab_per_split\n d_logits = torch.empty((num_tokens, vocab_per_split), device=hidden.device, dtype=hidden.dtype).contiguous()
|
| config.num_warps, | ||
| ) | ||
|
|
||
|
|
There was a problem hiding this comment.
Sweep measures different kernel The sweep launches candidates with
COMPUTE_ENTROPY=True, but the SkyRL adapter uses compute_entropy=False. These kernels do different work, so the reported winning schedule and latency gains may not represent the production path. Measuring the log-probability-only specialization would make the results useful for that path.
| logprobs, entropy, maximum, accumulate, entropy_b = fused_linear_logprob_triton.efficient_entropy_forward( | ||
| hidden, weight, labels, 1.0, None | ||
| ) |
There was a problem hiding this comment.
TP path lacks regression coverage This negative-logit test uses the local, entropy-enabled forward path. Tensor parallelism uses a separate epilogue, and the production adapter disables entropy, so the test would not catch a regression in that combination. A TP log-probability-only case would cover the changed production path.
Knowledge Base Used: Backend workers and distributed strategies
0f50e56 to
d219a61
Compare
d219a61 to
5566c6d
Compare
5566c6d to
d68c42b
Compare
d68c42b to
ae7d851
Compare
ae7d851 to
690e082
Compare
690e082 to
e2c58d5
Compare
e2c58d5 to
f382af2
Compare
f382af2 to
8eeef97
Compare
Signed-off-by: Dominic Yurk <dyurk@lila.ai>
Signed-off-by: Dominic Yurk <dyurk@lila.ai>
Signed-off-by: Dominic Yurk <dyurk@lila.ai>
8eeef97 to
8cc8466
Compare
# What does this PR do? Compute selected-token log-probabilities and policy entropy from one fused LM-head projection for RL. ## How it works Metric-only entropy reuses the forward reductions without adding entropy-gradient work. Entropy-loss mode keeps the entropy derivative in the fused autograd path. Torch and dense Triton return log-probabilities and entropy through the same packed TP/CP gather/scatter contract. #2375 adds block-sparse forward computation on top of this shared entropy path, and #2376 adds block-sparse backward computation. ## Usage Existing fused LM-head settings also cover entropy metrics and `use_entropy_loss`; the caller’s gradient requirement selects the entropy backward behavior. ## Performance Recorded measurements on one 8× NVIDIA H100 80 GB node compared the combined dense Triton path with fused selected-token log-probability scoring followed by the previous separate metric-entropy projection. Across **115 points**, the combined path was a median **1.44× faster**. The sweep covered four LM-head shapes, 16K–256K tokens, and representative TP/CP layouts with `TP × CP <= 8`. For differentiable entropy at TP4/CP2, `(H=4096,V=131072)`, and 16K tokens, the recorded speedup versus fused log-probability scoring plus a separate differentiable entropy projection was: | Active rows | Dense combined / previous two-pass speedup | | ---: | ---: | | 100% | 2.15× | | 95% | 2.13× | | 73% | 2.13× | | 64% | 2.11× | | 23% | 2.17× | The differentiable-entropy measurements used 180 CPU requested and limited. The small variation across activity levels is measurement noise: this PR's dense path does not inspect the activity pattern. #2375 measures the additional benefit from skipping inactive forward tiles against this shared entropy baseline. These are isolated kernel measurements on one GPU generation and four model shapes, not end-to-end training throughput or universal model/GPU claims. Recorded benchmarks predate the current rebase and were not rerun for it. ## Test coverage Tests compare entropy values, selected-token log-probabilities, hidden gradients, and weight gradients with materialized-logit references, including packed TP2/CP2 and metric-only entropy. ## Validation Before the short-tail metrics follow-up, focused CPU suites passed in both mirrors: 415 tests across collation, DP sampling, worker batching, layout, active spans, and Megatron correctness (13 Megatron-only skips); 40 bin-packer tests; and 3 unpacked active-span guard tests with megatron-core installed. Ruff and Black passed. Seven targeted Triton value/gradient tests passed on one H100; the full GPU suite and benchmarks were not rerun. After the metrics follow-up, 39 focused SFT CPU tests passed in each mirror, including synchronous and asynchronous short-tail logging; Ruff, Black, and secret detection passed on the changed files. ## Review order Part **6 of 9** in a linked series. The stack is based on `main`; each later PR targets the preceding layer. Review and merge from bottom to top. Depends on #2373. [Review only this layer](d219a61...1c449ae). 1. #2369 — use modified first-fit decreasing packing 2. #2370 — make sequence packing alignment-aware 3. #2371 — share packed segment layout derivation 4. #2372 — align packed bin counts to data parallelism 5. #2373 — autotune fused LM-head Triton kernels 6. #2374 — fuse LM-head entropy for RL 7. #2375 — add block-sparse fused LM-head forward 8. #2376 — add block-sparse fused LM-head backward 9. #2521 — fuse sample-support candidate projection with Triton <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes fused autograd and distributed log-prob/entropy math on the Megatron training hot path; mistakes would skew RL policy gradients or entropy bonuses, though new reference tests cover torch/Triton and TP layouts. > > **Overview** > Megatron’s **fused LM-head** path now returns **per-token entropy from the same hidden→vocab projection** as selected-token log-probs, for both the PyTorch chunked op and the Triton adapter. Callers pass `return_entropy` / `compute_entropy` and `entropy_requires_grad`; metric-only entropy skips backward work, while `use_entropy_loss` threads entropy gradients through the fused autograd backward (including TP/CP gather and packed scatter-back via new `_unpack_prediction_values`). > > The Megatron worker **drops the separate no-grad entropy projection** and the prior `NotImplementedError` when `fused_lm_head_logprob` and `use_entropy_loss` were both on—it reuses fused entropy for RL objectives (non–cross-entropy). Standalone helpers `from_parallel_hidden_to_entropy_packed_sequences` / `_fused_vocab_parallel_entropy_from_hidden` are removed in favor of this single pass. > > GPU tests compare fused entropy+log-prob outputs and gradients to materialized-logit references (torch and Triton); worker CI adds fused torch/triton + entropy-loss cases. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f6712a4. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Signed-off-by: Dominic Yurk <dyurk@lila.ai> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reconciles the backend_config validation with NovaSky-AI#2529, now on main: - check_lora_group_flags lost its strategy argument, and the critic's pre-worker call now passes the critic's init_adapter_path (the server-side trainer.critic.model.lora value is the one the workers will load), matching the policy-side and multi-tenant calls. - _build_critic keeps the dead rank check removed: the client-carried keys' outright rejection at the policy's first create_model still covers the critic, so no check is reintroduced for the merge. - Everything else is main's own: NovaSky-AI#2529's Megatron narrow honour, the OPD entrypoint (NovaSky-AI#2256) and the fused LM-head kernels (NovaSky-AI#2373, NovaSky-AI#2374). Co-Authored-By: Claude Code <noreply@anthropic.com>
# What does this PR do? Skip LM-head projection and softmax work for wholly inactive loss-mask row tiles in SFT and RL. ## How it works The explicit `triton_block_sparse` backend tests whether a row tile contains any active loss positions before projecting it. Active rows retain their original token coordinates, and inactive outputs are zeroed. The shared entropy path comes from #2374. This layer keeps dense backward computation; #2376 adds sparse backward work. The worker forwards the required targets and activity mask through packed and unpacked Megatron paths. Torch and dense Triton remain available through the same backend selector. ## Usage Enable `trainer.fused_lm_head_logprob=true` and set `trainer.fused_lm_head_logprob_backend=triton_block_sparse`. Benchmark representative masks and TP/CP layouts before selecting this backend: nearly fully active masks can regress. Sample-support replay is outside this series. ## Performance A recorded sweep on one 8× NVIDIA H100 80 GB node, with 180 CPU requested and limited, compared dense Triton with forward tile skipping, keeping backward computation identical between arms. Seven post-warmup CUDA-event repetitions used rotated order and the slowest TP/CP rank. These are isolated fused LM-head forward-plus-backward latencies, not whole-training throughput. Across 58 recorded loss-mask/layout measurements, skipping won **55/58**, with **4.67% mean** and **4.62% median** latency reduction; the range was **−2.49% to +10.00%**. All 40 TP4/CP2 model/length points won: | LM-head shape `(hidden, vocabulary)`, 16K–256K tokens | Wins | Mean latency reduction | Range | | --- | ---: | ---: | ---: | | Nemotron Super 120B `(4096, 131072)` | 10/10 | 4.59% | 2.59–6.99% | | Nemotron Ultra 550B `(8192, 131072)` | 10/10 | 5.10% | 3.34–8.75% | | GLM-5.3 Flash `(4096, 154880)` | 10/10 | 4.56% | 2.30–9.41% | | Kimi K3 `(7168, 163840)` | 10/10 | 5.73% | 3.42–9.31% | The three regressions had only 2K–4K rank-local rows. For RL with differentiable entropy at TP4/CP2, `(H=4096,V=131072)`, and 16K tokens, recorded speedups were: | Active rows | Sparse combined / previous two-pass | Sparse / dense combined | Metric-only sparse / dense median | | ---: | ---: | ---: | ---: | | 100% | 2.10× | 0.98× | 1.00× | | 95% | 2.11× | 0.99× | 1.01× | | 73% | 2.13× | 1.00× | 1.02× | | 64% | 2.35× | 1.11× | 1.16× | | 23% | 2.99× | 1.38× | 1.41× | The two-pass comparison includes the entropy-projection savings introduced in #2374; the dense-combined comparison isolates this PR's increment over that shared path. Those ratios are approximate. The metric-only medians come from the broader sweep at the same activity levels, rather than only the single shape used for the differentiable-entropy comparison. The benefit depends on whole inactive tiles, not just the percentage of masked tokens. Near-dense masks, short local sequences, other shapes, and other hardware can favor dense Triton; the backend remains opt-in. Recorded benchmarks predate the current rebase and were not rerun for it. ## Test coverage GPU reference tests cover forward parity and inactive outputs, while worker and configuration tests cover backend selection, loss-output gating, and packed TP/CP layouts. ## Validation Before the short-tail metrics follow-up, focused CPU suites passed in both mirrors: 415 tests across collation, DP sampling, worker batching, layout, active spans, and Megatron correctness (13 Megatron-only skips); 40 bin-packer tests; and 3 unpacked active-span guard tests with megatron-core installed. Ruff and Black passed. Seven targeted Triton value/gradient tests passed on one H100; the full GPU suite and benchmarks were not rerun. After the metrics follow-up, 39 focused SFT CPU tests passed in each mirror, including synchronous and asynchronous short-tail logging; Ruff, Black, and secret detection passed on the changed files. ## Review order Part **7 of 9** in a linked series. The stack is based on `main`; each later PR targets the preceding layer. Review and merge from bottom to top. Depends on #2374. [Review only this layer](1c449ae...0d8e3cf). 1. #2369 — use modified first-fit decreasing packing 2. #2370 — make sequence packing alignment-aware 3. #2371 — share packed segment layout derivation 4. #2372 — align packed bin counts to data parallelism 5. #2373 — autotune fused LM-head Triton kernels 6. #2374 — fuse LM-head entropy for RL 7. #2375 — add block-sparse fused LM-head forward 8. #2376 — add block-sparse fused LM-head backward 9. #2521 — fuse sample-support candidate projection with Triton <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes fused LM-head numerics and gradients on a hot Megatron training path (TP/CP/packed layouts), though behavior is opt-in and heavily tested; misconfigured masks or near-dense activity could still skew loss or performance. > > **Overview** > Adds an opt-in **`triton_block_sparse`** fused LM-head backend that threads an **`active_mask`** from the RL/SFT **loss mask** through Megatron’s packed and unpacked log-prob paths. > > The Triton forward **skips wholly inactive row tiles** (no reordering or row gathers) while inactive tokens still get **zero log-probs/entropy** and **zeroed gradients** on the dense backward path. **`FusedLmHeadBackend`** and trainer config validation gate the new mode; the Megatron worker **builds and packs** the mask, rejects **sample-support replay**, and **sanitizes inactive RL operands** so ratio-style losses do not NaN. The pure-torch fused path applies the same mask via **`masked_fill`**. Benchmarks and GPU/CPU tests cover parity, autotune keys, and backend selection. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 9602a10. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Signed-off-by: Dominic Yurk <dyurk@lila.ai> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Avigya Basnet <avigyabb@gmail.com>
What does this PR do?
Autotune the fused LM-head Triton kernels across bounded shape buckets and skip entropy work for log-probability-only calls.
TLDR: select launch geometry for each workload class while keeping autotuning inputs immutable and log-sum-exp stable for strongly negative logits.
How it works
H100 benchmark
The standalone sweep used one 8x NVIDIA H100 80GB node with 180 CPU requested and limited, Torch 2.11.0+cu130, and Triton 3.6.0. It covered four public LM-head configurations, total token counts from 16K through 256K, and every
(TP, CP)combination in{1,2,4,8}withTP*CP <= 8. The 200 logical cases collapse to 104 unique rank-local(M,H,V)shapes. This is a synthetic kernel-shape sweep; it does not consume dataset examples.Compared with the prior fixed five-stage mainloop:
Mean reductions by LM-head shape were 1.90% for
(H=4096,V=154880), 2.02% for(4096,131072), 7.49% for(7168,163840), and 10.89% for(8192,131072).The largest observed win was the Nemotron Ultra 550B shape at local
(M=8192,H=8192,V=131072), from 34.91 ms to 25.54 ms. These are isolated fused-mainloop results on one GPU generation, not end-to-end training claims; the candidate set intentionally retains schedule diversity for other models and accelerators.Dense log-prob-only specialization
A separate recorded sweep on one 8× NVIDIA H100 80 GB node, with 180 CPU requested and limited, compared the general entropy-capable launch with the log-prob-only specialization used by the SkyRL adapter. It timed the combined fused forward/backward call, with no loss masking. The 22 points covered four LM-head shapes at TP4/CP2 and 16K–256K total tokens, plus
(H=4096,V=131072)at TP1/CP1 and TP8/CP1 with 16K tokens.All 22/22 points were faster: 7.06% mean, 7.17% median runtime reduction, with a 3.28–10.86% range.
(hidden, vocabulary)at TP4/CP2(4096, 131072)(8192, 131072)(4096, 154880)(7168, 163840)The additional TP checks reduced runtime by 7.30% at TP1 and 10.11% at TP8. Removing entropy state saved 128 KiB–2 MiB of measured peak allocation as rank-local token count increased. These results measure the specialization separately from the autotuning comparison above; the percentages should not be added together. They are isolated kernel forward/backward results on H100, not whole-training throughput or other-GPU claims. Recorded benchmarks predate the current rebase and were not rerun for it.
Compile and tuning behavior
A specialization probe measured:
Crossing a power-of-two bucket intentionally retunes because the winning geometry can change. Broad cold tuning is nontrivial (14.9 s median first call and 514 s for the largest unsharded Kimi case in this sweep), so long-running jobs should warm expected buckets before timing.
Validation
Before the short-tail metrics follow-up, focused CPU suites passed in both mirrors: 415 tests across collation, DP sampling, worker batching, layout, active spans, and Megatron correctness (13 Megatron-only skips); 40 bin-packer tests; and 3 unpacked active-span guard tests with megatron-core installed. Ruff and Black passed. Seven targeted Triton value/gradient tests passed on one H100; the full GPU suite and benchmarks were not rerun.
After the metrics follow-up, 39 focused SFT CPU tests passed in each mirror, including synchronous and asynchronous short-tail logging; Ruff, Black, and secret detection passed on the changed files.
Review order
Part 5 of 9 in a linked series. The stack is based on
main; each later PR targets the preceding layer. Review and merge from bottom to top.Depends on #2372. Review only this layer.
Note
Medium Risk
Changes core fused LM-head Triton math, tensor-parallel epilogues, and backward memory behavior; incorrect numerics or autotune cache behavior would affect training loss and gradients, though tests and benchmarks target these paths.
Overview
This PR retunes SkyRL’s fused LM-head Triton path for Megatron: forward/backward/epilogue kernels now autotune over expanded tile schedules with cached, power-of-two token buckets (exact
num_tokensstill drives launches), and the SkyRL adapter calls the stack withcompute_entropy=Falseso entropy-only work is compiled out on the log-prob-only training path.Numerics and autotune safety are tightened: epilogues initialize and pad max reductions with
-infso all-negative logits stay finite, and final log-probs/entropy are written to separate result buffers so repeated tuning cannot corrupt inputs. Backward is simplified to the split-vocabd_logitspath only (unused VERL reductions/wrappers removed), with an early release of the stagingd_logitsbuffer on the last split to cut peak memory.Benchmarks gain
--autotune-sweepand an entropy-vs-logprob-only probe; GPU tests add schedule/bucket invariants and a strongly negative logits regression case.Reviewed by Cursor Bugbot for commit ae7d851. Bugbot is set up for automated code reviews on this repo. Configure here.