Skip to content

[Stacked PR 4/5] Enable GDN backward pass kernel and hybrid precision in Qwen3 model - #5154

Open
Rohan-Bierneni wants to merge 1 commit into
rbierneni-gdn-3-bwd-kernelfrom
rbierneni-gdn-4-model-integration
Open

[Stacked PR 4/5] Enable GDN backward pass kernel and hybrid precision in Qwen3 model#5154
Rohan-Bierneni wants to merge 1 commit into
rbierneni-gdn-3-bwd-kernelfrom
rbierneni-gdn-4-model-integration

Conversation

@Rohan-Bierneni

@Rohan-Bierneni Rohan-Bierneni commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Stacked PR Chain

Stack Status Branch Base PR
PR 1 🔗 Stack PR 1 rbierneni-gdn-1-ci-hygiene main #5151 - [Stacked PR 1/5] Fix upstream pyink formatting and GMM v2 compatibility for Qwen3.5
PR 2 🔗 Stack PR 2 rbierneni-gdn-2-fwd-kernel rbierneni-gdn-1-ci-hygiene #5152 - [Stacked PR 2/5] Import local Tokamax GDN forward kernel with custom remat for backward pass support
PR 3 🔗 Base PR rbierneni-gdn-3-bwd-kernel rbierneni-gdn-2-fwd-kernel #5153 - [Stacked PR 3/5] Add decoupled GDN Pallas backward pass kernel and parity tests
PR 4 🚀 This PR rbierneni-gdn-4-model-integration rbierneni-gdn-3-bwd-kernel #5154 - [Stacked PR 4/5] Enable GDN backward pass kernel and hybrid precision in Qwen3 model
PR 5 ⏳ Stacked on PR 4 rbierneni-gdnv3-bwd rbierneni-gdn-4-model-integration #5098 - [Stacked PR 5/5] Add GDN backward pass 8k and 64k latency and memory benchmark suite

Description

This is PR 4 of 5 in the stacked series enabling the Pallas Gated Delta Net (GDN) backward pass kernel in MaxText.

This PR integrates the GDN backward pass kernel into the Qwen3 model architecture with full configuration controls:

  1. Model Integration (src/maxtext/models/qwen3.py):
    • Connects gdn_decoupled_conv1d into Qwen3NextGatedDeltaNet, cleanly toggled by cfg.use_gdn_kernel.
    • Preserves optimized pure JAX fallback with fast triangular solve when use_gdn_kernel is False.
  2. Hybrid Precision Configuration:
    • Adds use_gdn_kernel (default: False), gdn_state_dtype: "float32", and gdn_decay_dtype: "float32" to src/maxtext/configs/base.yml, types.py, and pyconfig.py.
    • Hybrid precision ensures stable recurrent state accumulation in float32 while keeping activations in bfloat16.
  3. Configuration Testing:
    • Extends tests/unit/pyconfig_test.py with validation coverage for GDN configuration knobs.

Files Changed

  • src/maxtext/models/qwen3.py: Wiring gdn_decoupled_conv1d into Qwen3NextGatedDeltaNet.
  • src/maxtext/configs/base.yml: Default settings for use_gdn_kernel, gdn_state_dtype, and gdn_decay_dtype.
  • src/maxtext/configs/types.py: Type annotations and schema definitions for GDN fields.
  • src/maxtext/configs/pyconfig.py: Parser validation for GDN flags.
  • tests/unit/pyconfig_test.py: Unit test verifying pyconfig parsing and validation for GDN knobs.

Tests

  • Verified with pre-commit run --files ... (all hooks passed: codespell, pylint, pyink).
  • Verified configuration parsing via pytest tests/unit/pyconfig_test.py (41/41 unit tests passed).

Checklist

  • I have performed a self-review of my code.
  • I have necessary comments in my code, particularly in hard-to-understand areas.
  • I have run end-to-end tests and verified pre-commit linters pass.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

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 configuration options and support for the GDN Pallas kernel (use_gdn_kernel) along with customizable precision types (gdn_state_dtype and gdn_decay_dtype) in the Qwen3 model. Feedback on these changes highlights two key areas for improvement: first, under ShardMode.EXPLICIT, manual resharding of inputs is required for the Pallas GDN kernel to prevent compilation or runtime errors due to layout mismatches in shard_map; second, the dtype resolution logic for GDN decay and state parameters can be simplified and made more robust by utilizing jnp.dtype directly instead of manual string checks.

Comment on lines +954 to +957
qkv_pspec = logical_to_mesh_axes((KV_BATCH, None, None), mesh=self.mesh, rules=logical_rules)
b_a_pspec = logical_to_mesh_axes((KV_BATCH, None, None), mesh=self.mesh, rules=logical_rules)
conv_state_pspec = logical_to_mesh_axes((KV_BATCH, None, None), mesh=self.mesh, rules=logical_rules)
recurrent_state_pspec = logical_to_mesh_axes((KV_BATCH, None, None, None), mesh=self.mesh, rules=logical_rules)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Under ShardMode.EXPLICIT, shard_map manualizes the mesh axes and does not automatically insert resharding/all-gathers for operands whose layouts differ from in_specs. Since the Pallas GDN kernel expects the sequence and head dimensions to be local (replicated) on each device, calling gdn_step_fn directly without resharding will cause compilation/runtime errors.

We should adjust the PartitionSpecs using remove_incompatible_mesh_axes_from_partition_spec and manually reshard the inputs (qkv, b, a, conv_state_arg, and recurrent_state_arg) to match their respective PartitionSpecs when cfg.shard_mode == ShardMode.EXPLICIT.

        qkv_pspec = logical_to_mesh_axes((KV_BATCH, None, None), mesh=self.mesh, rules=logical_rules)
        b_a_pspec = logical_to_mesh_axes((KV_BATCH, None, None), mesh=self.mesh, rules=logical_rules)
        conv_state_pspec = logical_to_mesh_axes((KV_BATCH, None, None), mesh=self.mesh, rules=logical_rules)
        recurrent_state_pspec = logical_to_mesh_axes((KV_BATCH, None, None, None), mesh=self.mesh, rules=logical_rules)

        qkv_pspec = remove_incompatible_mesh_axes_from_partition_spec(
            qkv_pspec, qkv.shape, self.mesh, dims=(0,), allow_remove_axes=True
        )
        b_a_pspec = remove_incompatible_mesh_axes_from_partition_spec(
            b_a_pspec, b.shape, self.mesh, dims=(0,), allow_remove_axes=True
        )
        conv_state_pspec = remove_incompatible_mesh_axes_from_partition_spec(
            conv_state_pspec, conv_state_arg.shape, self.mesh, dims=(0,), allow_remove_axes=True
        )
        recurrent_state_pspec = remove_incompatible_mesh_axes_from_partition_spec(
            recurrent_state_pspec, recurrent_state_arg.shape, self.mesh, dims=(0,), allow_remove_axes=True
        )

        if cfg.shard_mode == ShardMode.EXPLICIT:
          qkv = jax.sharding.reshard(qkv, jax.sharding.NamedSharding(self.mesh, qkv_pspec))
          b = jax.sharding.reshard(b, jax.sharding.NamedSharding(self.mesh, b_a_pspec))
          a = jax.sharding.reshard(a, jax.sharding.NamedSharding(self.mesh, b_a_pspec))
          conv_state_arg = jax.sharding.reshard(
              conv_state_arg, jax.sharding.NamedSharding(self.mesh, conv_state_pspec)
          )
          recurrent_state_arg = jax.sharding.reshard(
              recurrent_state_arg, jax.sharding.NamedSharding(self.mesh, recurrent_state_pspec)
          )

Comment on lines +655 to +660
decay_dtype = getattr(cfg, "gdn_decay_dtype", jnp.float32)
if isinstance(decay_dtype, str):
decay_dtype = getattr(jnp, decay_dtype, jnp.float32)
state_dtype = getattr(cfg, "gdn_state_dtype", jnp.float32)
if isinstance(state_dtype, str):
state_dtype = getattr(jnp, state_dtype, jnp.float32)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The manual type checks and getattr calls on jnp can be simplified and made more robust by using jnp.dtype directly. jnp.dtype natively handles both string representations (e.g., 'float32') and existing dtype objects.

    decay_dtype = jnp.dtype(getattr(cfg, "gdn_decay_dtype", jnp.float32))
    state_dtype = jnp.dtype(getattr(cfg, "gdn_state_dtype", jnp.float32))

@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.75000% with 29 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/maxtext/models/qwen3.py 63.75% 25 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

@Rohan-Bierneni Rohan-Bierneni changed the title [Stacked PR 4/5] Integrate GDN kernel and hybrid precision into Qwen3 model [Stacked PR 4/5] Enable GDN backward pass kernel and hybrid precision in Qwen3 model Sep 6, 2026
@Rohan-Bierneni
Rohan-Bierneni force-pushed the rbierneni-gdn-4-model-integration branch from b5061ab to acdf62c Compare September 6, 2026 06:43
… in Qwen3 model

- Wire gdn_decoupled_conv1d into Qwen3NextGatedDeltaNet in qwen3.py, gated by use_gdn_kernel configuration flag.
- Add gdn_state_dtype: "float32" and gdn_decay_dtype: "float32" configurations to base.yml, types.py, and pyconfig.py for hybrid precision stability.
- Retain pure JAX triangular solve optimization when use_gdn_kernel is disabled.
- Add pyconfig validation tests for GDN configuration parameters.
@Rohan-Bierneni
Rohan-Bierneni force-pushed the rbierneni-gdn-4-model-integration branch from acdf62c to a8d6db0 Compare September 6, 2026 07:00
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.

1 participant