Skip to content

[JAX] Hopper BF16 grouped GEMM v2 support - #3083

Merged
jberchtold-nvidia merged 7 commits into
NVIDIA:mainfrom
jberchtold-nvidia:jberchtold/hopper-bf16-gmm
Sep 9, 2026
Merged

jberchtold-nvidia merged 7 commits into
NVIDIA:mainfrom
jberchtold-nvidia:jberchtold/hopper-bf16-gmm

Conversation

@jberchtold-nvidia

@jberchtold-nvidia jberchtold-nvidia commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds support for Hopper BF16 grouped GEMM

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

  • Hopper BF16 grouped GEMM

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

Signed-off-by: Jeremy Berchtold <jberchtold@nvidia.com>
@jberchtold-nvidia
jberchtold-nvidia marked this pull request as draft June 4, 2026 20:48
@jberchtold-nvidia

Copy link
Copy Markdown
Collaborator Author

/te-ci L1 jax

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR enables the JAX V2 grouped-GEMM path for non-quantized BF16 workloads on Hopper and adapts alpha/beta metadata to the architecture-specific native interface.

  • Expands BF16 V2 routing from SM100+ to SM90+ while retaining the SM100+ MXFP8 restriction.
  • Uses singleton alpha/beta buffers on Hopper and per-group buffers on supported newer devices.
  • Updates the native FFI wrappers to derive alpha/beta tensor sizes from their actual buffers.
  • Extends abstract shape validation to accept both supported cardinalities.

Confidence Score: 4/5

The PR is not yet safe to merge because mixed Hopper/Blackwell processes can fail grouped-GEMM native validation on Blackwell devices.

Alpha/beta cardinality is selected using the minimum capability across the process, while the native implementation validates it using the GPU executing each call; an SM90/SM100 process therefore supplies singleton buffers to an SM100 kernel that requires per-group values. The earlier bias-plus-ragged-last-dim finding also remains outstanding because the current routing still does not reject that combination before V2 execution.

Files Needing Attention: transformer_engine/jax/cpp_extensions/gemm.py

Important Files Changed

Filename Overview
transformer_engine/jax/cpp_extensions/gemm.py Adds Hopper BF16 routing and architecture-dependent alpha/beta sizing, but the process-wide capability choice conflicts with native per-device validation on heterogeneous GPU sets.
transformer_engine/jax/csrc/extensions/gemm.cpp Propagates the actual alpha/beta buffer cardinalities into the native TensorWrapper objects.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[JAX grouped_gemm] --> B{Minimum visible SM}
  B -->|SM90 present| C[Singleton alpha and beta]
  B -->|All SM100+| D[Per-group alpha and beta]
  C --> E[FFI dispatch on selected GPU]
  D --> E
  E --> F{Current device SM}
  F -->|SM90| G[Native expects singleton]
  F -->|SM100| H[Native expects per-group]
  C -. heterogeneous SM90/SM100 mismatch .-> H
Loading

Reviews (3): Last reviewed commit: "[pre-commit.ci] auto fixes from pre-comm..." | Re-trigger Greptile

Comment thread transformer_engine/jax/cpp_extensions/gemm.py
Comment on lines +2421 to +2422
if out_last_dims is not None:
raise NotImplementedError("V2 grouped GEMM bias is not supported for ragged last dims")

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.

P2 Runtime error surfaces after kernel execution for bias + ragged-last-dims

When has_bias=True and out_last_dims is not None, _is_v2_grouped_gemm_supported still returns True for BF16 on SM90+ (no gate for this combination), so the full V2 kernel is dispatched before _add_grouped_gemm_bias raises NotImplementedError. The check should be moved upstream — either into _is_v2_grouped_gemm_supported (returning False to fall back to V1) or as an early guard in grouped_gemm before the FFI bind.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This will be especially bad if NVTE_JAX_ENFORCE_V2_GROUPED_GEMM=1 because v1 fallback is disabled

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.

Exactly right — that makes the bug significantly worse. With NVTE_JAX_ENFORCE_V2_GROUPED_GEMM=1, _should_enforce_v2_grouped_gemm() returns True, use_v2_ffi is forced True, and the (bias + ragged last dims) combination crashes inside _add_grouped_gemm_bias with NotImplementedError — with no V1 fallback available and no useful error message referencing the env var.

The clean fix is to gate this combination in _is_v2_grouped_gemm_supported. The function already accepts has_bias; it just needs out_last_dims (or a boolean flag) added so it can return early with a clear message:

Suggested change
if out_last_dims is not None:
raise NotImplementedError("V2 grouped GEMM bias is not supported for ragged last dims")
def _is_v2_grouped_gemm_supported(
scaling_mode: ScalingMode,
dtype: jnp.dtype,
has_bias: bool,
lhs_shape: Tuple[int, ...],
rhs_shape: Tuple[int, ...],
lhs_axis_boundary: int,
rhs_axis_boundary: int,
has_ragged_last_dims: bool = False,
) -> Tuple[bool, str]:

Then add a guard near the top of the function (after the SM90 check, before the BF16 fast-path):

if has_bias and has_ragged_last_dims:
    return (
        False,
        "V2 grouped GEMM does not support bias with ragged last dims.",
    )

And pass has_ragged_last_dims=(out_last_dims is not None) at the call site in grouped_gemm.

This way, when NVTE_JAX_ENFORCE_V2_GROUPED_GEMM=1 is set, the enforce check fires with the correct reason rather than letting the kernel launch and then crashing in Python post-processing.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

@jberchtold-nvidia

Copy link
Copy Markdown
Collaborator Author

/te-ci

@jberchtold-nvidia
jberchtold-nvidia marked this pull request as ready for review July 10, 2026 14:33
Comment on lines +2421 to +2422
if out_last_dims is not None:
raise NotImplementedError("V2 grouped GEMM bias is not supported for ragged last dims")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This will be especially bad if NVTE_JAX_ENFORCE_V2_GROUPED_GEMM=1 because v1 fallback is disabled

return (out_2d + bias_rows).reshape(out_shape)

bias_shape = (num_gemms,) + (1,) * (out.ndim - 2) + (n_dim,)
return out + bias_2d.reshape(bias_shape)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Correct me if I am wrong but I think the test file is currently having bias=None, which will not test this path?

@phu0ngng
phu0ngng self-requested a review August 11, 2026 18:23
@jberchtold-nvidia
jberchtold-nvidia marked this pull request as draft August 11, 2026 20:57
@jberchtold-nvidia
jberchtold-nvidia force-pushed the jberchtold/hopper-bf16-gmm branch 3 times, most recently from a0b17d8 to 77f7098 Compare September 4, 2026 15:16
Signed-off-by: Jeremy Berchtold <jberchtold@nvidia.com>
@jberchtold-nvidia
jberchtold-nvidia force-pushed the jberchtold/hopper-bf16-gmm branch from e42bc59 to d264a62 Compare September 4, 2026 15:18
@jberchtold-nvidia

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 jax

@jberchtold-nvidia
jberchtold-nvidia marked this pull request as ready for review September 8, 2026 15:29

@cache
def _v2_grouped_gemm_supports_per_group_alpha_beta() -> bool:
"""Whether nvte_grouped_gemm accepts per-group alpha/beta on all visible devices."""

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 Mixed GPUs Break Dispatch

If a process exposes both Hopper and Blackwell GPUs, this process-wide minimum-capability check selects singleton alpha/beta buffers for every device. The native grouped-GEMM implementation validates their size using the device that executes the kernel, and Blackwell requires one value per grouped GEMM. A call dispatched to the Blackwell GPU therefore fails native validation. Select the alpha/beta representation per executing device or make the native contract accept the process-wide choice.

Knowledge Base Used: JAX runtime and public API

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we even support this use case in TE in general?

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.

Not explicitly, at least not in the JAX TE path. The code assumes one effective device capability for a compiled computation—feature gates use get_min_device_compute_capability(), and grouped GEMM has no per-executing-device dispatch. Supported deployments therefore appear to assume a homogeneous local mesh; mixed Hopper/Blackwell devices are outside the documented/implemented contract, even though there is no explicit homogeneity check.

I agree this should not be treated as a required fix for this PR. The remaining choice is whether to add an explicit rejection for mixed-capability meshes so they fail clearly, rather than implying that the process-wide minimum-capability decision supports them.

@tdophung tdophung left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM pending CI

@jberchtold-nvidia
jberchtold-nvidia merged commit 65809a1 into NVIDIA:main Sep 9, 2026
24 of 29 checks passed
@jberchtold-nvidia
jberchtold-nvidia deleted the jberchtold/hopper-bf16-gmm branch September 9, 2026 21:35
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.

2 participants