Skip to content

fix(mamba): apply Falcon-Mamba B/C/dt RMS norm once, no per-call ones - #1574

Merged
inureyes merged 2 commits into
mainfrom
fix/issue-1333-mamba-bcdt-rms-once
Sep 2, 2026
Merged

inureyes merged 2 commits into
mainfrom
fix/issue-1333-mamba-bcdt-rms-once

Conversation

@inureyes

@inureyes inureyes commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

When use_bcdt_rms is set (every falcon_mamba checkpoint), MambaBlock::ssm_step normalized delta, B and C twice per call and allocated a fresh ones weight array on every one of those six calls. This PR applies the weight-less RMS norm exactly once per tensor through the bridge's existing fast_rms_norm_no_weight, matching the upstream mlx-lm reference, and removes the now-unused ones-allocating helper.

What changed

  • src/models/mamba.rs: deleted rms_norm_no_scale (it allocated a fresh ones array per call just to reuse the weighted fast_rms_norm kernel).
  • src/models/mamba.rs: MambaBlock::mixer_norm now calls mlxcel_core::fast_rms_norm_no_weight(x, self.mixer_rms_eps) directly.
  • src/models/mamba.rs: ssm_step applies mixer_norm once per tensor (delta, B, C) instead of self.mixer_norm(&self.mixer_norm(&x)), and the stale "applies mixer_norm TWICE" comment is replaced with a one-line description of the corrected behavior.
  • src/models/mamba_tests.rs: added tiny_mamba_block (a minimal, functionally-inert MambaBlock builder for unit tests) and two tests: mamba_mixer_norm_applies_the_bridge_norm_exactly_once, which asserts mixer_norm(x) is bit-exact to one fast_rms_norm_no_weight call and bit-distinct from a second application on top of it, and mamba_mixer_norm_is_identity_when_bcdt_rms_disabled, which asserts the pass-through path when use_bcdt_rms is false.

For a T-token prefill this drops 3T redundant norm launches and 6T ones allocations per layer (64 layers on the falcon-mamba-7b checkpoint), with no change to the normalized values beyond removing the second, spurious application. mamba2.rs, falcon_h1.rs, jamba.rs, granitemoehybrid.rs and plamo2.rs carry no use_bcdt_rms field and are untouched.

Test plan

  • cargo test --profile test-fast --features metal,accelerate --lib models::mamba (18 passed, 2 pre-existing ignored)
  • cargo clippy --profile test-fast --lib --tests --features metal,accelerate -- -D warnings
  • cargo fmt --all -- --check
  • Real-checkpoint greedy-output and peak-memory comparison on models/falcon-mamba-7b-4bit before/after this change (orchestrator follow-up)

Closes #1333

When use_bcdt_rms is set (every falcon_mamba checkpoint), MambaBlock::ssm_step normalized delta, B and C twice per call via self.mixer_norm(&self.mixer_norm(&x)), and the old rms_norm_no_scale helper allocated a fresh ones weight array on every one of those six calls per token per layer.

The upstream mlx-lm reference (mlx_lm/models/mamba.py) applies the weight-less RMS norm exactly once to each of delta, B and C when use_bcdt_rms is true, using mx.fast.rms_norm(x, weight=None, eps=mixer_rms_eps). This change makes mixer_norm call the bridge's existing fast_rms_norm_no_weight directly instead of allocating ones and calling fast_rms_norm, removes the now-unused rms_norm_no_scale helper, and applies mixer_norm once per tensor in ssm_step instead of nesting it.

For a T-token prefill this drops 3T redundant norm launches and 6T ones allocations per layer (64 layers on the 7B checkpoint), with no change to the normalized values beyond the removal of the second, spurious application.

Added two unit tests in mamba_tests.rs that construct a minimal MambaBlock and pin the fix at the mixer_norm boundary: one asserts mixer_norm(x) is bit-exact to a single fast_rms_norm_no_weight call and bit-distinct from a second application on top of it (array_equal is exact, not tolerance-based, which is what actually catches a regression back to the double call since the double application is numerically close to a no-op), and one asserts mixer_norm is an identity pass-through when use_bcdt_rms is false.

Validated: cargo test --profile test-fast --features metal,accelerate --lib models::mamba (18 passed, 2 pre-existing ignored), cargo clippy --profile test-fast --lib --tests --features metal,accelerate -- -D warnings, cargo fmt --all -- --check.

Refs #1333
@inureyes inureyes added type:bug Bug fixes, error corrections, or issue resolutions priority:medium Medium priority area:models Model architectures, weights, loading, metadata area:inference Generation, sampling, decoding (incl. speculative, DRY) modelsize:small 4-bit checkpoint <= 10GB; fast iteration, safe for smoke tests modeltype:text Text-only language model arch:ssm State-space / recurrent model (Mamba, RWKV) status:review Under review labels Sep 2, 2026
@inureyes inureyes added status:done Completed and removed status:review Under review labels Sep 2, 2026
@inureyes
inureyes merged commit fca49fa into main Sep 2, 2026
13 checks passed
@inureyes
inureyes deleted the fix/issue-1333-mamba-bcdt-rms-once branch September 2, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch:ssm State-space / recurrent model (Mamba, RWKV) area:inference Generation, sampling, decoding (incl. speculative, DRY) area:models Model architectures, weights, loading, metadata modelsize:small 4-bit checkpoint <= 10GB; fast iteration, safe for smoke tests modeltype:text Text-only language model priority:medium Medium priority status:done Completed type:bug Bug fixes, error corrections, or issue resolutions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(mamba): apply the Falcon-Mamba B/C/dt RMS norm once and drop the per-call ones allocation

1 participant