Repository navigation
[diffusion] Add the dynamic LoRA delta inside the second GEMM - #43501
Merged
Merged
Conversation
An unmerged adapter's update used to be materialized as a full
[tokens, out] tensor, scaled in up to three separate passes (alpha / rank,
strength, request scale) and added to the base output in a fourth, so on a
fast GPU the dynamic path cost more than the base GEMM it sits next to.
At the default quality ("lossless") the second LoRA GEMM now accumulates the
scaled update straight onto the base output (addmm, beta = 1) and rounds
once, and a row-parallel layer's bias moves into the base GEMM's epilogue as
RowParallelLinear already does. At "exact" the reference chain stays,
minus the multiplications by a unit scale, which are exact; its output is
unchanged bit for bit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mickqian
requested review from
AgainstEntropy,
BBuf,
HaiShaw,
kevin-mii,
niehen6174,
ping1jing2,
wisclmy0611,
yichiche and
zijiexia
as code owners
October 10, 2026 06:13
Contributor
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Collaborator
Author
|
/tag-and-rerun-ci |
Under torch.compile, the replicated and nn.Linear LoRA forwards read the request's LoRA scale as a symbolic float. Passed as addmm's alpha keyword it was frozen at its first value: on CI, after a call at scale 1.0, a request at 0.5 got the full update (test_request_lora_scale_preserves_delta_offset_and_base). The scale now multiplies the rank-wide intermediate as a tensor op, which compiled code keeps per call, as the reference path's `delta * scale` does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The in-GEMM delta read the request's quality from the forward context and took a missing request for the default. Many stages set that context without forward_batch, LTX-2's denoising among them, so LTX-2.3's two-stage HQ pipeline ran its stage-2 distilled LoRA on the lossless path even for a quality="exact" request, and CI's bit-exact golden for ltx_2_3_hq_pipeline failed (37.5 dB). Such a forward now keeps the exact chain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
detain
added a commit
to detain/sglang
that referenced
this pull request
Oct 15, 2026
…y-gated bias path Merge sgl-project#4 kept our stack's pre-sgl-project#43501 class body and dropped upstream's new forward override (split through the generic _forward_with_delta decomposition, which always fuses the bias in the GEMM). That silently changed exact-tier numerics (bias added after reduce, bit-for-bit reference chain) and broke upstream's own test_row_parallel_bias_moves_into_the_gemm_ only_above_exact (new in sgl-project#43028) on every host, including GPU CI. Restores the override verbatim (imports split_tensor_along_last_dim and tensor_model_parallel_all_reduce) while keeping the stack's __init__, rank-0-gated _add_lora_output_offset, and slice hooks. Test file now matches origin/main and passes 25P/20S CPU. (cherry picked from commit a600fb88f46b88cd599f6dd7925891ba1842a28a)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
A dynamic (unmerged) LoRA computes its update as a full
[tokens, out]tensor and then handles it in separate passes:On a fast GPU these memory passes cost more than the base GEMM next to them. At real layer shapes on B300, the adapters CI and MiniMax-H3 use add the following on top of the base linears:
This cost matters more now that
--lora-merge-mode autokeeps adapters dynamic when a BF16 merge would round most of their update away (#43385), as with the H3 Turbo LoRA.Modifications
quality="lossless"(the default). The second LoRA GEMM accumulates the update straight onto the base output (addmm, beta = 1), so the[tokens, out]delta is never materialized. Before that, the combined scale (alpha/rank × strength × request scale) multiplies the rank-wide intermediatex @ Aᵀas a tensor op, not asaddmm'salpha.alpha: the compiled forwards read the request scale as a symbolic float, which a keyword argument would freeze at its first value. CI caught exactly that intest_request_lora_scale_preserves_delta_offset_and_base.addmmper group, on column views of the output.RowParallelLineardoes without LoRA.quality="exact". The reference chain is unchanged except that multiplications by a unit scale are skipped.x * 1.0is exact, so the output stays the same bit for bit.nn.Linear) now share one_add_lora_delta, so the two tiers are implemented once.Accuracy Tests
Layer level, at the real shapes. Each layer kind the Cseti Wan, elusarca Z-Image and Larry H3 Turbo adapters touch, with their ranks, on B300:
exact, every layer istorch.equalto main.lossless, every layer passes the lossless-tier admission gate (quality_tier_admission.assert_error_no_worse_than_reference) against the FP64 math, with main as the baseline.New unit test.
test_lora_dynamic_delta_in_gemm.py(CPU) checks:With it, the LoRA unit tests pass on B300 (91 passed).
End to end. MiniMax-H3 T2VA at Ulysses 2 with the Larry Turbo v4 LoRA,
--lora-merge-mode dynamic, 9 steps, same seed:quality="exact"is byte-identical to main (2fa46923e49d).33d18ed0e431, main gives266717abee3e), as any lossless-tier change gives on H3.exact, so their goldens are unaffected.Speed Tests and Profiling
LoRA cost per DiT forward, B300. Each value is the sum over the layers the adapter touches of
layer(x)minus the base linear alone, isolated CUDA-event timings:exactlossless(default)What remains in the lossless column is reading x again for the first LoRA GEMM and one read-modify-write of the output in the second. That is the floor for an update applied next to a cuBLAS GEMM.
End to end, 2×B300. MiniMax-H3 T2VA at Ulysses 2 with the Larry Turbo v4 LoRA, 9 steps, client latency of the two warm requests per arm:
--lora-merge-mode/ qualitymergedynamic, default qualitydynamic,quality="exact"At the default quality, a dynamic adapter now costs 0.5 s (+3.7%) over merged weights instead of 1.2 s (+9.0%).
The follow-up commit (scale on the rank-wide rows instead of
alpha) leaves this adapter's output unchanged: its combined scale is 1.0, so the multiplication is skipped. A rerun after it gave the same MP4s at both qualities (33d18ed0e431,2fa46923e49d) and 13.78 / 13.78 s at the default quality.Checklist
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ✅ Run #38055503503
Latest PR Test (Extra): ❌ Run #38055503258
Latest PR Test (AMD ROCm 10): ❌ Run #38055503360