stop a VRAM measurement outliving the quantization it was taken under

A measurement describes the model as it was BUILT. Change
component_quantization and the stored number describes something that no
longer exists -- and because force_vram_update makes the measurement win over
the configured size, the stale figure goes on deciding placement.

Wan2.2-TI2V-5B is the worked example, and the numbers are worth keeping. Its
components are 18.63 GB transformer (~5B params at ~3.7 bytes each, i.e.
fp32), 10.58 GB text encoder and 2.63 GB VAE. Its map named text_encoder and
vae; vae is conv-only so bitsandbytes skips it, which left exactly ONE
component quantized and an fp32 18.63 GB transformer loading whole. Hence
measured_vram_gb = 40.0 for a model whose name says 5B.

With the transformer actually quantized the same pipeline is roughly 10 GB,
but 40.0 would have kept forcing CPU offload on a 25.3 GB card until some
unlucky load happened to re-measure it.

The measurement is now stamped with pipeline_cache._signature -- which already
hashes model id, precision, load_in_4bit/8bit and component_quantization, the
exact set that decides how big the built weights are -- and is ignored when
that signature no longer matches. Offload strategy and used_vram_gb are not in
it, so a runtime placement change does not throw away a good measurement.

An entry with no recorded signature is still trusted, so upgrading does not
invalidate every existing measurement at once.
Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014T3rZozi1Ehk96NG1WesgC
parent bb1b78e3
......@@ -3567,6 +3567,19 @@ class MultiModelManager:
except Exception:
pass
def _quant_signature(self, resolved_name: str, cfg: dict) -> str:
"""Hash of the settings that decide how big the built weights are.
Reuses the pipeline cache's signature, which already covers exactly
this: model id, precision, load_in_4bit / load_in_8bit and
component_quantization.
"""
try:
from codai.models import pipeline_cache
return pipeline_cache._signature(resolved_name, cfg)
except Exception:
return ""
def _total_vram_gb(self) -> float:
"""Total VRAM (GB) of the largest visible NVIDIA GPU — used to size a vLLM
instance's gpu_memory_utilization footprint. Falls back to 24 GB."""
......@@ -3810,6 +3823,11 @@ class MultiModelManager:
_persist("measured_vram_gb", measured_vram, vram_guard=True)
_persist("measured_ram_gb", measured_ram)
# Stamp WHICH quantization this measurement describes, so a later
# config change invalidates it instead of silently outliving it.
if measured_vram:
_persist("measured_quant_sig",
self._quant_signature(resolved_name, cfgd))
# --- Anti-ratchet guard for the learned GPU-layer split -------------
# Never let a VRAM-CONTENDED (offloaded-to-CPU) load overwrite a HIGHER
......@@ -4912,6 +4930,25 @@ class MultiModelManager:
measured = self._measured_vram_gb.get(model_key)
if not measured:
_cm = cfg.get("measured_vram_gb")
# A measurement is only valid for the quantization it was taken
# under. Change the map and the stored number describes a model
# that no longer exists -- and because force_vram_update makes the
# measurement WIN, the stale figure keeps deciding placement.
#
# Measured 2026-10-29: Wan2.2-TI2V-5B carried measured_vram_gb=40.0
# from a load where only its text encoder was quantized (its map
# named text_encoder and vae; vae is conv-only, so bitsandbytes
# skipped it and an fp32 18.63 GB transformer loaded whole). Once
# the transformer is quantized the same pipeline is about 10 GB,
# but 40.0 would have gone on forcing CPU offload on a 25.3 GB card
# until some unlucky load happened to re-measure it.
_sig_now = self._quant_signature(resolved_name, cfg)
_sig_then = cfg.get("measured_quant_sig")
if _cm and _sig_now and _sig_then and _sig_then != _sig_now:
print(f" Ignoring measured_vram_gb={_cm} for '{model_key}': "
f"recorded under different quantization settings "
f"({_sig_then} != {_sig_now}); re-measuring this load.")
_cm = None
if _cm:
try:
measured = float(_cm)
......
# CoderAI - OpenAI-compatible API server
# Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""A VRAM measurement is only valid for the quantization it was taken under.
Change component_quantization and the stored number describes a model that no
longer exists. That would be harmless if the measurement were a hint, but
force_vram_update makes it WIN over the configured size, so a stale figure
goes on deciding placement.
Measured 2026-10-29: Wan2.2-TI2V-5B carried measured_vram_gb = 40.0 from a
load where only its text encoder was quantized -- its map named text_encoder
and vae, and vae is conv-only so bitsandbytes skipped it, leaving an fp32
18.63 GB transformer to load whole. Its components are 18.63 + 10.58 + 2.63 GB
on disk; quantized properly the same pipeline is roughly 10 GB. Without
invalidation, 40.0 would have kept forcing CPU offload on a 25.3 GB card until
some unlucky load happened to re-measure it.
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from codai.models import pipeline_cache
BASE = {"precision": "bf16", "load_in_4bit": True,
"component_quantization": {"text_encoder": "4bit", "vae": "4bit"}}
def _sig(cfg):
return pipeline_cache._signature("Wan-AI/Wan2.2-TI2V-5B-Diffusers", cfg)
def test_adding_a_component_changes_the_signature():
"""The real change: the transformer joins the quantized set."""
after = dict(BASE, component_quantization={
"text_encoder": "4bit", "vae": "4bit", "transformer": "4bit"})
assert _sig(BASE) != _sig(after)
def test_the_global_flag_changes_the_signature():
assert _sig(BASE) != _sig(dict(BASE, load_in_4bit=False))
def test_precision_changes_the_signature():
assert _sig(BASE) != _sig(dict(BASE, precision="fp16"))
def test_an_unrelated_edit_does_not():
"""Offload is a runtime placement decision, not a weight-size one; it must
not throw away a perfectly good measurement."""
assert _sig(BASE) == _sig(dict(BASE, offload_strategy="model",
used_vram_gb=14.0, load_mode="on-request"))
def test_the_manager_stamps_and_checks_the_signature():
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
src = open(os.path.join(root, "codai/models/manager.py"),
encoding="utf-8").read()
assert '_persist("measured_quant_sig"' in src, (
"the measurement is stored without recording which quantization it "
"describes, so it can never be invalidated")
assert "_sig_then != _sig_now" in src, (
"a measurement from a different quantization is still trusted")
def test_a_measurement_with_no_signature_is_still_trusted():
"""Entries measured before this existed must not all be discarded at
once -- that would re-measure every model on one upgrade."""
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
src = open(os.path.join(root, "codai/models/manager.py"),
encoding="utf-8").read()
block = src.split("_sig_then = cfg.get(\"measured_quant_sig\")", 1)[1][:400]
assert "_sig_then and" in block, (
"an entry without a recorded signature would be invalidated too")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment