Skip to content

Grouped Linear CPU Overhead Reductions By Removing Unnecessary Python Logic - #3613

Open
zhongbozhu wants to merge 10 commits into
NVIDIA:mainfrom
zhongbozhu:optimize/grouped-linear-cpu-overhead
Open

zhongbozhu wants to merge 10 commits into
NVIDIA:mainfrom
zhongbozhu:optimize/grouped-linear-cpu-overhead

Conversation

@zhongbozhu

@zhongbozhu zhongbozhu commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Please include a brief summary of the changes, relevant motivation and context.

Partly Fixes #2897

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

Please list the changes introduced in this PR:

  • Change A
  • Change B

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@github-actions github-actions Bot added the community-contribution PRs from external contributor outside the core maintainers, representing community-driven work. label Oct 2, 2026
@zhongbozhu
zhongbozhu force-pushed the optimize/grouped-linear-cpu-overhead branch from d75052c to 033def7 Compare October 6, 2026 00:26
Note: please use `restore_from_func_ctx` instead if you are restoring tensors from a function context to make sure tensor_objects is detached and its memory can be freed
"""
tensor_objects = []
cursor = 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch, it turns out this logic was O(n^2) since slicing a list involves copying entries. We can still run afoul of n^2 if we have many QuantizedTensorStorages, but fixing that would require a larger structural change.

Comment on lines +776 to +782
grouped = is_op_fuser_grouped_tensor_path_supported(
FP8GlobalStateManager.get_fp8_recipe(), dtype
)
input_quantizers = (self._input_quantizers[0],) if grouped else self._input_quantizers
grad_output_quantizers = (
(self._grad_output_quantizers[0],) if grouped else self._grad_output_quantizers
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm wary of this approach since the decision between the grouped tensor impl and split-quantize impl is an internal implementation detail within GroupedLinear. If we apply the grouped MLP fusion, this conditional logic is irrelevant. Instead of exposing internal implementation details in pre_fuser_forward, perhaps it would be better to change our convention to only respect the first quantizer. pre_fuser_forward only sets three quantizers (input, weight, grad output), and within the split-quantize impl we manually set all of the quantizers to match the first. I'm not fully comfortable with this approach, but it's already the case that we assume quantizers are uniform.

Comment on lines +859 to +869
# Cache object references for this recipe generation, not tensor workspaces.
# Keep the per-expert slots for legacy split quantization and checkpoint metadata.
self._input_quantizers = tuple(
self.get_quantizer("forward", 2 * i) for i in range(self.num_groups)
)
self._weight_quantizers = tuple(
self.get_quantizer("forward", 2 * i + 1) for i in range(self.num_groups)
)
self._grad_output_quantizers = tuple(
self.get_quantizer("backward", i) for i in range(self.num_groups)
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of caching, how about we only iterate over quantizers in the split-quantize path? This way the grouped tensor path only requires accessing a few quantizers. I'm worried how complicated grouped linear is and that adding unnecessary nonlocal logic will make it more challenging for humans to read.

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.

The code changes after removing the cache feels harder to read for me :)

Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Comment thread transformer_engine/pytorch/module/grouped_linear.py Outdated
Comment thread tests/pytorch/test_quantized_tensor.py Outdated
Comment thread tests/pytorch/test_grouped_linear.py Outdated
Comment thread tests/pytorch/test_grouped_linear.py Outdated
Comment thread tests/pytorch/test_grouped_linear.py Outdated
Comment thread tests/pytorch/test_grouped_linear.py Outdated
@zhongbozhu
zhongbozhu force-pushed the optimize/grouped-linear-cpu-overhead branch from 033def7 to a619742 Compare October 12, 2026 20:20
zhongbozhu and others added 7 commits October 15, 2026 11:59
Signed-off-by: zhongboz <zhongboz@nvidia.com>
Signed-off-by: zhongboz <zhongboz@nvidia.com>
Signed-off-by: zhongboz <zhongboz@nvidia.com>
Signed-off-by: zhongboz <zhongboz@nvidia.com>
Signed-off-by: zhongboz <zhongboz@nvidia.com>
@zhongbozhu
zhongbozhu force-pushed the optimize/grouped-linear-cpu-overhead branch from a619742 to c31c2f6 Compare October 15, 2026 21:23
Signed-off-by: zhongboz <zhongboz@nvidia.com>
@zhongbozhu
zhongbozhu marked this pull request as ready for review October 16, 2026 06:05
@zhongbozhu
zhongbozhu requested a review from ksivaman as a code owner October 16, 2026 06:05
@greptile-apps

greptile-apps Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] Refactors grouped linear operations and quantizer configuration.

Restore the required FP8 weight transpose before merging so training on Hopper does not fail during backward.

Findings

  1. P1 FP8 backward fails on Hopper ▶

Summary

This PR reduces Python work around grouped linear calls.

  • GroupedLinear prepares only what its active execution path needs.
  • Bias gradients run during the main backward, separate from delayed weight gradients.
  • Distributed grouped weights are gathered only for each computation.
  • Saved quantized tensors restore without repeatedly slicing the saved sequence.

Reviews (2) · Last reviewed commit: "resolve comments" · Reviewed by Greptile

Signed-off-by: zhongboz <zhongboz@nvidia.com>
@zhongbozhu

Copy link
Copy Markdown
Collaborator Author

/te-ci pytorch

Comment on lines +980 to +982
required_columnwise = True if columnwise_usage else None
if w._quantizer is not None:
w._quantizer.set_usage(rowwise=True, columnwise=required_columnwise)

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.

P1 FP8 backward fails on Hopper

On Hopper, a discrete FP8 weight created under torch.no_grad() has no transpose buffer. Removing w.update_usage(...) means _get_discrete_weights_for_gemm now changes only quantizer flags when training later needs input gradients. Those flags do not build the missing buffer. Forward succeeds, but backward calls grouped GEMM with layout="NN" and fails because the weight has no columnwise data.

Keep the call that builds required layouts for existing quantized weights.

Knowledge Base Used:

This branch has not been deployed

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

Labels

community-contribution PRs from external contributor outside the core maintainers, representing community-driven work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PyTorch] Reduce CPU overhead in grouped MLP block

2 participants