Dynamic VRAM applies LoRAs in the weight's dtype: on bf16 models, part of the LoRA rounds away
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 77/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Domain
- backend, machine-learning, performance
Research direction
Read LowVramPatch.__call__ in comfy/model_patcher.py and the dynamic path in comfy/ops.py (post_cast), then compare with patch_weight_to_device and lora_compute_dtype() as described in the issue. Check how the sum's intermediate dtype and result cast affect LoRA deltas on bf16 weights. Done when dynamic and static VRAM paths retain comparable LoRA changes without changing the output weight dtype.
Written by the indexing model from the issue text.
Description
What happens
With dynamic VRAM on (the default), a LoRA on a bf16-compute model is applied weaker than with --disable-dynamic-vram. The same workflow, seed and LoRA give visibly different pictures in the two modes.
Cause
LowVramPatch.__call__ (comfy/model_patcher.py) computes the patch with intermediate_dtype=weight.dtype:
return comfy.lora.calculate_weight(patches, weight, self.key, intermediate_dtype=weight.dtype)
In the dynamic path (comfy/ops.py, post_cast), the weight has already been cast or dequantized to the compute dtype, so on a bf16 model W + dW is summed in bf16. A LoRA's per-weight change is usually far below bf16's step at the weight's magnitude, so most of the sums round back to W. The static path uses lora_compute_dtype() (fp16 or fp32), which keeps the delta.
Measured
MiniMax H3 Image (minimax_h3_image_pruned_int8_convrot.safetensors, supported_inference_dtypes = [bfloat16, float32]), a rank-32 LoRA on 200 Linears, |dW|/|W| 0.06–0.5% per layer:
| Sum done in | LoRA kept (projection of the applied delta on dW) | Weights left unchanged |
|---|---|---|
| bf16 | 0.85 overall, 0.44–0.99 per layer | 36–86% |
| fp16 | 0.999 | 7–39% |
Renders, same seed, 1536×1536, 6 steps (mean abs pixel difference, 0–255; two renders of the same setup differ by about 4):
- dynamic VRAM on vs
--disable-dynamic-vram: 15.3 - a reference implementation applying the exact LoRA vs
--disable-dynamic-vram: 5.7 - the same reference with only the
W + dWsum moved to bf16 vs dynamic VRAM on: 3.5
Minimal reproduction of the rounding (torch only)
import torch
torch.manual_seed(0)
W = torch.randn(4096, 4096) * 0.02
up, down = torch.randn(4096, 32) * 0.002, torch.randn(32, 4096) * 0.002
dW = up @ down # |dW|/|W| ~ 0.1%
for dt in (torch.float32, torch.float16, torch.bfloat16):
Wl = W.to(dt)
got = (Wl + dW.to(dt)).float() - Wl.float()
print(dt, "kept %.3f" % ((got * dW).sum() / (dW * dW).sum()),
"unchanged %.0f%%" % (100 * (got == 0).float().mean()))
# float32 kept 1.000 unchanged 0%
# float16 kept 1.000 unchanged 20%
# bfloat16 kept 0.573 unchanged 75%
Suggested fix
Do the sum in comfy.model_management.lora_compute_dtype(device) (as patch_weight_to_device does) and cast the result back, instead of weight.dtype.
Version
ComfyUI 0.45.0 (4d64a8b, 11 Nov 2026), Windows, RTX 5090, PyTorch CUDA build. Reproduced with --disable-all-custom-nodes.
- Dominant language
- Python
- Stars
- 146k
- Forks
- 17.4k
- Avg merge
- 19h 44m
- Merged PRs (30d)
- 178
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Comfy-Org/ComfyUI
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
ROCm gfx1100: contiguous-k/v SDPA workaround is gfx1151-only, MiniMax H3 sampling 2.2x slower on RX 7900 XTXPossibly taken @TwelveNights claimed this 3 days ago. OpenPotential Bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Sub-quadratic attention is ~10x slower on long sequences on RDNA2 (gfx1030) because the key passed to bmm is non-contiguousPossibly taken @KornienkoDev claimed this 3 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
MoGe-3 output is nondeterministic on CUDA: sparse_pool3d_mean sums with float index_add_ (atomics)Possibly taken @potoior claimed this 10 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Comfy-Org/ComfyUI#17498 · 1 comment ·
Maintainers usually reply within 1 day
-
TRELLIS.2 texture bake fails under --bf16-vae: BakeTextureFromVoxel 'Got unsupported ScalarType BFloat16'Possibly taken @Shubham-Padkonde claimed this 11 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
All issues in Comfy-Org/ComfyUI
Similar issues
-
add entry to blockOpen
Difficulty 1/5 Under an hour Newbie friendliness 65/100
StevenBlack/hosts#3299 ·
-
`garak --report` overwrites the input report when its name has no ".report"Possibly taken @0z5a claimed this today. Openneeds-triage
Difficulty 2/5 Half a day Newbie friendliness 72/100
Maintainers usually reply within 2 days
-
plot_graph crashes when a mesh node has no m2m edgePossibly taken @tygvi claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
mllam/neural-lam#883 · 1 comment ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
future-agi/future-agi#4092 · 1 comment ·
Maintainers usually reply within 1 day
-
transcript analytics: a text whose only protected spans the prose pass redacts is withheld wholeOpen
Difficulty 1/5 Under an hour Newbie friendliness 82/100
WingedGuardian/GENesis-AGI#4641 ·
Maintainers usually reply within 1 day