#018

mla_decode_attention

bf16 aiter · decode · aiter.mla · importance 2.3%

Reference Implementation

reference.py
import torch
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):

    def __init__(self, nhead: int, kv_lora_rank: int, qk_rope_head_dim: int) -> None:
        super().__init__()
        self.nhead = nhead
        self.kv_lora_rank = kv_lora_rank
        self.qk_rope_head_dim = qk_rope_head_dim
        self.qk_head_dim = kv_lora_rank + qk_rope_head_dim
        self.v_head_dim = kv_lora_rank

    def forward(self, q: torch.Tensor, kv_cache: torch.Tensor, seq_lens: torch.Tensor) -> torch.Tensor:
        batch_size = seq_lens.shape[0]
        out = torch.empty(batch_size, self.nhead, self.v_head_dim, dtype=torch.float32, device=q.device)
        seq_lens_list = seq_lens.tolist()
        kv_offset = 0
        for i in range(batch_size):
            seq_len = int(seq_lens_list[i])
            kvc_i = kv_cache[kv_offset:kv_offset + seq_len].float()
            q_i = q[i].float().unsqueeze(0).unsqueeze(2)
            k_i = kvc_i.permute(1, 0, 2).expand(self.nhead, seq_len, self.qk_head_dim).unsqueeze(0)
            v_i = kvc_i[..., :self.kv_lora_rank].permute(1, 0, 2).expand(self.nhead, seq_len, self.v_head_dim).unsqueeze(0)
            o_i = F.scaled_dot_product_attention(q_i, k_i, v_i, is_causal=False)
            out[i] = o_i.squeeze(0).squeeze(1)
            kv_offset += seq_len
        return out.to(q.dtype)

Shapes

TSOL hardware:
# batch_sizectx_lennheadkv_lora_rankqk_rope_head_dim TSOL(XPU-A)TProdS
0 412812851264 0.78 us 46.70 us 1.7%
1 12561651264 0.06 us 19.00 us 0.3%
2 72561651264 0.44 us 21.00 us 2.1%

Input Generation

input.py
import torch


def _make_inputs(batch_size: int, ctx_len: int, nhead: int, kv_lora_rank: int, qk_rope_head_dim: int) -> dict[str, torch.Tensor]:
    qk_head_dim = kv_lora_rank + qk_rope_head_dim
    q = torch.randn(batch_size, nhead, qk_head_dim, dtype=torch.bfloat16, device='cuda')
    total_kv = batch_size * ctx_len
    kv_cache = torch.randn(total_kv, 1, kv_lora_rank + qk_rope_head_dim, dtype=torch.bfloat16, device='cuda')
    seq_lens = torch.full((batch_size,), ctx_len, dtype=torch.int32, device='cuda')
    return {'q': q, 'kv_cache': kv_cache, 'seq_lens': seq_lens}