VerifiedarXiv:2405.2106037 min
Architecture · Sequence models

Mamba-2: Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality

Mamba's recurrence, rewritten as a structured matrix so a GPU can compute it with matrix multiplications.

Tri Dao and Albert Gu restrict the state space model's per-step decay to one number, show that the restricted model is a form of masked attention without the softmax, and use that to compute it in chunks. The new layer is 2 to 8 times faster than Mamba's scan at state size 64 and supports states 8 times larger.

Explaining the paperTransformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space DualityDao, Gu · Princeton & CMU · ICML 2024 · arXiv:2405.21060 ↗

At 2.7B parameters and 300B tokens of the Pile, Mamba-2 beats Pythia-6.9B, trained on the same data, on every benchmark in the paper's appendix table. This page builds the model from the recurrence up: the matrix an SSM computes, why its blocks are low rank, the one restriction that turns it into masked attention, and the chunked algorithm that follows.

Mamba showed that a recurrent layer with a small, input-dependent state could match Transformers on language modeling. Mamba-2 keeps that layer's shape (a fixed-size state updated once per token) and changes how it is computed. The paper's argument has four steps, and the page follows them in order:

  1. Any state space model (SSM) computes y=Mxy = Mx for a lower-triangular T×TT \times T matrix MM, where TT is the sequence length.
  2. That matrix is semiseparable: every block below its diagonal has rank at most NN, the state size.
  3. If the state decays by a single scalar per step, MM factors into a decay mask times an attention-shaped matrix CB⊤CB^{\top}. This is the structured state space duality (SSD) of the title.
  4. Cut MM into chunks: compute the diagonal chunks like attention, and carry everything else through a short recurrence between chunks. Nearly all of the work becomes matrix multiplication.

Why Mamba was slow on a GPU

A selective SSM reads a sequence x0,…,xT−1x_0, \ldots, x_{T-1} one token at a time and keeps a state vector ht∈RNh_t \in \mathbb{R}^N. At each token it decays the old state, adds the new input, and reads out one number:

ht=At ht−1+Bt xt,yt=Ct⊤hth_t = A_t\, h_{t-1} + B_t\, x_t, \qquad y_t = C_t^{\top} h_t
(1)

This is the paper's equation (2). Here xtx_t and yty_t are scalars (one channel), Bt,Ct∈RNB_t, C_t \in \mathbb{R}^N are computed from the input at token tt, and AtA_t is an N×NN \times N matrix that also depends on the token. “Selective” refers to that input dependence: the model can choose, token by token, how much of the past to keep (through AtA_t) and how much of the present to write (through BtB_t). In Mamba, AtA_t is diagonal, so each of the NN state slots decays at its own rate, and the layer runs one such SSM per channel. Mamba-2.8B has 5120 inner channels per layer and N=16N = 16, so each layer carries 5120 × 16 = 81,920 state numbers from one token to the next.

Each step of (1) is a handful of elementwise multiply-adds on vectors of length 16. A GPU does that kind of arithmetic on its general-purpose cores. Matrix multiplications run on separate tensor cores, which are far faster: an A100 is rated at 312 teraFLOP/s for 16-bit matrix multiplication on tensor cores and 19.5 teraFLOP/s for ordinary 32-bit arithmetic, a factor of 16.6 Mamba's scan does work proportional to TT, but none of that work is a matrix multiplication, so it cannot use the tensor cores. It needed a custom fused CUDA kernel to be efficient, and even with that kernel the paper counts it as less efficient than models built on matrix multiplication.

Attention has the opposite profile. Its cost grows like T2T^2, but almost all of it is two large matrix multiplications, and FlashAttention showed how to run those at close to the tensor cores' peak by tiling them through on-chip memory. Mamba-2 asks whether an SSM can be rewritten so that its work is also matrix multiplication, while keeping the cost linear in TT.

An SSM is a triangular matrix

Unroll (1) from h0=B0x0h_0 = B_0 x_0. Each input xsx_s enters the state through BsB_s and is then multiplied by every later decay:

ht=∑s=0tAtAt−1⋯As+1 Bs xsh_t = \sum_{s=0}^{t} A_t A_{t-1} \cdots A_{s+1}\, B_s\, x_s

Multiplying by Ct⊤C_t^{\top} gives each output as a weighted sum of the inputs so far. Renaming the output index jj and the input index ii:

yj=∑i=0jMji xiMji=Cj⊤AjAj−1⋯Ai+1Bi\begin{gathered} y_j = \sum_{i=0}^{j} M_{ji}\, x_i \\ M_{ji} = C_j^{\top} A_j A_{j-1} \cdots A_{i+1} B_i \end{gathered}
(2)

So y=Mxy = Mx with a T×TT \times T matrix MM. It is lower triangular because output jj depends only on inputs i≤ji \le j. For three tokens it reads

M=[C0⊤B000C1⊤A1B0C1⊤B10C2⊤A2A1B0C2⊤A2B1C2⊤B2]M = \begin{bmatrix} C_0^{\top} B_0 & 0 & 0 \\ C_1^{\top} A_1 B_0 & C_1^{\top} B_1 & 0 \\ C_2^{\top} A_2 A_1 B_0 & C_2^{\top} A_2 B_1 & C_2^{\top} B_2 \end{bmatrix}

The diagonal entry Cj⊤BjC_j^{\top} B_j is how much token jj contributes to its own output. The entry MjiM_{ji} below the diagonal (row jj, column ii) routes input ii through the state and through the decays Ai+1,…,AjA_{i+1}, \ldots, A_j before it is read out. The recurrence and the product MxMx compute the same numbers (the paper's Theorem 3.5); they are two algorithms for one linear map. The recurrence costs work proportional to TT and never forms MM. Forming MM and multiplying costs work proportional to T2T^2, which is what attention pays.

When a layer has more than one channel, the paper shares one MM across the PP channels of a head: the input is a T×PT \times P matrix XX, and the output is Y=MXY = MX. In Mamba each head is one channel (P=1P = 1); Mamba-2 uses P=64P = 64 or 128128, like a Transformer's head dimension. The larger PP turns the vector operations in the recurrence into small matrix operations, which the chunked algorithm below relies on.

Why the off-diagonal blocks of an SSM matrix are low rank

Take any block of MM that sits below the diagonal: rows r≥jr \ge j and columns c≤i−1c \le i - 1, for some split with j≥ij \ge i. Every entry in it is Cr⊤Ar⋯Ac+1BcC_r^{\top} A_r \cdots A_{c+1} B_c, and the chain of decays passes through the block's corner. Cut the chain there:

Mrc=ur⊤ W vcur⊤=Cr⊤Ar⋯Aj+1(row only)W=Aj⋯Ai(fixed)vc=Ai−1⋯Ac+1Bc(column only)\begin{aligned} M_{rc} &= u_r^{\top}\, W\, v_c \\ u_r^{\top} &= C_r^{\top} A_r \cdots A_{j+1} &&\text{(row only)} \\ W &= A_j \cdots A_i &&\text{(fixed)} \\ v_c &= A_{i-1} \cdots A_{c+1} B_c &&\text{(column only)} \end{aligned}
(3)

The left factor uru_r is an NN-vector that depends only on the row, the right factor vcv_c is an NN-vector that depends only on the column, and WW is the same N×NN \times N matrix for the whole block. Stacking the rows gives the block as UWVU W V, with UU of width NN and VV of height NN, so its rank is at most NN however large the block is. In words: every contribution of input cc to output rr passes through the NN-dimensional state at the block's boundary.

A lower-triangular matrix in which every block on or below the diagonal has rank at most NN is called N-semiseparable (Definition 3.1), and the form Mji=Cj⊤Aj⋯Ai+1BiM_{ji} = C_j^{\top} A_j \cdots A_{i+1} B_i is its sequentially semiseparable (SSS) representation (Definition 3.2). Equation (3) proves one direction (Lemma 3.3). The converse is a known result (Proposition 3.4): every NN-semiseparable matrix can be written this way. So SSMs with state size NN and NN-semiseparable matrices are the same set of linear maps. The SSS form stores O(N2T)O(N^2 T) numbers (one N×NN \times N matrix per token), but these matrices can be stored in O(NT)O(NT) numbers and multiplied by a vector in O(NT)O(NT) time (Proposition 3.6, from Pernet, Signargout and Villard).4

Because of this equivalence, any method for multiplying a vector by a semiseparable matrix is also a way to run an SSM. The recurrence is one such method. The chunked method of the SSD section is another, and it uses the factorization (3) directly.

Figure 1 · the rank of an off-diagonal block
3
8
A 16-token SSM with a diagonal AtA_t as in Mamba-1, so the bound applies beyond Mamba-2. Cells are MjiM_{ji} (teal positive, amber negative); hover to read one. The dashed box is the block below row block corner and left of it, and the bars are its singular values, computed live, on a log scale. Gray stubs are zero to rounding. Drag state size N and the number of nonzero bars follows it until the block's own row or column count caps it; drag the corner to 1 or 15 and the block is a single column or row, rank 1 for any NN.

Mamba-2's restriction: one decay per step

With a diagonal AtA_t, the chain Aj⋯Ai+1A_j \cdots A_{i+1} in (2) is a diagonal matrix whose nn-th entry is the product of slot nn's decays. In Mamba those decays are at[n]=exp⁡(ΔtAn)a_t[n] = \exp(\Delta_t A_n), with a step size Δt\Delta_t computed from the token and one learned negative rate AnA_n per slot, so each slot has its own cumulative decay between any two tokens. Writing (2) out slot by slot:

Mji=∑n=1NCj[n]  (aj[n]⋯ai+1[n])  Bi[n]M_{ji} = \sum_{n=1}^{N} C_j[n]\; \big(a_j[n] \cdots a_{i+1}[n]\big)\; B_i[n]

Each slot contributes its own decay pattern, so MM is a sum of NN separately masked rank-one matrices, and no single mask factors out of the sum.

Mamba-2 requires At=atIA_t = a_t I: a scalar times the identity, the same decay for every slot at token tt. Then Aj⋯Ai+1=(aj⋯ai+1)IA_j \cdots A_{i+1} = (a_j \cdots a_{i+1}) I, the scalar comes out of the dot product, and

Mji=Lji  Cj⊤BiLji={ajaj−1⋯ai+1j≥i0j<i\begin{gathered} M_{ji} = L_{ji}\; C_j^{\top} B_i \\ L_{ji} = \begin{cases} a_j a_{j-1} \cdots a_{i+1} & j \ge i \\ 0 & j < i \end{cases} \end{gathered}
(4)

or, for the whole matrix, M=L∘(CB⊤)M = L \circ (C B^{\top}), where ∘\circ multiplies entry by entry, CC and BB are T×NT \times N with rows Cj⊤C_j^{\top} and Bi⊤B_i^{\top}, and the diagonal of LL is 1 (an empty product). The mask LL is itself 1-semiseparable: it is MM for a state of size 1. With decays a1,a2,a3=0.9,0.5,0.8a_1, a_2, a_3 = 0.9, 0.5, 0.8 on four tokens:

L=[10.910.450.510.360.40.81]L = \begin{bmatrix} 1 & & & \\ 0.9 & 1 & & \\ 0.45 & 0.5 & 1 & \\ 0.36 & 0.4 & 0.8 & 1 \end{bmatrix}

The bottom-left entry,

L30=a3a2a1=0.8×0.5×0.9=0.36,L_{30} = a_3 a_2 a_1 = 0.8 \times 0.5 \times 0.9 = 0.36,

is the fraction of whatever token 0 wrote into the state that is still there at token 3. Multiplying a vector by LL is the scalar recurrence yt=atyt−1+xty_t = a_t y_{t-1} + x_t (the paper's equation (7)).

In the official code, each head has one learned rate A=−exp⁡(Alog⁡)<0A = -\exp(A_{\log}) < 0 and each token a step size Δt>0\Delta_t > 0, so at=exp⁡(ΔtA)a_t = \exp(\Delta_t A) lies strictly between 0 and 1. The paper describes the ata_t as “input-dependent scalars bounded in [0, 1]”. A value near 1 keeps the state; a value near 0 clears it.

Figure 2 · diagonal A gives N masks, scalar A gives one
none
The decay mask of each of four state slots over 12 tokens, with the same step sizes Δt\Delta_t in both settings. Toggle diagonal (Mamba-1): slot nn decays by exp⁡(ΔtAn)\exp(\Delta_t A_n), so the four masks fade at four speeds. Toggle scalar × I (Mamba-2): the four are one matrix LL. Click any cell to read it in all four slots. New sequence at sets ab=0a_b = 0 at token bb, which zeroes every entry that connects a token before bb to one at or after it.

The last control in Figure 2 is how Mamba-2 handles several short sequences packed into one long row for training (Section 8.3). The decay that carries the state from token tt into token t+1t+1 is at+1a_{t+1}, and every LjiL_{ji} with i≤t<ji \le t < j contains it as a factor. Setting at+1=0a_{t+1} = 0 on the first token of the next sequence therefore zeroes the whole rectangle of LL that would leak one sequence into the next, with no padding and no special mask.

Why not keep the diagonal AtA_t? The paper notes that a diagonal AA has the same theoretical efficiency but is “less hardware-friendly”: with one mask per slot, the masked-attention computation of the next section would need NN separate masked T×TT \times T products instead of one masked matrix multiplication. The restriction costs expressiveness: the paper describes it as “slightly decreasing the expressive power in return for significant training efficiency improvements”, and it does not run an experiment that isolates the cost (the section on limits returns to this).

The same matrix as masked attention

Softmax attention computes Y=softmax⁡(QK⊤) VY = \operatorname{softmax}(QK^{\top})\,V with a query, key and value per token. Linear attention (Katharopoulos et al., 2020) drops the softmax and applies the causal mask directly, Y=(L∘QK⊤)VY = (L \circ QK^{\top})V with LL the all-ones lower-triangular matrix, and shows that this product can be computed as a recurrence.3 Compare with (4). Renaming Q=CQ = C, K=BK = B and V=XV = X:

Y=(L∘CB⊤)X=(L∘QK⊤)VY = \big(L \circ C B^{\top}\big) X = \big(L \circ Q K^{\top}\big) V
(5)

So the scalar-decay SSM is masked attention with no softmax and with the causal mask replaced by the decay mask LL. Linear attention is the case at=1a_t = 1 for every token (nothing decays). RetNet uses a fixed decay per head, and GateLoop makes the decay input-dependent; the paper places both in the same family. It calls the general form (L∘QK⊤)V(L \circ QK^{\top})V with a structured LL structured masked attention, and proves the converse direction too (Theorem 5.2): any structured masked attention that can be computed as a recurrence with a bounded state must have a semiseparable mask. The attention analogy also fixes the roles of the sizes: the state size NN plays the part of the query-key dimension, and the head dimension PP the value dimension.

The two sides of (5) are two ways to compute the same outputs, with opposite costs. With P=NP = N:

Figure 3 runs a six-token example both ways. At token 3 the recurrent mode takes the previous state h2h_2, the decay 0.8, the input 0.5 with B3B_3 and C3C_3, and the attention mode takes row 3 of MM against the first four inputs:

h3=0.8 (1.45, 0.225)+0.5 (1, −0.5)=(1.66, −0.07)y3=C3⊤h3=(0.5, −0.5)⋅(1.66, −0.07)=0.865y3=0.09⋅1−0.1⋅2−0.6⋅(−1)+0.75⋅0.5=0.865\begin{aligned} h_3 &= 0.8\,(1.45,\ 0.225) + 0.5\,(1,\ {-0.5}) = (1.66,\ {-0.07}) \\ y_3 &= C_3^{\top} h_3 = (0.5,\ {-0.5}) \cdot (1.66,\ {-0.07}) = 0.865 \\ y_3 &= 0.09 \cdot 1 - 0.1 \cdot 2 - 0.6 \cdot (-1) + 0.75 \cdot 0.5 = 0.865 \end{aligned}

The first entry of that row is L30L_{30} times C3⊤B0C_3^{\top} B_0, 0.36 × 0.25, with the same L30L_{30} as in the example above.

Figure 3 · one example, computed as a recurrence and as masked attention
3
Six tokens, state size 2, one channel. Scrub token t or press Play. The matrix panel shows M=L∘CB⊤M = L \circ CB^{\top} with row tt highlighted, next to the input column x and the outputs filled in so far. The other panel shows either the recurrent update that produces yty_t from the previous state, or the attention factors: the Gram matrix G=CB⊤G = CB^{\top}, the mask LL, and row tt of their product. The two values of yty_t agree at every tt.

Neither mode suits a GPU at long sequence lengths. The recurrent mode has the right total work but runs as a long chain of small operations. The attention mode is all tensor-core work, but its work grows like T2T^2. The SSD algorithm uses attention mode inside short chunks and recurrent mode between them.

How the SSD algorithm computes Mamba-2

Cut the sequence into chunks of QQ tokens, and cut MM into a grid of Q×QQ \times Q blocks to match: T/QT/Q blocks on the diagonal and the rest below it. A diagonal block describes how the tokens inside one chunk affect each other. A block below the diagonal describes how an earlier chunk affects a later one, and by (3) it factors through the N×PN \times P state at the chunk boundary. The algorithm (Section 6 and Listing 1) computes the output as the sum of these two kinds of contribution, in four steps:

  1. Diagonal blocks (inside each chunk). For each chunk, compute (Lc∘CcBc⊤)Xc(L_c \circ C_c B_c^{\top}) X_c in attention mode, as if the state entering the chunk were zero. The chunks are independent, so this is one batched matrix multiplication over all of them. Result: YdiagY_{\text{diag}}.
  2. Right factors (inputs to chunk state). For each chunk, compute the state at its last token, again starting from zero: the sum of Bixi⊤B_i x_i^{\top} over the chunk's tokens, each decayed from token ii to the chunk's end. That is an (N×Q)(N \times Q) by (Q×P)(Q \times P) matrix multiplication per chunk.
  3. Center factors (chunk state to chunk state). The states from step 2 ignore everything before their chunk. Run the scalar recurrence across chunks: the true state entering chunk c+1c+1 is the true state entering chunk cc, decayed by the product of chunk cc's decays, plus chunk cc's own state from step 2. This is a scan of length T/QT/Q, not TT.
  4. Left factors (chunk state to outputs). For each token jj in a chunk, read the true incoming state through CjC_j, decayed from the chunk's start to jj. Another batched matrix multiplication. Result: YoffY_{\text{off}}.

The output is Y=Ydiag+YoffY = Y_{\text{diag}} + Y_{\text{off}}. Steps 2 to 4 are the three factors of (3) applied chunk by chunk: vv (right), WW (center), uu (left). They are never multiplied out into the off-diagonal blocks of MM.

The cost follows from the shapes (Section 6.3). Counted as multiply-adds per head, step 1 is TQ(N+P)TQ(N + P), steps 2 and 4 are TNPTNP each, and step 3 is TNP/QTNP/Q. With N=P=QN = P = Q every matrix multiplication is N×NN \times N by N×NN \times N, the total is O(TN2)O(TN^2) (the same order as the recurrence), memory is O(TN)O(TN), and the only work that is not matrix multiplication is the step-3 scan over T/QT/Q chunks, which the paper reports as negligible in its GPU implementation (Theorem 6.1). The chunk length is a free parameter: the paper's Listing 1 defaults to 64, the official Mamba-2 module to 256.

At T=1024T = 1024 and N=P=64N = P = 64, per head: chunks of Q=64Q = 64 give 8.4M multiply-adds in the diagonal blocks, 4.2M each in steps 2 and 4, and a 16-step scan with 65.5k multiply-adds, 16.8M in total. Pure attention mode (Q=TQ = T) needs 134M, all in the diagonal block. Pure recurrence (Q=1Q = 1) has the fewest multiply-adds, but it spends 4.2M of them in a scan of 1024 sequential steps, and its “matrix” multiplications are between 1 × 1 blocks.

Figure 4 · the SSD block decomposition
64
T=1024T = 1024, N=P=64N = P = 64. Drag chunk length Q through every power of two from 1 (the plain recurrence: 1024 one-token chunks and a 1024-step scan) to 1024 (one chunk: quadratic attention, no scan). Left: the matrix cut into diagonal blocks and low-rank off-diagonal blocks. Right: the chunk timeline, with inputs X, chunk states H and outputs Y; pick a step to see its arrows. Bottom: multiply-adds per head for each step, from the paper's cost formulas.

The algorithm in PyTorch

The paper's Listing 1 implements the four steps in about 30 lines; the copy here differs from it only in how long comment lines wrap. In this code A is the log of the decay, one value per token and head, so a cumulative sum of A is the log of a cumulative product of decays. segsum turns it into all the partial sums ∑k=i+1jAk\sum_{k=i+1}^{j} A_k and sets the entries above the diagonal to −∞-\infty, so exp(segsum(A)) is exactly the mask LL of (4), with Lji=aj⋯ai+1L_{ji} = a_j \cdots a_{i+1} below the diagonal, 1 on it and 0 above it.

def segsum(x):
    """Naive segment sum calculation. exp(segsum(A)) produces a 1-SS matrix,
       which is equivalent to a scalar SSM."""
    T = x.size(-1)
    x_cumsum = torch.cumsum(x, dim=-1)
    x_segsum = x_cumsum[..., :, None] - x_cumsum[..., None, :]
    mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=0)
    x_segsum = x_segsum.masked_fill(~mask, -torch.inf)
    return x_segsum

def ssd(X, A, B, C, block_len=64, initial_states=None):
    """
    Arguments:
        X: (batch, length, n_heads, d_head)
        A: (batch, length, n_heads)
        B: (batch, length, n_heads, d_state)
        C: (batch, length, n_heads, d_state)
    Return:
        Y: (batch, length, n_heads, d_head)
    """
    assert X.dtype == A.dtype == B.dtype == C.dtype
    assert X.shape[1] % block_len == 0

    # Rearrange into blocks/chunks
    X, A, B, C = [rearrange(x, "b (c l) ... -> b c l ...", l=block_len)
                  for x in (X, A, B, C)]

    A = rearrange(A, "b c l h -> b h c l")
    A_cumsum = torch.cumsum(A, dim=-1)

    # 1. Compute the output for each intra-chunk (diagonal blocks)
    L = torch.exp(segsum(A))
    Y_diag = torch.einsum("bclhn,bcshn,bhcls,bcshp->bclhp", C, B, L, X)

    # 2. Compute the state for each intra-chunk
    # (right term of low-rank factorization of off-diagonal blocks; B terms)
    decay_states = torch.exp((A_cumsum[:, :, :, -1:] - A_cumsum))
    states = torch.einsum("bclhn,bhcl,bclhp->bchpn", B, decay_states, X)

    # 3. Compute the inter-chunk SSM recurrence; produces correct SSM states
    # at chunk boundaries (middle term of factorization of off-diag blocks;
    # A terms)
    if initial_states is None:
        initial_states = torch.zeros_like(states[:, :1])
    states = torch.cat([initial_states, states], dim=1)
    decay_chunk = torch.exp(segsum(F.pad(A_cumsum[:, :, :, -1], (1, 0))))
    new_states = torch.einsum("bhzc,bchpn->bzhpn", decay_chunk, states)
    states, final_state = new_states[:, :-1], new_states[:, -1]

    # 4. Compute state -> output conversion per chunk
    # (left term of low-rank factorization of off-diagonal blocks; C terms)
    state_decay_out = torch.exp(A_cumsum)
    Y_off = torch.einsum('bclhn,bchpn,bhcl->bclhp', C, states, state_decay_out)

    # Add output of intra-chunk and inter-chunk terms
    # (diagonal and off-diagonal blocks)
    Y = rearrange(Y_diag+Y_off, "b c l h p -> b (c l) h p")
    return Y, final_state

Line by line: Y_diag is step 1, contracting CC with BB over the state index n, masking by L, and contracting with X over the in-chunk position s. decay_states is the decay from each token to its chunk's end, and states is step 2. Step 3 prepends the initial state (zeros unless one is passed in), builds a 1-semiseparable mask over chunks from each chunk's total log-decay, and applies it with one small einsum; after this, states[:, c] is the true state entering chunk cc, and final_state is the state after the last token. Y_off is step 4. The paper calls this version “relatively efficient even in native PyTorch without requiring special low-level kernels”; the speed numbers later on are for a dedicated implementation, not for this listing.

The model calls the same function with concrete shapes. The listing below shows one Mamba-2 layer at the 2.7B model's width, with the official module's defaults:

# One Mamba-2 layer at d_model = 2560 (code defaults: expand 2, headdim 64,
# d_state 128, chunk_size 256), one sequence of T = 1024 tokens:
#   heads H = 2 * 2560 / 64 = 80,  P = 64,  N = 128,  Q = 256,  chunks c = 4
dt = softplus(dt_raw + dt_bias)        # (1, 1024, 80)    one step size per head
A  = -exp(A_log)                        # (80,)            learned, negative
ssd(X * dt[..., None], A * dt, B, C, block_len=256)   # log a_t = dt_t * A
#   X  (1, 4, 256, 80, 64)   after the chunk rearrange
#   L  (1, 80, 4, 256, 256)  one 256 x 256 mask per head per chunk
#   states      (1, 4, 80, 64, 128)  -> after the cat: (1, 5, 80, 64, 128)
#   decay_chunk (1, 80, 5, 5)
#   final_state (1, 80, 64, 128)     the state you would keep for generation

The step size Δt\Delta_t enters twice (this is the official test in ssd_minimal.py): it scales the input xtx_t, and it scales the log-decay, so log⁡at=ΔtA\log a_t = \Delta_t A. The model has one BB and one CC for all 80 heads (ngroups=1), so they reach the listing with a head axis of size 1, which the einsums broadcast; the official test calls it the same way. The SSD function has no weights of its own. In training, the gradient flows through it into the input projection that produced xx, BB, CC and Δ\Delta, into A_log and the step-size bias, and from there into everything upstream.

The Mamba-2 block

The SSD layer sits inside a block that also changed. In Mamba-1, an input projection makes the SSM input xx and a gate zz; xx passes through a short convolution; and then a second projection, x_proj, computes Δ\Delta, BB and CC from the convolved xx. Mamba-2 views the layer as a function of (A,X,B,C)(A, X, B, C), the way attention is a function of (Q,K,V)(Q, K, V), and produces all of its inputs with one projection at the start of the block (Section 7.1, Figure 6). In the official module that projection outputs zz, xx, BB, CC and Δ\Delta together, and the convolution runs over xx, BB and CC. As in Mamba-1, the module also adds a skip term D xtD\,x_t to the SSD output, with one learned DD per head.

Mamba-2 does this for tensor parallelism, which splits each large weight matrix of a layer across several GPUs. A Transformer's attention and MLP blocks need one all-reduce each under this scheme: the first projection is split by columns, so each GPU computes its own heads, and the last is split by rows, with one sum at the end. In Mamba-1, x_proj reads all channels of xx, and after the first projection those channels are spread across GPUs, so computing BB and CC needs communication in the middle of the block. In Mamba-2 every SSM input comes from the block's input, which each GPU already has, so the block needs one all-reduce, like attention (Figure 7 of the paper). The parallel projection also saves parameters: in the paper's block ablation (Table 4) it takes a 129.3M model to 126.5M and perplexity from 11.76 to 11.66.

The second change is an extra normalization after the output gate and before the output projection. The paper reports that preliminary experiments at larger scale were prone to instability and that this norm helps; in Table 4 it also improves perplexity (11.76 to 11.54 alone, 11.49 with both changes). TransNormerLLM and RetNet add a similar norm, but before the gate.5 The paper allows LayerNorm, GroupNorm or RMSNorm and uses GroupNorm under tensor parallelism, so that each GPU can normalize its own group of channels with no extra communication; the official module uses a gated RMSNorm applied after the gate (norm_before_gate=False). All three normalize each token's channels separately, as in layer normalization.

Figure 5 · Mamba-1 block versus Mamba-2 block
Toggle Mamba-1 and Mamba-2. With widths on, each arrow is labeled with its size per token at dmodel=2560d_{\text{model}} = 2560 under the code defaults: Mamba-2's single projection is 2×5120+2×128+80=105762 \times 5120 + 2 \times 128 + 80 = 10576 wide (gate, input, BB, CC, one Δ\Delta per head); Mamba-1's x_proj makes a rank-160 Δ\Delta and 16-wide BB and CC from the convolved input, in series. The per-layer states are 80 heads × 64 × 128 against 5120 × 16.

Those state sizes differ by a factor of 8: 655,360 numbers per layer for Mamba-2 against 81,920 for Mamba-1, at the same width. The paper's introduction describes this as states “8× the size of Mamba or even higher, with minimal slowdown”. The size of each head's state follows the attention analogy: BB and CC are shared by all heads (ngroups=1) while each head has its own xx and its own decay. The paper calls this pattern multi-value attention (MVA), or multi-input SSM, by analogy with multi-query attention, which shares keys and values instead. Mamba already used it, and it ablates best: at 125M parameters, perplexity 11.66 against 12.62 for sharing BB and XX (multi-query, one CC per head) and 12.59 for sharing CC and XX (multi-key, one BB per head), with the same total state size (Table 5).

State size and associative recall

A recurrent model keeps only what fits in its state; it cannot reread the input. Tasks that require storing many unrelated facts test that limit. Multi-query associative recall (MQAR) is the synthetic version: the model sees a list of key-value pairs, then a series of keys, and must output each key's value.7 The paper uses a harder setup than the task's original: sequence length 256, 512 or 1024 with a quarter as many key-value pairs (so 256 pairs at length 1024), a vocabulary of 8192, two-layer models, and model dimension 32 to 256. The baselines are softmax attention and Based, a linear-attention hybrid.

Two results (Figure 8 of the paper). First, larger state helps: the paper reports that raising NN from 16 to 64 to 256 consistently improves accuracy. The one exception in its plot is length 256 at model dimension 32, where N=64N = 64 reads 0. Second, at equal state size Mamba-2 is far ahead of Mamba-1. At N=16N = 16 and length 1024, Mamba-1 is at 0 for every model size while Mamba-2 climbs to 0.92 at dimension 256. The paper says it is not sure which part of the architecture causes this. Attention scores below Mamba-2's larger states on this task: at length 1024 it reaches about 0.27 at dimension 64 and 0.52 at 128, while Mamba-2 with N=64N = 64 or 256 is near 0.9 or above from dimension 64. The paper's caption summarizes Mamba-2 as performing “significantly better than Mamba-1 and even vanilla attention” on this task.

Figure 6 · MQAR accuracy by model dimension and state size
1024
32
Accuracy of two-layer models on MQAR, read off the paper's Figure 8. Drag sequence length (256, 512, 1024; the number of key-value pairs is a quarter of it), and hover a group or drag model dimension to read its six values. At length 1024, attention trails Mamba-2 at N=64N = 64 and N=256N = 256 at every model dimension. Mamba (N=16N = 16) is near 0 except at length 256 and dimension 256.

MQAR is a controlled test with tiny models. It shows that state size limits recall on this task and that Mamba-2 can afford a much larger state, but it does not settle how pure SSMs compare to attention on recall in large language models, which is one motivation for the hybrids tested below.

Speed: SSD against Mamba's scan and FlashAttention-2

The paper benchmarks the SSD layer on an A100 80GB PCIe (Figure 10). Against Mamba's fused scan at state size N=64N = 64, SSD is 2 to 8 times faster across sequence lengths. Both do work proportional to TT; SSD does it as matrix multiplication. Against FlashAttention-2, SSD is faster from sequence length 2K onward, and about 6 times faster at 16K, because its work grows like TT and attention's like T2T^2 while both run on tensor cores.

The second panel fixes the sequence length at 4K and varies NN. The scan's time grows linearly with NN, from about 0.45 ms at 16 to about 7.6 ms at 256. SSD goes from about 0.22 ms to about 0.68 ms over the same range, because a larger NN makes its matrix multiplications larger, which tensor cores absorb with little extra time. At N=64N = 64, SSD takes about 0.28 ms, less than the scan needs at N=16N = 16.

Figure 7 · time per layer against sequence length and state size
16k
Read off the paper's Figure 10 (A100 80GB PCIe). vs sequence length: log-log, N=64N = 64; drag sequence length from 512 to 512K to read the three times and the ratios to SSD. vs state size: linear axes, T=4KT = 4\text{K}, FlashAttention-2 dashed as a constant; at N=4N = 4 the scan is slightly faster than SSD. The two panels were run with settings the paper does not list, and their absolute times do not agree at T=4KT = 4\text{K}, N=64N = 64.

A faster layer does not make a faster model at every length. The paper notes that at short sequences (about 2K) a Mamba-2 model may train more slowly than a Transformer of the same size, because the Transformer spends half its layers on MLPs, which are pure matrix multiplication, while Mamba-2 uses SSD layers throughout. Mixing SSD and MLP layers is its suggested remedy.

Language modeling and hybrids

Scaling. The paper trains models from about 125M to 1.3B parameters on the Pile with the GPT-2 tokenizer, at the token counts the Chinchilla rules prescribe for each size, and compares them with Mamba and with Transformer++. Transformer++ is the strong Transformer recipe from the Mamba paper, based on PaLM and LLaMA: rotary position embeddings, a SwiGLU MLP, RMSNorm instead of LayerNorm, no bias terms, and a peak learning rate 5 times the GPT-3 value. Mamba-2 matches or exceeds both (Figure 9), and against the Transformer baseline it is better on perplexity, on theoretical FLOPs and on wall-clock time at once.

Downstream. At 2.7B parameters, trained for 300B tokens on the Pile with the GPT-NeoX tokenizer (the same data, tokenizer and length as Pythia), Mamba-2 reaches Pile perplexity 6.09, against 6.22 for Mamba-2.8B and 6.73 for Pythia-2.8B, and an average of 60.2 over seven zero-shot tasks (LAMBADA, HellaSwag, PIQA, ARC-easy, ARC-challenge, WinoGrande, OpenBookQA) against 59.9 and 55.7. It beats Pythia-2.8B on every task. Against Mamba-2.8B it is not a sweep: Mamba is ahead on ARC-easy (69.7 against 69.6) and OpenBookQA (39.6 against 38.8). The appendix's full table adds Pythia-6.9B, which Mamba-2-2.7B beats on every column, with average accuracy 60.2 against 58.3. The paper's summary for each size is that Mamba-2 outperforms Mamba and generally matches Pythia at twice the size.

Hybrids. In a 350M model with 48 layers trained to 7B tokens, replacing some SSD layers with attention layers lowers perplexity from 8.60 (pure Mamba-2) to 8.38 with a single attention layer, and to a minimum of 8.26 with six. Past that it rises again, to 8.50 at 24. Transformer++ gets 8.68. The paper reads this as around 10% of layers being attention (6 of 48 is 12.5%) and hypothesizes that attention acts as a retrieval mechanism, letting the model refer back to an exact earlier token instead of relying on what the SSM state kept.

Figure 8 · how many attention layers a hybrid needs
6
The paper's Table 2. Drag attention layers through the counts it tested (0 to 24 of 48). The strip shows the 48 layers with the attention layers spaced out; the paper gives exact positions only for its 2.7B hybrid and reports that placement matters little as long as the layers are spaced out and not at the very start or end. The dashed line is Transformer++ at 8.68.

At 2.7B and 300B tokens (all variants 64 layers deep, same data order), pure Mamba-2 gets Pile perplexity 6.09, Transformer++ (32 attention and 32 MLP layers) 6.13, and Mamba-2-Attention, with 6 attention layers at positions 9, 18, 27, 36, 45 and 56 among 58 SSD layers, gets 5.95 and the best average accuracy of the five variants, 61.0.

What the paper does not show

The cost of the scalar decay. No experiment compares a scalar AtA_t with a diagonal one under otherwise equal conditions. Every Mamba-2 versus Mamba comparison changes the state size, the head dimension, the block and the kernel together, so the gains cannot be credited to any one of them, and a task that needs different memory lengths in different state slots at the same token could lose from the restriction. The paper hypothesizes that its structured-matrix algorithms could be extended to the general diagonal case.

Recall at long range. The longest MQAR sequence is 1024 tokens. The paper makes no claim either way about recall at much longer lengths, where a fixed-size state has to hold more pairs.

Model-level speed. The 2 to 8 times and the 6 times are per-layer measurements. As noted above, the paper cautions that a whole Mamba-2 model might train less efficiently than a Transformer at short sequence lengths.

Systems work. Tensor parallelism, sequence parallelism and variable-length batching all had to be adapted to SSMs (Section 8). For sequence parallelism, each GPU takes a stretch of the sequence, runs it from an incoming state, and passes its final state to the next GPU, which is the chunked algorithm at the scale of devices and needs communication linear in the number of GPUs.

Provenance Verified against primary literatureHow we verify
Gu & Dao (2023), MambaThe selective SSM with input-dependent step size, B and C, a diagonal A per channel (d_state 16 by default), and the fused hardware-aware scan that Mamba-2 is benchmarked against.
Katharopoulos et al. (2020), linear attentionDropping the softmax and using associativity turns causal (L o QK^T)V with an all-ones lower-triangular L into a recurrence. SSD is the case where L is a 1-semiseparable mask built from data-dependent decays.
Pernet, Signargout & Villard (2023)Proposition 3.6 of the paper: an N-semiseparable T x T matrix has an O(NT) representation and O(NT) matrix-vector multiplication (arXiv:2302.04515, "Exact computations with quasiseparable matrices").
state-spaces/mamba, mamba_ssm/modules/mamba2.pyDefaults d_state=128, expand=2, headdim=64, ngroups=1, norm_before_gate=False, chunk_size=256 (lines 41-59). A = -exp(A_log) per head (line 182). in_proj width 2*d_inner + 2*ngroups*d_state + nheads (line 96). Gated RMSNorm (lines 143-145).
state-spaces/mamba, mamba_ssm/modules/ssd_minimal.pyThe paper's Listing 1 as a runnable file. Its test (line 103) checks the fused kernel against ssd_minimal_discrete(x*dt, A*dt, B, C, chunk_size): the step size scales both the input and the log-decay.
mamba_ssm/modules/mamba_simple.py (Mamba-1)d_state=16, expand=2, dt_rank = ceil(d_model/16); x_proj maps the convolved x to dt, B and C; dt_proj expands dt back to d_inner.
Figures 8 and 10 of the paperMQAR accuracies and benchmark times exist only as plots. The values in the figures on this page were read off the rendered PDF (v1, pp. 28-29): about +-0.02 accuracy, about 15% on the log-time panel.
correctionThree slips in the paper, none of which changes its results. (1) Section 7.1 says "X, B, C correspond to the Q, K, V projections", which pairs them in the wrong order; the paper's own mapping table (Figure 4) and Section 9.4.2 ("the number of C, B, X (analogous to Q, K, V) heads") give C to Q, B to K, X to V, which is what this page uses. (2) Section 8.3 says variable-length packing sets "A_t = 0 for tokens t at the end of one sequence to prevent it from passing information to the token t + 1". Under the paper's recurrence h_t = A_t h_(t-1) + B_t x_t, the factor that carries the state from token t into token t + 1 is A_(t+1), so the zero belongs on the first token of the next sequence; A_t = 0 on the last token would instead cut that token off from its own sequence. (3) Theorem 6.1 states O(TN) inference FLOPs, but the comparison table in the same section lists N^2 inference FLOPs for both SSM and SSD, which is what one recurrent step costs when the head dimension P equals N (an N x P state update). This page uses N x P per generated token.

Questions you might still have

?

Is Mamba-2 the same model as Mamba with a faster kernel?
No. The layer is different in two ways that change the function it computes: the per-step decay is one scalar per head instead of one number per state slot and channel, and a head carries a 64- or 128-dimensional value instead of a single channel. The block is also different (one input projection, a norm after the gate). The SSD algorithm computes this new layer; it does not compute Mamba-1's diagonal-A layer.

?

Does the scalar decay cost quality?
The paper calls it "slightly decreasing the expressive power" and does not measure it in isolation. Its Mamba-2 versus Mamba comparisons change the state size (128 versus 16 in the code defaults), the head dimension, the block and the algorithm at the same time. The paper measures only the combined change: at 2.7B and 300B tokens Mamba-2 reaches Pile perplexity 6.09 against Mamba's 6.22, and on MQAR Mamba-2 at N = 16 beats Mamba at N = 16.

?

If SSD is masked attention, is Mamba-2 a Transformer?
It shares one computation with a softmax-free Transformer: y = (L o QK^T)V. It does not share the softmax, and it does not keep a cache of every past token. At generation time it runs in recurrent mode with a fixed N x P state per head (64 x 128 numbers in the code defaults), so memory does not grow with the length of the context. That is why it needs the larger N to do recall well, and why the paper tests hybrids that add a few real attention layers.

?

How does Mamba-2 generate text one token at a time?
In recurrent mode. For each head it keeps the N x P state h, and for a new token it computes a_t, B_t, C_t and x_t from the input projection, updates h = a_t h + B_t x_t^T, and reads y_t = C_t^T h. That costs N x P multiply-adds per head per token and never looks back at earlier tokens. The chunked algorithm is for training and for processing a prompt, where all T tokens are available at once; its final_state output is the h that generation continues from.

?

Why is SSD faster than FlashAttention-2 only from about 2K tokens?
Both run on tensor cores, so the comparison is mostly about work. Attention does work proportional to T^2 and SSD proportional to T, but SSD has a larger constant (the state is N x P per head, and it runs a short scan between chunks). At 1K tokens the paper's plot has FlashAttention-2 slightly ahead; from 2K onward SSD is ahead, by about 6x at 16K.

?

How should the chunk length Q be chosen?
The paper's cost analysis sets Q equal to N and P so every matrix multiply is roughly square; the Listing uses 64 and the official module uses 256. Smaller Q makes the scan between chunks longer and the matmuls smaller; larger Q makes the diagonal blocks cost grow like T times Q. The chunked-algorithm figure on this page shows both ends.

?

Why would a hybrid with a few attention layers beat both pure models?
The paper offers a hypothesis, not a proof: the SSM layers work as general sequence mixers, and the attention layers let the model look up an exact earlier token instead of relying on what survived compression into the state. In its 350M ablation, 6 attention layers out of 48 gave the lowest perplexity (8.26), against 8.60 for pure Mamba-2 and 8.68 for Transformer++.

Footnotes & further reading

  1. The paper: Dao, Gu, Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality (Princeton & CMU, ICML 2024). Code, including mamba2.py and ssd_minimal.py.
  2. The predecessor: Gu, Dao, Mamba: Linear-Time Sequence Modeling with Selective State Spaces. The Mamba explainer covers the selective scan and the input-dependent Δ,B,C\Delta, B, C; S4 covers the time-invariant SSMs before it, and Mamba-3 the follow-up.
  3. Linear attention: Katharopoulos et al., Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention. The Mamba-2 title answers it. Softmax attention itself is covered in Attention Is All You Need.
  4. Semiseparable matrices: Pernet, Signargout, Villard, Exact computations with quasiseparable matrices (2023), the source of the O(NT)O(NT) result the paper cites as Proposition 3.6; and the textbook by Vandebril, Van Barel and Mastronardi, Matrix Computations and Semiseparable Matrices.
  5. The late-normalization relatives: Qin et al., TransNormerLLM, and Sun et al., RetNet.
  6. Tensor-core and non-tensor throughput: NVIDIA, A100 Tensor Core GPU datasheet (FP32 19.5 TFLOPS; BF16/FP16 Tensor Core 312 TFLOPS, dense). The IO-aware attention kernel SSD is compared against: Dao, FlashAttention-2, and the FlashAttention explainer.
  7. The MQAR task: Arora et al., Zoology: Measuring and Improving Recall in Efficient Language Models.
  8. The authors' own walk through SSD: State Space Duality (Mamba-2) on the Goomba Lab blog.