Skip to content

[WIP] [MoE] No-copy 3D loading - #2941

Closed
kylesayrs wants to merge 12 commits into
mainfrom
kylesayrs/no-copy-3d
Closed

[WIP] [MoE] No-copy 3D loading#2941
kylesayrs wants to merge 12 commits into
mainfrom
kylesayrs/no-copy-3d

Conversation

@kylesayrs

@kylesayrs kylesayrs commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Purpose

  • Accelerate packed checkpoint model load time by storing linear moe weights as views of the original 3d tensors
  • Accelerate algorithms like GPTQ/ AWQ for packed checkpoints by overlapping the load needed for splitting with the load needed to apply the algorithm

TODO

  • Break out inkling changes and saving changes into separate (assume that 3d models don't support repacking in this PR, save for later)
  • Write test for qwen3_vl_moe

Prerequisites

Changes

  • Replace logic which previoiusly copied weights to now use references

Testing

Before:

Linearizing experts: 100%|█| 48/48 [00:11<00:00,  4.34it/s]

After:

Linearizing experts: 100%|█| 48/48 [00:02<00:00, 16.31it/s]

I've confirmed that remaining runtime is purely cpu overhead

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 requested a review from dsikka as a code owner July 20, 2026 07:07
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Ignore keyword(s) in the title.

⛔ Ignored keywords (3)
  • WIP
  • DO NOT MERGE
  • DRAFT

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: c9f21908-824d-4037-8fde-6f393e3bd911

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kylesayrs/no-copy-3d

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.

@github-actions

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to llm-compressor. Please add the ready label when the PR is ready for review.

Note: This is required to complete the testing suite, please only add the label once the PR is code complete and local testing has been performed.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the MoE linearization and conversion mapping logic, introducing dynamic mapping registration, support for the inkling_mm_model architecture, and a zero-copy weight assignment mechanism in from_experts_module to optimize offloading. However, several critical issues need to be addressed: the use of the unsupported log_once parameter in loguru warnings will cause runtime crashes; registering save_mappings instead of mappings during saving discards other weight conversions; and the zero-copy logic fails to handle biases and transposed experts. Additionally, please restore the commented-out test cases and the load_quantizable_moe context manager to maintain test coverage.

Comment on lines 275 to 279
logger.warning(
"Linearized model performs a weight conversion during loading. This "
f"may lead to longer load times\n{converter}"
"may lead to longer load times",
log_once=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.

high

The log_once parameter is not supported by loguru.logger.warning and will raise a TypeError at runtime. Please remove log_once=True to prevent crashes.

            logger.warning(
                "Linearized model performs a weight conversion during loading. This "
                "may lead to longer load times"
            )

Comment on lines 300 to 304
logger.warning(
"Linearized model performs a weight conversion during saving. This "
f"may lead to longer save times\n{converter}"
"may lead to longer save times",
log_once=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.

high

The log_once parameter is not supported by loguru.logger.warning and will raise a TypeError at runtime. Please remove log_once=True to prevent crashes.

            logger.warning(
                "Linearized model performs a weight conversion during saving. This "
                "may lead to longer save times"
            )

Comment on lines +306 to +307
model._weight_conversions = save_mappings
register_checkpoint_conversion_mapping(model_type, save_mappings, overwrite=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.

high

Assigning and registering save_mappings instead of mappings will cause the model to lose all other weight conversions during saving. Please use mappings instead.

Suggested change
model._weight_conversions = save_mappings
register_checkpoint_conversion_mapping(model_type, save_mappings, overwrite=True)
model._weight_conversions = mappings
register_checkpoint_conversion_mapping(model_type, mappings, overwrite=True)

Comment on lines +152 to +176
# zero copy: remove weight to avoid offloading
for index in range(self.num_experts):
expert: ExpertMLP = self[index]
expert.copy_from_experts_module(experts, index)
if hasattr(expert, "gate_proj"):
del expert.gate_proj.weight
del expert.up_proj.weight
del expert.down_proj.weight

# copy offloading from original
offload_kwargs = get_cache_init_kwargs(experts)
for module in self.modules():
offload_module(module, **offload_kwargs)

# zero copy: assign linear weights as a view
with disable_onloading():
for index in range(self.num_experts):
expert: ExpertMLP = self[index]
if hasattr(expert, "gate_proj"):
expert.gate_proj._parameters["weight"] = experts.gate_up_proj[
index, : self.intermediate_size
]
expert.up_proj._parameters["weight"] = experts.gate_up_proj[
index, self.intermediate_size :
]
expert.down_proj._parameters["weight"] = experts.down_proj[index]

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.

high

The new from_experts_module implementation has two major issues:

  1. It completely ignores biases. If the model has biases enabled, they will remain on the "meta" device or uninitialized, causing runtime crashes or incorrect outputs.
  2. It ignores experts.is_transposed. If the experts are transposed, slicing and assigning the weights directly will result in incorrect shapes and weights.

We should update this block to correctly handle both transposition and biases.

        # zero copy: remove weight and bias to avoid offloading
        for index in range(self.num_experts):
            expert: ExpertMLP = self[index]
            if hasattr(expert, "gate_proj"):
                del expert.gate_proj.weight
                if experts.has_bias:
                    del expert.gate_proj.bias
            del expert.up_proj.weight
            if experts.has_bias:
                del expert.up_proj.bias
            del expert.down_proj.weight
            if experts.has_bias:
                del expert.down_proj.bias

        # copy offloading from original
        offload_kwargs = get_cache_init_kwargs(experts)
        for module in self.modules():
            offload_module(module, **offload_kwargs)

        # zero copy: assign linear weights and biases as a view
        with disable_onloading():
            for index in range(self.num_experts):
                expert: ExpertMLP = self[index]
                if not experts.is_transposed:
                    gate_up_proj = experts.gate_up_proj[index]
                    down_proj = experts.down_proj[index]
                else:
                    gate_up_proj = experts.gate_up_proj[index].T
                    down_proj = experts.down_proj[index].T

                if hasattr(expert, "gate_proj"):
                    expert.gate_proj._parameters["weight"] = gate_up_proj[: self.intermediate_size]
                    if experts.has_bias:
                        expert.gate_proj._parameters["bias"] = experts.gate_up_proj_bias[
                            index, : self.intermediate_size
                        ]
                expert.up_proj._parameters["weight"] = gate_up_proj[self.intermediate_size :]
                if experts.has_bias:
                    expert.up_proj._parameters["bias"] = experts.gate_up_proj_bias[
                        index, self.intermediate_size :
                    ]
                expert.down_proj._parameters["weight"] = down_proj
                if experts.has_bias:
                    expert.down_proj._parameters["bias"] = experts.down_proj_bias[index]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

  1. I agree with the biases part
  2. is_transposed is handled by the weight converter and at runtime. At this point, it's standardized

Comment on lines +118 to +132
# with load_quantizable_moe(model_cls):
from compressed_tensors.offload import set_onload_device

from llmcompressor.utils import load_context

with load_context(model_cls):
model2 = model_cls.from_pretrained(
model_stub,
device_map="auto_offload",
max_memory={},
offload_folder="offload_folder",
)
set_onload_device(model2, "cuda")
# with load_quantizable_moe(model_cls):
# model2 = model_cls.from_pretrained(model_stub, device_map="cuda")

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.

high

The load_quantizable_moe context manager is commented out and replaced with load_context. This means the test is no longer verifying the behavior of load_quantizable_moe. Please restore the original context manager to ensure the feature is properly tested.

Suggested change
# with load_quantizable_moe(model_cls):
from compressed_tensors.offload import set_onload_device
from llmcompressor.utils import load_context
with load_context(model_cls):
model2 = model_cls.from_pretrained(
model_stub,
device_map="auto_offload",
max_memory={},
offload_folder="offload_folder",
)
set_onload_device(model2, "cuda")
# with load_quantizable_moe(model_cls):
# model2 = model_cls.from_pretrained(model_stub, device_map="cuda")
with load_quantizable_moe(model_cls):
model2 = model_cls.from_pretrained(model_stub, device_map="cuda")

Comment on lines +67 to +90
# (
# "inference-optimization/DSV4-tiny-empty",
# [
# "model.layers.0.mlp.experts.2.up_proj.weight",
# "model.layers.1.mlp.experts.0.gate_proj.weight",
# "model.layers.2.mlp.experts.1.down_proj.weight",
# ],
# ),
# (
# "inference-optimization/Qwen3-1.6B-A0.9B",
# [
# "model.layers.0.mlp.experts.2.up_proj.weight",
# "model.layers.1.mlp.experts.0.gate_proj.weight",
# "model.layers.2.mlp.experts.1.down_proj.weight",
# ],
# ),
# (
# "inference-optimization/GLM-5.2-0.8B-A0.8B",
# [
# "model.layers.2.mlp.experts.2.up_proj.weight",
# "model.layers.3.mlp.experts.0.gate_proj.weight",
# "model.layers.4.mlp.experts.1.down_proj.weight",
# ],
# ),

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.

medium

These test cases for DeepSeek V4, Qwen3, and GLM models have been commented out, which disables test coverage for these architectures. Please restore them to ensure no regressions are introduced.

        (
            "inference-optimization/DSV4-tiny-empty",
            [
                "model.layers.0.mlp.experts.2.up_proj.weight",
                "model.layers.1.mlp.experts.0.gate_proj.weight",
                "model.layers.2.mlp.experts.1.down_proj.weight",
            ],
        ),
        (
            "inference-optimization/Qwen3-1.6B-A0.9B",
            [
                "model.layers.0.mlp.experts.2.up_proj.weight",
                "model.layers.1.mlp.experts.0.gate_proj.weight",
                "model.layers.2.mlp.experts.1.down_proj.weight",
            ],
        ),
        (
            "inference-optimization/GLM-5.2-0.8B-A0.8B",
            [
                "model.layers.2.mlp.experts.2.up_proj.weight",
                "model.layers.3.mlp.experts.0.gate_proj.weight",
                "model.layers.4.mlp.experts.1.down_proj.weight",
            ],
        ),

Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

The quality checks have failed. Please run make style and make quality under
the root directory to adddress the lint failures. You will need to install the
dev optional install to get the required linting packages:
https://github.com/vllm-project/llm-compressor/blob/main/CONTRIBUTING.md

Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
@mergify mergify Bot removed the quality-failed label Jul 21, 2026
@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

The quality checks have failed. Please run make style and make quality under
the root directory to adddress the lint failures. You will need to install the
dev optional install to get the required linting packages:
https://github.com/vllm-project/llm-compressor/blob/main/CONTRIBUTING.md

Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
@mergify

mergify Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The quality checks have failed. Please run make style and make quality under
the root directory to adddress the lint failures. You will need to install the
dev optional install to get the required linting packages:
https://github.com/vllm-project/llm-compressor/blob/main/CONTRIBUTING.md

@mergify

mergify Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

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
  • approved-reviews-by=yiliu30
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
    • approved-reviews-by=yiliu30
  • #changes-requested-reviews-by = 0

@kylesayrs kylesayrs mentioned this pull request Aug 19, 2026
@mergify

mergify Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @kylesayrs.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@kylesayrs

Copy link
Copy Markdown
Collaborator Author

Rather than add lots of complex code, let's just linearize moes on the fly. Users will have to eat the cost of up-front linearization if the sequential targets are smaller, but that should be an uncommon case
#3077

@kylesayrs kylesayrs closed this Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants