Skip to content

[Offload] Support views - #786

Open
kylesayrs wants to merge 6 commits into
mainfrom
kylesayrs/offload-view
Open

[Offload] Support views#786
kylesayrs wants to merge 6 commits into
mainfrom
kylesayrs/offload-view

Conversation

@kylesayrs

Copy link
Copy Markdown
Collaborator

Purpose

  • Support MoE workflows where offloaded tensors can be onloaded as views
    • Writing to the viewed tensors causes them to write a separate and new offload

Changes

  • Add view_index to OffloadCache which tracks the views of each tensor relative to the base tensor
  • Add ref_counter which counts how many references there are to a base tensor via views. This can replaced by finalizers later if needed
    • This is primarily used by the disk cache to avoid early deletion of viewed base tensors
  • Add key argument to OffloadCache.onload which allows lookup of the view associated with the onloaded tensor

Disk Offloading Note

  • It is well documented that using get_slice followed by an indexing only loads the necessary memory view from disk, not the entire tensor

Testing

  • TODO: more unit testing

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fba1048b-4016-44c5-a54b-0d1991e06048

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Offload view-aware caching

Layer / File(s) Summary
View metadata and reference tracking
src/compressed_tensors/offload/cache/base.py, src/compressed_tensors/offload/utils.py
OffloadCache records view indices and reference counts, stores base tensors, and removes backing references when no longer used. index_from_view derives indices that reconstruct tensor views.
Keyed onload implementations and wiring
src/compressed_tensors/offload/cache/{base,cpu,device,disk}.py, src/compressed_tensors/offload/module.py
The onload API receives tensor names, and cache implementations apply recorded view indices while loading CPU, device, and disk-backed tensors.
Contract documentation and tests
src/compressed_tensors/offload/README.md, tests/test_offload/*
Documentation and cache tests use the keyed onload contract, and a utility test validates view reconstruction.

Pretrained loading initialization

Layer / File(s) Summary
Non-source rank initialization
src/compressed_tensors/offload/load.py
Non-source ranks no longer forcibly default tie_word_embeddings to false.

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

Possibly related PRs

Suggested labels: documentation

Suggested reviewers: etelis, brian-dellabetta, hdcharles

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding view support to offloaded tensors.
Description check ✅ Passed The description is directly related to the changeset and matches the added view/offload support.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kylesayrs/offload-view

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot added the documentation Improvements or additions to documentation label Jul 20, 2026

@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/compressed_tensors/offload/cache/base.py (2)

124-129: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Use __setitem__ to properly populate view metadata and reference counts.

from_mapping currently assigns directly to instance.offloaded_values, bypassing __setitem__. This means view_index and ref_counter are never populated during module initialization. As a result, subsequent onloading of views will fail (by erroneously returning the full base tensor) and disk cache cleanup will break due to missing reference counts.

Refactoring this to use instance[name] = tensor ensures all tracking logic executes correctly. As per path instructions, verify that offload hooks interact correctly with model loading.

♻️ Proposed fix
-        instance.offloaded_values = {
-            name: instance.offload(tensor) for name, tensor in mapping.items()
-        }
+        for name, tensor in mapping.items():
+            instance[name] = tensor
🤖 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 `@src/compressed_tensors/offload/cache/base.py` around lines 124 - 129, Update
from_mapping to populate the instance through __setitem__ by assigning each
mapping entry with instance[name] = tensor instead of directly replacing
offloaded_values. Preserve the existing offload behavior while ensuring
__setitem__ records view_index and ref_counter metadata for every initialized
tensor.

Source: Path instructions


222-249: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Fix AttributeError on None values and accurately track ref_counter for offloaded tensors.

This implementation has several critical issues that will lead to crashes and memory leaks:

  1. Crash on None: value._base will raise an AttributeError if value is None (which is permitted by the torch.Tensor | None signature and actively evaluated downstream).
  2. Key mismatch memory leak: ref_counter is incremented using the pre-offload value. For caches like DiskCache that create a new meta tensor during offloading, value (CPU tensor) and offloaded (Meta tensor) are different objects. __delitem__ decrements ref_counter[offloaded], leaving the original value pinned in memory forever and prematurely deleting the disk cache.
  3. Double counting on updates: ref_counter is unconditionally incremented at the top. If an existing key is updated in-place (torch.is_same_size is true), the reference count incorrectly inflates. Furthermore, old tensor references are not decremented when a key is completely overwritten.

Refactor __setitem__ to safely check for _base and to strictly manage ref_counter against the actual stored offloaded tensor object. As per path instructions, review cache utilities for no memory leaks.

🔒️ Proposed fix
     def __setitem__(self, key: Hashable, value: torch.Tensor | None):
         """
         Update the offloaded and onloaded values if the key exists, otherwise
         offload the value and add it to the cache.
         """
         # capture slice data
-        if value._base is not None:
+        if value is not None and getattr(value, "_base", None) is not None:
             base, view_index = index_from_view(value)
             self.view_index[key] = view_index
             value = base
         elif key in self.view_index:
             del self.view_index[key]
 
-        self.ref_counter[value] += 1
-
         # when onloading is disabled, parameters can be access and assigned directly
         if self.onloading_disabled:
+            old_value = self.offloaded_values.get(key, None)
+            if old_value is not None and old_value is not value:
+                self.ref_counter[old_value] -= 1
+                if self.ref_counter[old_value] <= 0:
+                    del self.ref_counter[old_value]
             self.offloaded_values[key] = value
+            if value is not None:
+                self.ref_counter[value] += 1
             return
 
         # if the key already exists, update with the new value
         offloaded = self.offloaded_values.get(key, None)
-        if offloaded is not None and torch.is_same_size(offloaded, value):
+        if offloaded is not None and value is not None and torch.is_same_size(offloaded, value):
             self.update_offload(offloaded, value)
 
             onloaded = self.keep_onloaded_values.get(offloaded, None)
             if onloaded is not None and onloaded is not offloaded:
                 onloaded.copy_(value)
 
         # if the key does not exist (or the value is None), offload the new value
         else:
-            self.offloaded_values[key] = self.offload(value)
+            if offloaded is not None:
+                self.ref_counter[offloaded] -= 1
+                if self.ref_counter[offloaded] <= 0:
+                    del self.ref_counter[offloaded]
+
+            new_offloaded = self.offload(value)
+            self.offloaded_values[key] = new_offloaded
+            if new_offloaded is not None:
+                self.ref_counter[new_offloaded] += 1
🤖 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 `@src/compressed_tensors/offload/cache/base.py` around lines 222 - 249,
Refactor __setitem__ to handle None before accessing _base, and manage
ref_counter using the exact tensor object stored in offloaded_values after
offload. Avoid incrementing counts for in-place updates, decrement the previous
stored tensor when replacing a key, and keep __delitem__ consistent so every
stored reference is released exactly once.

Source: Path instructions

🧹 Nitpick comments (1)
src/compressed_tensors/offload/cache/disk.py (1)

77-79: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename slice to avoid shadowing the Python builtin.

♻️ Proposed fix
-            onloaded = file.get_slice(weight_info["weight_name"])
-            slice = self.view_index.get(key, ...)
-            onloaded = onloaded[slice]  # this materializes the tensor from `get_slice`
+            onloaded = file.get_slice(weight_info["weight_name"])
+            view_slice = self.view_index.get(key, ...)
+            onloaded = onloaded[view_slice]  # this materializes the tensor from `get_slice`
🤖 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 `@src/compressed_tensors/offload/cache/disk.py` around lines 77 - 79, Rename
the local variable `slice` in the cache loading logic to a non-conflicting name,
and update the subsequent `onloaded[...]` indexing expression to use it.
🤖 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.

Inline comments:
In `@src/compressed_tensors/offload/cache/cpu.py`:
- Around line 20-31: Add an early None return in onload for both
src/compressed_tensors/offload/cache/cpu.py lines 20-31 and
src/compressed_tensors/offload/cache/device.py lines 32-44, then rename the
slicing variable from slice to view_slice and update its use. Correct each
onload docstring so the tensor parameter is documented as offloaded rather than
duplicating key.

---

Outside diff comments:
In `@src/compressed_tensors/offload/cache/base.py`:
- Around line 124-129: Update from_mapping to populate the instance through
__setitem__ by assigning each mapping entry with instance[name] = tensor instead
of directly replacing offloaded_values. Preserve the existing offload behavior
while ensuring __setitem__ records view_index and ref_counter metadata for every
initialized tensor.
- Around line 222-249: Refactor __setitem__ to handle None before accessing
_base, and manage ref_counter using the exact tensor object stored in
offloaded_values after offload. Avoid incrementing counts for in-place updates,
decrement the previous stored tensor when replacing a key, and keep __delitem__
consistent so every stored reference is released exactly once.

---

Nitpick comments:
In `@src/compressed_tensors/offload/cache/disk.py`:
- Around line 77-79: Rename the local variable `slice` in the cache loading
logic to a non-conflicting name, and update the subsequent `onloaded[...]`
indexing expression to use it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 51d715cc-0604-4d7d-b13d-d3e03bfec14b

📥 Commits

Reviewing files that changed from the base of the PR and between c98cc8d and ed210d5.

📒 Files selected for processing (10)
  • src/compressed_tensors/offload/README.md
  • src/compressed_tensors/offload/cache/base.py
  • src/compressed_tensors/offload/cache/cpu.py
  • src/compressed_tensors/offload/cache/device.py
  • src/compressed_tensors/offload/cache/disk.py
  • src/compressed_tensors/offload/load.py
  • src/compressed_tensors/offload/module.py
  • src/compressed_tensors/offload/utils.py
  • tests/test_offload/cache/helpers.py
  • tests/test_offload/test_utils.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • vllm-project/llm-compressor (manual)

Comment thread src/compressed_tensors/offload/cache/cpu.py
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
@kylesayrs
kylesayrs force-pushed the kylesayrs/offload-view branch from 365dd3a to 2f12bfd Compare August 12, 2026 16:58
@mergify

mergify Bot commented Aug 17, 2026

Copy link
Copy Markdown

Merge Protections

🔴 1 of 1 protections blocking · waiting on 👀 reviews

Protection Waiting on
🔴 Require one maintainer review 👀 reviews

🔴 Require one maintainer review

Waiting for any of

  • approved-reviews-by=HDCharles
  • approved-reviews-by=brian-dellabetta
  • approved-reviews-by=dsikka
  • approved-reviews-by=kylesayrs
This rule is failing.

All PRs must have at least one approving review from a maintainer before merging.

  • any of:
    • approved-reviews-by=HDCharles
    • approved-reviews-by=brian-dellabetta
    • approved-reviews-by=dsikka
    • approved-reviews-by=kylesayrs
  • #changes-requested-reviews-by = 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant