Skip to content

[None][feat] Add an opt-in raw-weight cache to the HF weight loader - #16054

Merged
QiJune merged 3 commits into
NVIDIA:mainfrom
sunnyqgg:hf_weight_cache
Jul 13, 2026
Merged

[None][feat] Add an opt-in raw-weight cache to the HF weight loader#16054
QiJune merged 3 commits into
NVIDIA:mainfrom
sunnyqgg:hf_weight_cache

Conversation

@sunnyqgg

@sunnyqgg sunnyqgg commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Independent optimization split out of #15777 per review. Workers that load the same checkpoint repeatedly (e.g. across LLM instances sharing one MPI worker pool) can keep the raw checkpoint tensors in CPU RAM instead of re-reading and re-deserializing them.

  • Off by default; enable with TRTLLM_HF_WEIGHT_CACHE=1. The default path pays nothing (no fingerprinting).
  • Invalidation: keyed by file fingerprints (abs path, size, mtime_ns) — any checkpoint change invalidates.
  • Memory: LRU bounded by TRTLLM_HF_WEIGHT_CACHE_MAX_ENTRIES (default 1). Eviction happens BEFORE the new load so CPU never holds the old cached and the new loading weights at once (a ~2x transient peak otherwise).
  • Correctness: hits return a fresh ConsumableWeightsDict wrapper sharing the read-only tensors; the safetensors hit path joins the same local MPI barrier the miss path is about to enter (rank-local caches can diverge after eviction). One _with_weight_cache() wrapper serves both the safetensors and bin/pth paths.

Measured

Qwen3-235B-A22B nvfp4 on 4xB200: warm reload 106.6s -> 76.2s with the cache (page-cache-warm baseline); DeepSeek-V3-Lite accuracy suites show the same per-case reload saving when workers are shared.

Companion PRs

Test plan

  • Unit tests: hit/miss/eviction-before-load/fingerprint invalidation (8 tests)
  • CI

Summary by CodeRabbit

  • New Features

    • Added an optional cache for loaded checkpoint weights to speed up repeated loading of the same files.
    • Added environment-based controls to enable caching and limit how many checkpoints are kept.
  • Bug Fixes

    • Improved synchronization during weight loading to reduce the risk of rank mismatches and deadlocks.
    • Ensured older cached weights are evicted before new ones load when the cache is full.
  • Tests

    • Added coverage for cache reuse, cache eviction, and the default uncached behavior.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an opt-in LRU cache for raw HuggingFace checkpoint weight tensors, keyed by file path/size/mtime, controlled via environment variables. Refactors safetensors/bin loading through a new _with_weight_cache wrapper with MPI barrier synchronization on hits and eviction-before-load on misses. Adds corresponding unit tests.

Changes

HF Weight Loader Caching

Layer / File(s) Summary
Cache state and eviction machinery
tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py
Adds threading/OrderedDict imports, module-level cache config (env vars, lock, LRU store), and methods for enabling/disabling the cache, computing capacity, generating fingerprint-based keys, evicting LRU entries, and storing/retrieving cached weights.
Cache-aware loading wrapper and safetensors integration
tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py
Adds _with_weight_cache wrapper handling hit/miss logic with optional MPI barrier on hits and eviction before misses; refactors safetensors/bin loading to route through it, adding _prefetch_and_load for memory-heuristic prefetching with an unconditional barrier.
Cache behavior unit tests
tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py
Adds an autouse fixture clearing the weight cache per test, plus tests for cache reuse, eviction-before-load ordering, and default-disabled caching.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HfWeightLoader
  participant WithWeightCache as _with_weight_cache
  participant WeightCache as _WEIGHT_CACHE
  participant PrefetchLoad as _prefetch_and_load

  HfWeightLoader->>WithWeightCache: request weights (cache key)
  WithWeightCache->>WeightCache: lookup key
  alt cache hit
    WeightCache-->>WithWeightCache: cached weights
    WithWeightCache->>WithWeightCache: optional MPI barrier (barrier_on_hit)
    WithWeightCache-->>HfWeightLoader: ConsumableWeightsDict
  else cache miss
    WithWeightCache->>WeightCache: evict LRU entries if needed
    WithWeightCache->>PrefetchLoad: load_fn()
    PrefetchLoad->>PrefetchLoad: prefetch heuristic + MPI barrier
    PrefetchLoad->>PrefetchLoad: _load_weights_in_parallel
    PrefetchLoad-->>WithWeightCache: loaded weights
    WithWeightCache->>WeightCache: store weights
    WithWeightCache-->>HfWeightLoader: ConsumableWeightsDict
  end
Loading

Suggested reviewers: yuxianq, pcastonguay, syuoni

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly states the main change and follows the requested [ticket][type] style.
Description check ✅ Passed The description covers the change, behavior, and test plan, with only minor deviations from the template.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (3)
tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py (1)

144-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Annotate the load_fn parameter.

load_fn is unannotated; it's invoked as load_fn() and returns a ConsumableWeightsDict. Add a precise Callable type so mypy/pyright can check callers.

♻️ Proposed annotation
-    def _with_weight_cache(self, weight_files: List[str],
-                           use_consolidated: bool, barrier_on_hit: bool,
-                           load_fn) -> ConsumableWeightsDict:
+    def _with_weight_cache(
+            self, weight_files: List[str], use_consolidated: bool,
+            barrier_on_hit: bool,
+            load_fn: Callable[[], ConsumableWeightsDict]
+    ) -> ConsumableWeightsDict:

As per coding guidelines: "Always annotate functions" and "Prefer specifying argument types in Callables."

🤖 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 `@tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py` around lines 144
- 146, The _with_weight_cache method currently leaves load_fn untyped even
though it is called as a zero-argument loader returning a ConsumableWeightsDict.
Update the signature of _with_weight_cache in weight_loader.py to annotate
load_fn with an appropriate Callable type that reflects no inputs and a
ConsumableWeightsDict return, so type checkers can validate callers and the
existing return annotation stays consistent.

Source: Coding guidelines

tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py (2)

110-137: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No assertion that the cache-hit barrier is exercised.

Per the PR summary, "The safetensors hit path uses the same local MPI barrier behavior as the miss path" — i.e., _with_weight_cache calls local_mpi_barrier() on a hit when barrier_on_hit=True. This test drives exactly that scenario (safetensors hit) but never mocks/asserts local_mpi_barrier was invoked, so a regression that skips the barrier on hit (a distributed deadlock risk across ranks) wouldn't be caught here.

Consider patching tensorrt_llm._torch.models.checkpoints.hf.weight_loader.local_mpi_barrier and asserting it's called once on the second (second = loader.load_weights(...)) call.

🤖 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 `@tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py` around
lines 110 - 137, Add coverage for the safetensors cache-hit barrier path in
test_weight_cache_reuses_raw_weights_with_fresh_consumable_wrapper by mocking
local_mpi_barrier from HfWeightLoader’s weight_loader module and asserting it is
called once on the second load_weights invocation. Keep the existing assertions
around _load_weights_in_parallel and cached raw weight reuse, and verify the hit
path exercises the same barrier behavior as the miss path when barrier_on_hit is
enabled.

6-203: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fingerprint-invalidation test appears to be missing.

The PR objectives state unit tests cover "hit, miss, eviction-before-load, and fingerprint invalidation behavior," but this diff only adds tests for reuse-on-hit, eviction-before-load-on-miss, and disabled-by-default. There's no test that mutates a cached file's mtime/size and asserts the resulting cache key changes / triggers a miss (per PR summary: "Cache entries are invalidated using file fingerprints based on absolute path, size, and mtime_ns"). This is a coverage gap on a core correctness property of the cache (stale-data avoidance), so I'd rate current coverage as insufficient relative to the stated test matrix.

Suggest adding test_weight_cache_invalidated_on_fingerprint_change to tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py that touches/rewrites the safetensors file (changing size or mtime) between two load_weights() calls and asserts _load_weights_in_parallel is invoked twice.

As per path instructions, "Act as a QA engineer reviewing test changes and coverage for TensorRT-LLM ... suggest concrete list file names and whether coverage is sufficient, insufficient, or needs follow-up outside the PR."

🤖 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 `@tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py` around
lines 6 - 203, Coverage is insufficient because there is no test for cache
invalidation when a safetensors file fingerprint changes. Add a new test in
HfWeightLoader coverage, e.g.
test_weight_cache_invalidated_on_fingerprint_change, that mutates the checkpoint
file between two load_weights() calls by changing size or mtime and then
verifies _load_weights_in_parallel runs again instead of reusing the cached
entry. Use the existing HfWeightLoader and _WEIGHT_CACHE setup to assert the
fingerprint-based cache key changes and forces a miss.

Source: Path instructions

🤖 Prompt for all review comments with 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.

Nitpick comments:
In `@tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py`:
- Around line 144-146: The _with_weight_cache method currently leaves load_fn
untyped even though it is called as a zero-argument loader returning a
ConsumableWeightsDict. Update the signature of _with_weight_cache in
weight_loader.py to annotate load_fn with an appropriate Callable type that
reflects no inputs and a ConsumableWeightsDict return, so type checkers can
validate callers and the existing return annotation stays consistent.

In `@tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py`:
- Around line 110-137: Add coverage for the safetensors cache-hit barrier path
in test_weight_cache_reuses_raw_weights_with_fresh_consumable_wrapper by mocking
local_mpi_barrier from HfWeightLoader’s weight_loader module and asserting it is
called once on the second load_weights invocation. Keep the existing assertions
around _load_weights_in_parallel and cached raw weight reuse, and verify the hit
path exercises the same barrier behavior as the miss path when barrier_on_hit is
enabled.
- Around line 6-203: Coverage is insufficient because there is no test for cache
invalidation when a safetensors file fingerprint changes. Add a new test in
HfWeightLoader coverage, e.g.
test_weight_cache_invalidated_on_fingerprint_change, that mutates the checkpoint
file between two load_weights() calls by changing size or mtime and then
verifies _load_weights_in_parallel runs again instead of reusing the cached
entry. Use the existing HfWeightLoader and _WEIGHT_CACHE setup to assert the
fingerprint-based cache key changes and forces a miss.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2710e5a7-954e-4daa-83eb-68afe7807c40

📥 Commits

Reviewing files that changed from the base of the PR and between 4d1cb8f and b3444e2.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py
  • tests/unittest/_torch/models/checkpoints/hf/test_weight_loader.py

@sunnyqgg

sunnyqgg commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57985 [ run ] triggered by Bot. Commit: b3444e2 Link to invocation

Comment thread tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py Outdated
Comment thread tensorrt_llm/_torch/models/checkpoints/hf/weight_loader.py
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57985 [ run ] completed with state SUCCESS. Commit: b3444e2
/LLM/main/L0_MergeRequest_PR pipeline #46657 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@sunnyqgg
sunnyqgg requested a review from a team as a code owner July 7, 2026 14:17
@sunnyqgg
sunnyqgg requested a review from tomeras91 July 7, 2026 14:17
@sunnyqgg

sunnyqgg commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58015 [ run ] triggered by Bot. Commit: 27769d5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58015 [ run ] completed with state SUCCESS. Commit: 27769d5
/LLM/main/L0_MergeRequest_PR pipeline #46685 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@sunnyqgg

sunnyqgg commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

1 similar comment
@sunnyqgg

sunnyqgg commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58418 [ run ] triggered by Bot. Commit: 27769d5 Link to invocation

@sunnyqgg

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58543 [ run ] triggered by Bot. Commit: 27769d5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58418 [ run ] completed with state ABORTED. Commit: 27769d5

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58543 [ run ] completed with state SUCCESS. Commit: 27769d5
/LLM/main/L0_MergeRequest_PR pipeline #47143 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@sunnyqgg

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58576 [ run ] triggered by Bot. Commit: 27769d5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58576 [ run ] completed with state SUCCESS. Commit: 27769d5
/LLM/main/L0_MergeRequest_PR pipeline #47170 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@sunnyqgg

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58602 [ run ] triggered by Bot. Commit: 27769d5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58602 [ run ] completed with state FAILURE. Commit: 27769d5
/LLM/main/L0_MergeRequest_PR pipeline #47192 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@sunnyqgg

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58620 [ run ] triggered by Bot. Commit: 27769d5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58620 [ run ] completed with state FAILURE. Commit: 27769d5
/LLM/main/L0_MergeRequest_PR pipeline #47209 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

sunnyqgg added 3 commits July 10, 2026 08:29
Workers that load the same checkpoint repeatedly (e.g. across LLM
instances sharing one MPI worker pool) can keep the raw checkpoint
tensors in CPU RAM instead of re-reading and re-deserializing them:

- Off by default; enabled with TRTLLM_HF_WEIGHT_CACHE=1.
- Keyed by file fingerprints (abs path, size, mtime_ns) so any checkpoint
  change invalidates the entry.
- LRU with TRTLLM_HF_WEIGHT_CACHE_MAX_ENTRIES (default 1); eviction
  happens BEFORE the new load so CPU never holds the old cached and new
  loading weights at once (a ~2x peak otherwise).
- Cache hits return a fresh ConsumableWeightsDict wrapper sharing the
  read-only tensors; the safetensors hit path joins the same local MPI
  barrier the miss path is about to enter (rank-local caches can diverge
  after eviction).
- One _with_weight_cache() wrapper serves both the safetensors and
  bin/pth paths.

Covered by unit tests (hit/miss/eviction/fingerprint invalidation).
Split out of NVIDIA#15777 per review (independent optimization with its own
memory and invalidation considerations).

Signed-off-by: qgai <qgai@nvidia.com>
_with_weight_cache already evicts before load_fn() (the load-bearing
eviction for the memory peak); re-running it inside _cache_loaded_weights
was derivable-as-done work. _clear_weight_cache becomes a staticmethod
like its sibling helpers.

Signed-off-by: qgai <qgai@nvidia.com>
…ective divergence

Two review findings on the HF raw-weight cache (thanks @QiJune):

1. Shared-tensor mutation: the cache shares raw tensors across loads, and
   a consumer mutating one in place (e.g. Nemotron-H's preprocess applies
   exp_()/neg_() to a view of the raw tensor; .to(torch.float32) is a
   no-op for fp32 checkpoints) would poison every later cache hit. The
   cache now records a sampled fingerprint per stored tensor and
   re-verifies it on every hit: a mutated entry is detected, logged with
   the offending keys, dropped and reloaded from disk. Models with
   in-place preprocessing (currently Nemotron-H, per a mapper audit) thus
   effectively opt out of caching - every hit self-heals into a fresh
   load - until their preprocessing is made non-mutating.

2. Collective divergence: with divergent rank-local cache states the first
   collective was Barrier on a hit rank but Allreduce (inside
   _get_local_available_host_memory) on a miss rank - a deadlock. The hit
   path now mirrors the miss path's exact sequence (Allreduce then
   Barrier); the bin/pth path performs no collectives on either side. The
   barrier_on_hit parameter is renamed mirror_load_collectives to match.

Unit tests: in-place mutation between loads -> detected + clean reload;
recorded collective sequences for a forced hit and a forced miss are
identical ([allreduce, barrier]).

Signed-off-by: qgai <qgai@nvidia.com>
@sunnyqgg

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58677 [ run ] triggered by Bot. Commit: 37ecb41 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58677 [ run ] completed with state SUCCESS. Commit: 37ecb41
/LLM/main/L0_MergeRequest_PR pipeline #47263 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@sunnyqgg

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58741 [ run ] triggered by Bot. Commit: 37ecb41 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58741 [ run ] completed with state SUCCESS. Commit: 37ecb41
/LLM/main/L0_MergeRequest_PR pipeline #47323 completed with status: 'SUCCESS'

CI Report

Link to invocation

@QiJune
QiJune merged commit e9b8e91 into NVIDIA:main Jul 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants