Skip to content

fix(dist): write back broadcast qparams to offload storage on non-src ranks - #3066

Open
xesdiny wants to merge 2 commits into
vllm-project:mainfrom
xesdiny:fix/broadcast-qparams-writeback
Open

fix(dist): write back broadcast qparams to offload storage on non-src ranks#3066
xesdiny wants to merge 2 commits into
vllm-project:mainfrom
xesdiny:fix/broadcast-qparams-writeback

Conversation

@xesdiny

@xesdiny xesdiny commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

broadcast_qparams_and_cleanup calls dist.broadcast on module attributes such as weight_scale, weight_zero_point, and weight_g_idx. When these tensors are stored in CPUCache (the common case with sequential pipeline auto_offload), getattr(module, name) returns a temporary onloaded GPU tensor — a new allocation each call. dist.broadcast modifies that temporary in-place, but the underlying CPU storage is never updated — leaving non-source ranks with stale or uninitialised quantization parameters.

Trigger conditions: multi-GPU DDP GPTQ with sequential pipeline and auto_offload=True, typically used to parallelize activation collection over large calibration sets. The quantization completes silently with no exception; the corruption only appears when inspecting saved checkpoints.

This PR adds an explicit update_offload_parameter writeback for non-source ranks after _wait_for_comms, which correctly flushes the broadcast result back to CPUCache storage.

Changes

  • src/llmcompressor/utils/dist.py: collect (module, name, param, broadcast_param) for non-src-rank entries before issuing async broadcasts; after comms complete, copy back and call update_offload_parameter (+40/-2 lines)
  • tests/llmcompressor/utils/test_broadcast_qparams_writeback.py: unit tests covering non-src writeback, src no-writeback, missing params, and multi-module cases
  • tests/llmcompressor/utils/test_cpucache_temporary_tensor.py (new): 3 unit tests proving CPUCache returns a new temporary CUDA tensor per getattr call and that in-place modification is lost without writeback
  • tests/llmcompressor/utils/test_broadcast_qparams_ddp_integration.py (new): 2-GPU integration test using real dist.broadcast + real CPUCache, demonstrating the bug (Phase A) and fix (Phase B) in a single distributed session

Empirical validation

Controlled A/B on 8× L20, Qwen3.5-122B-A10B, N=8192, seq=6144:

with fix without fix
rank 0 SCALE_CHECK ✅ CLEAN (total=37056) ⚠️ CORRUPTED nan=31924 neg=402
rank 1 SCALE_CHECK ✅ CLEAN ⚠️ CORRUPTED nan=840 inf=1 neg=3139
rank 4 SCALE_CHECK ✅ CLEAN ⚠️ CORRUPTED nan=3829 neg=3
all 8 ranks CLEAN multiple ranks CORRUPTED

Full details in issue #3119.

Fixes #3119

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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: Team

Run ID: 85e4e5d5-e553-4948-8b2f-794b974b1728

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

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.

@mergify

mergify Bot commented Aug 20, 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

@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 ensures that broadcasted quantization parameters are correctly written back to offload storage (CPUCache) on non-source ranks by calling update_offload_parameter after communication is complete. This resolves issues where the underlying CPU storage remains stale. Unit tests have been added to verify this behavior. The reviewer suggests guarding the broadcast logic with dist.is_initialized() to prevent potential crashes in non-distributed environments.

Comment thread src/llmcompressor/utils/dist.py Outdated
@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.

@mergify

mergify Bot commented Aug 20, 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

@xesdiny
xesdiny requested a review from dsikka as a code owner August 20, 2026 03:52
@mergify mergify Bot removed the quality-failed label Aug 20, 2026
@mergify

mergify Bot commented Aug 20, 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

@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from 999bd35 to 9506e3d Compare August 20, 2026 06:11
@mergify mergify Bot removed the quality-failed label Aug 20, 2026
@mergify

mergify Bot commented Aug 20, 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

@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from 9506e3d to 3aca174 Compare August 20, 2026 06:17
@mergify mergify Bot removed the quality-failed label Aug 20, 2026
@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from 3aca174 to 7d63da1 Compare August 20, 2026 06:23
@xesdiny

xesdiny commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review

@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 introduces writeback support for CPU-offloaded modules during quantization parameter broadcasting by calling update_offload_parameter on non-source ranks, and adds corresponding unit tests. The review feedback points out a potential issue where as_broadcastable(param) might return a copy of the tensor rather than a view; if so, the broadcasted values would not be written back to the offload storage. It is recommended to save and write back the tensor returned by as_broadcastable(param) to ensure correctness.

Comment thread src/llmcompressor/utils/dist.py Outdated
@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from 7d63da1 to 7e5316a Compare August 20, 2026 06:51
@xesdiny

xesdiny commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review

@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 updates broadcast_qparams_and_cleanup in src/llmcompressor/utils/dist.py to ensure that broadcasted quantization parameters are correctly written back to CPU-offloaded storage on non-source ranks using update_offload_parameter, and adds corresponding regression tests. The review feedback recommends appending the original param instead of broadcast_param to writeback_items to avoid potential shape or dtype mismatches, as the original parameter is already updated in-place by dist.broadcast.

Comment thread src/llmcompressor/utils/dist.py Outdated
@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from 7e5316a to c179dc3 Compare August 20, 2026 07:34
@xesdiny

xesdiny commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review

@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 updates broadcast_qparams_and_cleanup in src/llmcompressor/utils/dist.py to ensure that broadcasted quantization parameters are correctly written back to offload storage (such as CPUCache) on non-source ranks using update_offload_parameter. It also adds a comprehensive suite of regression tests to verify this behavior. The review feedback suggests caching dist.is_initialized() outside the loop to improve efficiency and defensively copying data back to the original parameter tensor if as_broadcastable returns a copy or a view, ensuring correctness under all circumstances.

Comment thread src/llmcompressor/utils/dist.py Outdated
Comment thread src/llmcompressor/utils/dist.py Outdated
@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from c179dc3 to e78e258 Compare August 20, 2026 08:12
@xesdiny

xesdiny commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review

@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 updates the broadcast_qparams_and_cleanup function in src/llmcompressor/utils/dist.py to ensure that CPU-offloaded modules correctly persist broadcast results back to offload storage on non-source ranks using update_offload_parameter. It also introduces regression tests to validate this behavior. The review feedback suggests optimizing the writeback logic by checking data_ptr() to avoid redundant copy operations when tensors share the same underlying storage, and adding unit tests to cover the code path where an explicit copy is required.

Comment thread src/llmcompressor/utils/dist.py Outdated
Comment thread tests/llmcompressor/utils/test_broadcast_qparams_writeback.py
@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from e78e258 to bca0a15 Compare August 20, 2026 08:23
@mergify

mergify Bot commented Aug 20, 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

@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from 262f741 to 6066531 Compare August 20, 2026 14:29
@xesdiny

xesdiny commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review

@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 updates broadcast_qparams_and_cleanup to ensure that non-source ranks correctly write back broadcasted quantization parameters to offload storage (such as CPUCache) using update_offload_parameter, and adds comprehensive regression tests. The feedback suggests using reshape_as instead of view_as when copying the broadcasted parameter back to avoid potential RuntimeErrors if the tensor is non-contiguous.

Comment thread src/llmcompressor/utils/dist.py Outdated
xesdiny added 2 commits September 1, 2026 16:28
… ranks

broadcast_qparams_and_cleanup calls dist.broadcast on module attributes
such as weight_scale, weight_zero_point, and weight_g_idx. When these
tensors are stored in CPUCache (the common case with sequential pipeline
auto_offload), getattr(module, name) returns a temporary onloaded GPU
tensor — a new allocation each call. dist.broadcast modifies that
temporary in-place, but the underlying CPU storage is never updated,
leaving non-source ranks with stale or uninitialised quantization
parameters. The quantization completes silently with no exception.

Fix: collect (module, name, param, broadcast_param) tuples for non-src
ranks before issuing async broadcasts. After _wait_for_comms, copy back
if storage differs and call update_offload_parameter unconditionally to
flush the broadcast result to CPUCache storage.

Closes vllm-project#3119

Signed-off-by: xesdiny <xesdiny@gmail.com>
…cleanup writeback

Three test files covering different levels of the bug:

- test_broadcast_qparams_writeback.py: mock-based unit tests verifying
  the control-flow logic (update_offload_parameter called at the correct
  time for non-src ranks, not called for src rank, missing params
  handled, multi-module case).

- test_cpucache_temporary_tensor.py (1 GPU, no dist): root-cause proof
  that CPUCache.__getattr__ returns a new temporary CUDA tensor on each
  call. Demonstrates that in-place modification without writeback is
  silently lost, and that update_offload_parameter correctly persists it.

- test_broadcast_qparams_ddp_integration.py (2 GPU, NCCL): end-to-end
  integration test with real dist.broadcast and real CPUCache. Phase A
  confirms the upstream bug (non-src rank keeps stale weight_scale after
  broadcast). Phase B confirms the fix propagates correct values to all
  ranks. Modules are created before dist.init_process_group() to force
  CPUCache selection.

Signed-off-by: xesdiny <xesdiny@gmail.com>
@xesdiny
xesdiny force-pushed the fix/broadcast-qparams-writeback branch from f6646c7 to bfc9e1c Compare September 1, 2026 08:30
@xesdiny

xesdiny commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist review

@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 updates broadcast_qparams_and_cleanup in src/llmcompressor/utils/dist.py to explicitly write back broadcasted quantization parameters to CPU-offloaded storage on non-source ranks, preventing parameters from being lost when dist.broadcast modifies temporary onloaded tensors. It also adds comprehensive integration, regression, and root-cause tests. The review feedback suggests replacing torch.accelerator APIs (set_device_index and is_available) with their torch.cuda equivalents in the new tests to maintain backward compatibility with PyTorch versions older than 2.4.

rank = int(os.environ["RANK"])
local_rank = int(os.environ["LOCAL_RANK"])
device = torch.device(f"cuda:{local_rank}")
torch.accelerator.set_device_index(local_rank)

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

Using torch.accelerator.set_device_index requires PyTorch 2.4 or newer. To maintain backward compatibility with older PyTorch versions (e.g., 2.1 to 2.3) which are commonly used in production environments, it is safer to use torch.cuda.set_device.

Suggested change
torch.accelerator.set_device_index(local_rank)
torch.cuda.set_device(local_rank)

This is the key property that makes dist.broadcast's in-place write
ineffective: it writes to a temporary that is immediately discarded.
"""
if not torch.accelerator.is_available():

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

Using torch.accelerator.is_available requires PyTorch 2.4 or newer. To ensure backward compatibility with older PyTorch versions, use torch.cuda.is_available() instead.

Suggested change
if not torch.accelerator.is_available():
if not torch.cuda.is_available():


This is the bug: non-source ranks end up with stale weight_scale.
"""
if not torch.accelerator.is_available():

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

Using torch.accelerator.is_available requires PyTorch 2.4 or newer. To ensure backward compatibility with older PyTorch versions, use torch.cuda.is_available() instead.

Suggested change
if not torch.accelerator.is_available():
if not torch.cuda.is_available():


After calling it, subsequent getattr returns the new value.
"""
if not torch.accelerator.is_available():

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

Using torch.accelerator.is_available requires PyTorch 2.4 or newer. To ensure backward compatibility with older PyTorch versions, use torch.cuda.is_available() instead.

Suggested change
if not torch.accelerator.is_available():
if not torch.cuda.is_available():

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.

[Bug]: broadcast_qparams_and_cleanup silently corrupts weight_scale on non-src ranks when modules use CPUCache offload

1 participant