mimo26-fp8-oproj.diff 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. diff --git a/python/sglang/srt/layers/quantization/mimo26_fp8_serve.py b/python/sglang/srt/layers/quantization/mimo26_fp8_serve.py
  2. new file mode 100644
  3. index 0000000000..2aa32bfa98
  4. --- /dev/null
  5. +++ b/python/sglang/srt/layers/quantization/mimo26_fp8_serve.py
  6. @@ -0,0 +1,50 @@
  7. +"""Serve checkpoint-BF16 linear layers as 128x128 block FP8 (MiMo-V2.6 on SM121).
  8. +
  9. +Only the module quant_method is swapped, so the parallel linear keeps its own
  10. +forward: input splitting, bias handling and any in-layer all-reduce are unchanged.
  11. +Scales are fp32 amax/448 per block; the GEMM is the same Triton block-FP8 kernel
  12. +that serves the checkpoint FP8 layers.
  13. +"""
  14. +import logging
  15. +
  16. +import torch
  17. +
  18. +logger = logging.getLogger(__name__)
  19. +
  20. +
  21. +class _BlockFp8Apply:
  22. + def __init__(self, base):
  23. + self.base = base
  24. +
  25. + def apply(self, layer, x, bias=None):
  26. + from sglang.srt.layers.quantization.fp8_utils import triton_w8a8_block_fp8_linear
  27. +
  28. + out = triton_w8a8_block_fp8_linear(x, layer._mimo26_fp8_weight, [128, 128], layer._mimo26_fp8_scale)
  29. + return out if bias is None else out + bias
  30. +
  31. + def __getattr__(self, name):
  32. + return getattr(self.base, name)
  33. +
  34. +
  35. +def serve_linear_as_block_fp8(mod) -> bool:
  36. + w = getattr(mod, "weight", None)
  37. + if w is None or w.dtype != torch.bfloat16 or w.dim() != 2 or not hasattr(mod, "quant_method"):
  38. + return False
  39. + if type(mod.quant_method).__name__ != "UnquantizedLinearMethod":
  40. + return False
  41. + out_f, in_f = w.shape
  42. + if out_f % 128 or in_f % 128:
  43. + return False
  44. + blocks = w.data.float().view(out_f // 128, 128, in_f // 128, 128)
  45. + scale = (blocks.abs().amax(dim=(1, 3), keepdim=True) / 448.0).clamp(min=1e-12)
  46. + q = (blocks / scale).to(torch.float8_e4m3fn).view(out_f, in_f).contiguous()
  47. + mod.register_buffer("_mimo26_fp8_weight", q, persistent=False)
  48. + mod.register_buffer("_mimo26_fp8_scale", scale.view(out_f // 128, in_f // 128).contiguous(), persistent=False)
  49. + mod.quant_method = _BlockFp8Apply(mod.quant_method)
  50. + return True
  51. +
  52. +
  53. +def serve_modules_as_block_fp8(model, predicate, label):
  54. + n = sum(serve_linear_as_block_fp8(m) for name, m in model.named_modules() if predicate(name))
  55. + logger.info("mimo26: serving %d %s linear layers as block FP8", n, label)
  56. + return n
  57. diff --git a/python/sglang/srt/models/dflash.py b/python/sglang/srt/models/dflash.py
  58. index 5722b43d45..cd5c6cc6ae 100644
  59. --- a/python/sglang/srt/models/dflash.py
  60. +++ b/python/sglang/srt/models/dflash.py
  61. @@ -880,6 +880,18 @@ class DFlashDraftModel(nn.Module):
  62. f"{sorted(missing)}."
  63. )
  64. + import os as _os
  65. +
  66. + if _os.environ.get("MIMO26_FP8_DRAFT", "0") == "1":
  67. + from sglang.srt.layers.quantization.mimo26_fp8_serve import serve_modules_as_block_fp8
  68. +
  69. + serve_modules_as_block_fp8(
  70. + self,
  71. + lambda n: n.startswith("layers.")
  72. + and n.rsplit(".", 1)[-1] in ("o_proj", "gate_up_proj", "down_proj"),
  73. + "DFlash draft",
  74. + )
  75. +
  76. class DFlashLagunaAttention(DFlashAttention):
  77. """Laguna DFlash attention with the trained Laguna softplus gate."""
  78. diff --git a/python/sglang/srt/models/mimo_v2.py b/python/sglang/srt/models/mimo_v2.py
  79. index 6f67bef0d5..bb48d20564 100644
  80. --- a/python/sglang/srt/models/mimo_v2.py
  81. +++ b/python/sglang/srt/models/mimo_v2.py
  82. @@ -13,6 +13,7 @@
  83. # ==============================================================================
  84. import logging
  85. +import os
  86. import math
  87. import re
  88. from array import array
  89. @@ -1708,6 +1709,14 @@ class MiMoV2ForCausalLM(nn.Module, AudioEncoderMixin):
  90. expected_fused_tp_size,
  91. config=self.config,
  92. )
  93. + if os.environ.get("MIMO26_FP8_OPROJ", "0") == "1":
  94. + from sglang.srt.layers.quantization.mimo26_fp8_serve import serve_modules_as_block_fp8
  95. +
  96. + serve_modules_as_block_fp8(
  97. + self,
  98. + lambda n: n.startswith("model.layers.") and n.endswith("self_attn.o_proj"),
  99. + "attention o_proj",
  100. + )
  101. def get_embed_and_head(self):
  102. assert self.model is not None and self.lm_head is not None, (