Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions examples/hf_ptq/example_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,103 @@
SPECULATIVE_MODEL_LIST = ["Eagle", "Medusa"]


def patch_match_named_modules() -> None:
"""Make compressed_tensors ``match_named_modules`` O(modules) for exact-name targets.
``compress_model``/``decompress_model`` pass ~69k exact module names as ``targets``; the stock
nested ``for target in targets`` loop is O(modules x targets) (~100 min for Kimi) -- this is the
"Compressing model..." phase observed at load time. Bucket the exact-name (non-"re:") targets
into a set for O(1) membership; regex and class-name targets are still handled exactly as before.
The yielded module set is identical (verified on the full model: 69,300 matches, 0.10 s vs
~100 min).
Idempotent: re-applying is a no-op (it detects the already-patched function and returns). Shared
by hf_ptq.py and multinode_ptq.py so the speedup is applied wherever a compressed-tensors
checkpoint is loaded.
"""
try:
import compressed_tensors.utils.match as _m
except Exception:
return
Comment on lines +70 to +73

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Do not silently disable the optimization on arbitrary import failures.

Catching every exception makes this a no-op on broken/incompatible compressed-tensors imports, sending users back to the slow load path without an actionable error. Catch only an absent optional package; re-raise other failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/hf_ptq/example_utils.py` around lines 70 - 73, Update the import
handling in the compressed-tensors optimization setup to catch only the
exception indicating the optional package is absent; allow other import failures
from compressed_tensors.utils.match to propagate instead of silently returning.
Preserve the no-op behavior when compressed-tensors is genuinely unavailable.


_orig = _m.match_named_modules
# Idempotent guard: if we already patched, the bound name is our wrapper, not the original.
if getattr(_orig, "_modelopt_fast_match", False):
return

def fast_match_named_modules(model, targets, ignore=None, fused=None, warn_on_fail=False):
targets = list(targets or [])
ignore = ignore or []
# Fall back to the original for the rare/complex cases this fast path doesn't cover.
if fused is not None or any(not isinstance(t, str) for t in targets):
yield from _orig(model, targets, ignore, fused=fused, warn_on_fail=warn_on_fail)
return
name_set = {t for t in targets if not t.startswith("re:")}
re_targets = [t for t in targets if t.startswith("re:")]
# Determine which non-"re:" targets are class names actually present (usually 0-1).
class_names, has_linearbase = set(), False
for _, mod in model.named_modules():
for c in mod.__class__.__mro__:
class_names.add(c.__name__)
has_linearbase = has_linearbase or c.__name__ == "LinearBase"
class_targets = [
t for t in name_set if t in class_names or (t == "Linear" and has_linearbase)
]
unmatched = set(targets)
internal_module_cls = getattr(_m, "InternalModule", None)
for name, module in model.named_modules():
# Original is_match() excludes InternalModule from target matches; mirror exactly.
if internal_module_cls is not None and isinstance(module, internal_module_cls):
continue
mt = None
if name in name_set:
mt = name
else:
for t in class_targets:
if _m._match_class(module, t):
mt = t
break
if mt is None:
for t in re_targets:
if _m._match_name(name, t, None):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file and locate matcher-related symbols/usages.
git ls-files examples/hf_ptq/example_utils.py
ast-grep outline examples/hf_ptq/example_utils.py --view expanded || true
rg -n "_match_name|match_name|compressed_tensors|patch_match_named_modules" examples/hf_ptq/example_utils.py -n -C 3 || true

# Search the repository for any references to the matcher helper API.
rg -n "match_name\(|_match_name\(" -S . || true

Repository: NVIDIA/Model-Optimizer

Length of output: 5174


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file section directly if needed.
sed -n '1,220p' examples/hf_ptq/example_utils.py | cat -n

Repository: NVIDIA/Model-Optimizer

Length of output: 10829


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect upstream API documentation/source via web if local repo lacks the dependency source.
python3 - <<'PY'
print("No-op placeholder: awaiting shell output from repository inspection.")
PY

Repository: NVIDIA/Model-Optimizer

Length of output: 229


🌐 Web query:

compressed-tensors utils.match match_name _match_name current API

💡 Result:

In the current API of the compressed-tensors library, the module matching logic has been refactored significantly to improve performance and maintainability [1][2]. The older functions and internal naming conventions, such as _match_name, have been largely replaced or consolidated [3][2]. Key updates to the matching API include: - Consolidation into match_named_modules: Much of the logic previously scattered across various helper functions has been centralized into match_named_modules [1][2]. This function is designed to efficiently determine if a module or parameter name matches the targets or ignore lists defined in a quantization configuration [1]. - Performance Improvements: Recent updates (e.g., as of April 2026) have rewritten match_named_modules to optimize matching, moving away from nested loops (O(N*M) complexity) to more efficient lookup structures like sets and precompiled regexes [1]. This is particularly important for large models, such as Mixture-of-Experts (MoE) architectures, which may contain thousands of modules [1][4]. - Deprecation of Older Helpers: Older utilities like expand_target_names, is_target, find_name_or_class_matches, and _find_matches have been replaced by the consolidated matching logic [2]. If you are working with the library, you should favor using match_named_modules from src/compressed_tensors/utils/match.py rather than relying on legacy internal underscore-prefixed functions like _match_name, which are generally considered internal and subject to change or removal [3][1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for dependency pins and local imports that can reveal the expected API.
rg -n "compressed-tensors|compressed_tensors" -S pyproject.toml requirements*.txt .github examples || true

Repository: NVIDIA/Model-Optimizer

Length of output: 1348


🌐 Web query:

site:github.com/vllm-project/compressed-tensors _match_name match_name compressed_tensors.utils.match

💡 Result:

In the vllm-project/compressed-tensors library, match_name is a utility function used for matching module or tensor names against specified targets [1]. It is part of the library's utility infrastructure, specifically residing within the compressed_tensors.utils.match module [2][3]. The library utilizes several matching functions to handle module and parameter targeting, including match_name, match_named_modules, match_targets, and is_narrow_match [3][4][1]. These utilities are essential for operations like applying quantization configurations, dequantization, and managing model compression [3][1]. Recent developments in the library have focused on optimizing these matching processes. For instance, the match_named_modules function was recently refactored to replace inefficient quadratic O(N x M) logic with optimized lookup structures, significantly improving performance for large models such as MoE (Mixture of Experts) architectures [2][5]. The match_name function is often used within these broader workflows, such as in converter logic, to verify specific target matches when identifying quantizable tensors or dependencies [1].

Citations:


🌐 Web query:

current compressed-tensors matcher API match_name _match_name site:github.com/vllm-project/compressed-tensors

💡 Result:

In the compressed-tensors library, the primary API for module matching is match_named_modules, which is located in src/compressed_tensors/utils/match.py [1][2]. As of the current library structure, match_named_modules is the established function used to traverse model modules and match them against specified target or ignore criteria [1][3]. Historical development discussions have referenced internal helper methods or naming variations such as match_name, match_class, and _find_matches during refactoring efforts [4], but match_named_modules serves as the public-facing utility for this operation [1][3]. Recent updates, such as those in Pull Request #697, have optimized match_named_modules from an O(N×M) complexity to near-linear performance by implementing one-time lookup structures for target and ignore lists [1][3]. The function signature typically follows this pattern [2]: def match_named_modules( model: torch.nn.Module, targets: Iterable[str] | None, ignore: Iterable[str] | None = None,) -> Generator[tuple[str, torch.nn.Module]]: The library uses these utilities to facilitate the efficient application of quantization and compression configurations across model components [3][5].

Citations:


Use the public matcher helper for regex targets.

_m._match_name(...) is not part of the current matcher API, so re: targets can hit AttributeError here. Call _m.match_name(name, t, None) instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/hf_ptq/example_utils.py` at line 114, Update the matcher call in the
regex-target handling to use the public _m.match_name(name, t, None) helper
instead of the private _m._match_name symbol, preserving the existing matching
arguments and behavior.

mt = t
break
if mt is not None:
unmatched.discard(mt)
if not _m.is_match(name, module, ignore, fused=None):
yield name, module
if warn_on_fail:
import logging as _logging

for t in unmatched:
_logging.getLogger("compressed_tensors").warning(
f"Could not match `{t}` in instance of {model.__class__.__name__}"
)

fast_match_named_modules._modelopt_fast_match = True
_m.match_named_modules = fast_match_named_modules

# `compress_model` (and friends) did `from ...match import match_named_modules`, binding the name
# into THEIR module namespace at import time, so patching the `match` module attribute alone does
# not reach them -- confirmed via py-spy: compress_model still ran the original O(modules x
# targets) matcher despite the patch. Rebind every already-imported module that still holds the
# original ref. (Modules imported *after* this point pick up the fast version automatically.)
import contextlib
import sys

with contextlib.suppress(Exception):
# ensure the key importer is loaded so the sweep below can rebind it
import compressed_tensors.compressors.model_compressors.model_compressor # noqa: F401
rebound = []
for _modname, _mod in list(sys.modules.items()):
if _mod is None:
continue
if getattr(_mod, "match_named_modules", None) is _orig:
_mod.match_named_modules = fast_match_named_modules
rebound.append(_modname)
print(f"[match-speedup] patched match_named_modules; rebound importers: {rebound}", flush=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Make this multinode log rank-aware.

examples/hf_ptq/multinode_ptq.py invokes the patch during import on every rank, so each rank emits this message. Route it through the repository’s rank-zero logging helper.

As per coding guidelines, “Develop distributed code with rank-aware logging such as print_rank_0 or warn_rank_0.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/hf_ptq/example_utils.py` at line 150, The patched-module status
message in match_named_modules must be rank-aware because multinode_ptq.py
executes it on every rank. Replace the direct print with the repository’s
print_rank_0 helper, preserving the existing message content and flush behavior.

Source: Coding guidelines



def run_nemotron_vl_preview(
full_model,
tokenizer,
Expand Down
4 changes: 4 additions & 0 deletions examples/hf_ptq/hf_ptq.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
is_nemotron_vl,
load_mtp_weights,
needs_checkpoint_path_update,
patch_match_named_modules,
resolve_checkpoint_dir,
run_nemotron_vl_preview,
)
Expand Down Expand Up @@ -88,6 +89,9 @@
RAND_SEED = 1234


patch_match_named_modules()


def _kv_cfg_uses_constant_amax(kv_quant_cfg: list[dict[str, Any]]) -> bool:
"""Return True if this KV cfg pins ``use_constant_amax`` on the bmm quantizer.

Expand Down
7 changes: 6 additions & 1 deletion examples/hf_ptq/multinode_ptq.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import torch
import torch.nn as nn
from accelerate import Accelerator
from example_utils import build_quant_cfg, get_tokenizer
from example_utils import build_quant_cfg, get_tokenizer, patch_match_named_modules
from tqdm import tqdm
from transformers import AutoModelForCausalLM, PreTrainedTokenizer, PreTrainedTokenizerFast

Expand All @@ -48,6 +48,11 @@
# Enable HuggingFace checkpointing
mto.enable_huggingface_checkpointing()

# Speed up the compressed-tensors load (the "Compressing model..." phase): make match_named_modules
# O(modules) instead of O(modules x ~69k targets). Must run before any compressed-tensors load, so
# apply it at import time (mirrors hf_ptq.py).
patch_match_named_modules()


def parse_args():
"""Parse command line arguments."""
Expand Down
Loading