From 031a652b936ce0a3e468b2de7411293b05d1c189 Mon Sep 17 00:00:00 2001 From: debo3 <128179937+debo3@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:11:16 -0800 Subject: [PATCH] Fix TRT-LLM MLA backend applying k_scale to BF16 KV cache in BMM1 (#18396) --- .../layers/attention/trtllm_mla_backend.py | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/python/sglang/srt/layers/attention/trtllm_mla_backend.py b/python/sglang/srt/layers/attention/trtllm_mla_backend.py index de8f7983f..4f81a46f0 100755 --- a/python/sglang/srt/layers/attention/trtllm_mla_backend.py +++ b/python/sglang/srt/layers/attention/trtllm_mla_backend.py @@ -4,6 +4,7 @@ from __future__ import annotations Support attention backend for TRTLLM MLA kernels from flashinfer. """ +import logging import math from dataclasses import dataclass from typing import TYPE_CHECKING, Optional, Union @@ -35,6 +36,8 @@ if TYPE_CHECKING: from sglang.srt.model_executor.model_runner import ModelRunner from sglang.srt.speculative.spec_info import SpecInput +logger = logging.getLogger(__name__) + _is_cuda = is_cuda() if _is_cuda: @@ -909,15 +912,26 @@ class TRTLLMMLABackend(FlashInferMLAAttnBackend): # The final BMM1 scale is computed as: q_scale * k_scale * softmax_scale # Scale components: # - q_scale: Query scaling factor (set to 1.0 for both FP16/FP8 paths) - # - k_scale: Key scaling factor from model checkpoint (defaults to 1.0 if not available) + # - k_scale: Key scaling factor from model checkpoint. Only applied when KV cache + # stores FP8-quantized values, to compensate for the quantization scaling. + # For BF16/FP16 KV cache, k_scale must be 1.0 since values are unscaled. # - softmax_scale: Attention softmax scaling = 1/sqrt(head_dim), pre-computed as layer.scaling - # This unified approach works for both FP16 and FP8 quantized attention paths. q_scale = 1.0 - k_scale = ( - layer.k_scale_float - if getattr(layer, "k_scale_float", None) is not None - else 1.0 - ) + if self.data_type == torch.float8_e4m3fn: + k_scale = ( + layer.k_scale_float + if getattr(layer, "k_scale_float", None) is not None + else 1.0 + ) + else: + if getattr(layer, "k_scale_float", None) is not None: + logger.warning_once( + "Checkpoint has k_scale but KV cache dtype is not FP8. " + "Ignoring k_scale for BMM1 (k_scale=%.4f, kv_dtype=%s).", + layer.k_scale_float, + self.data_type, + ) + k_scale = 1.0 bmm1_scale = q_scale * k_scale * layer.scaling @@ -1033,11 +1047,21 @@ class TRTLLMMLABackend(FlashInferMLAAttnBackend): kv_cache = k_cache.view(-1, self.page_size, self.kv_cache_dim).unsqueeze(1) q_scale = 1.0 - k_scale = ( - layer.k_scale_float - if getattr(layer, "k_scale_float", None) is not None - else 1.0 - ) + if self.data_type == torch.float8_e4m3fn: + k_scale = ( + layer.k_scale_float + if getattr(layer, "k_scale_float", None) is not None + else 1.0 + ) + else: + if getattr(layer, "k_scale_float", None) is not None: + logger.warning_once( + "Checkpoint has k_scale but KV cache dtype is not FP8. " + "Ignoring k_scale for BMM1 (k_scale=%.4f, kv_dtype=%s).", + layer.k_scale_float, + self.data_type, + ) + k_scale = 1.0 q = q.to(self.data_type) bmm1_scale = q_scale * k_scale * layer.scaling