Skip to content

Commit aab72d9

Browse files
authored
perf(quant): 融合 SwiGLU 激活与 FP8 输入量化 (#1679)
1 parent 269ab62 commit aab72d9

3 files changed

Lines changed: 113 additions & 2 deletions

File tree

‎lightllm/common/basemodel/triton_kernel/fused_moe/moe_silu_and_mul.py‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,93 @@
66
from lightllm.utils.config_utils import ffn_use_tanh_approximate_gelu
77

88

9+
@triton.jit
10+
def _silu_and_mul_quant_fp8(
11+
input_ptr,
12+
quant_ptr,
13+
scale_ptr,
14+
input_stride: tl.constexpr,
15+
size_n: tl.constexpr,
16+
groups,
17+
scale_stride_n,
18+
limit,
19+
fp8_max: tl.constexpr,
20+
GROUP_SIZE: tl.constexpr,
21+
GROUPS_PER_CTA: tl.constexpr,
22+
USE_LIMIT: tl.constexpr,
23+
USE_GELU: tl.constexpr,
24+
):
25+
ids = tl.program_id(0) * GROUPS_PER_CTA + tl.arange(0, GROUPS_PER_CTA)
26+
rows = ids // (size_n // GROUP_SIZE)
27+
group_cols = ids % (size_n // GROUP_SIZE)
28+
cols = tl.arange(0, GROUP_SIZE)[None, :]
29+
offsets = rows[:, None] * input_stride + group_cols[:, None] * GROUP_SIZE + cols
30+
mask = ids[:, None] < groups
31+
gate = tl.load(input_ptr + offsets, mask, 0).to(tl.float32)
32+
up = tl.load(input_ptr + offsets + size_n, mask, 0).to(tl.float32)
33+
if USE_LIMIT:
34+
gate = tl.minimum(gate, limit)
35+
up = tl.minimum(tl.maximum(up, -limit), limit)
36+
if USE_GELU:
37+
cubed = gate * gate * gate
38+
arg = 0.7978845608028654 * (gate + 0.044715 * cubed)
39+
gate = 0.5 * gate * (1.0 + (2.0 / (1.0 + tl.exp(-2.0 * arg)) - 1.0))
40+
else:
41+
gate = gate / (1 + tl.exp(-gate))
42+
gate = gate.to(input_ptr.dtype.element_ty)
43+
# Preserve both rounding boundaries of activation -> BF16 buffer -> FP8.
44+
value = (up * gate.to(tl.float32)).to(input_ptr.dtype.element_ty).to(tl.float32)
45+
scale = tl.maximum(tl.max(tl.abs(value), axis=1), 1e-10) / fp8_max
46+
scale = tl.exp2(tl.ceil(tl.log2(scale)))
47+
quant = tl.clamp(value / scale[:, None], -fp8_max, fp8_max).to(quant_ptr.dtype.element_ty)
48+
tl.store(quant_ptr + ids[:, None] * GROUP_SIZE + cols, quant, mask)
49+
tl.store(scale_ptr + rows + group_cols * scale_stride_n, scale, ids < groups)
50+
51+
52+
@autotune(
53+
kernel_name="silu_and_mul_quant_fp8:v1",
54+
configs_gen_func=lambda: [
55+
{"GROUPS_PER_CTA": g, "num_warps": w} for g, w in [(1, 1), (4, 1), (4, 4), (8, 4), (16, 4)]
56+
],
57+
static_key_func=lambda input, group_size, dtype, limit: {
58+
"N": input.shape[1] // 2,
59+
"input_dtype": str(input.dtype),
60+
"output_dtype": str(dtype),
61+
"group_size": group_size,
62+
"clamp": limit is not None,
63+
"gelu": ffn_use_tanh_approximate_gelu() and limit is None,
64+
},
65+
run_key_func=lambda input: input.shape[0],
66+
)
67+
def silu_and_mul_quant_fp8(input, group_size, dtype, limit=None, alloc_func=torch.empty, run_config=None):
68+
"""Fuse blocked activation with FP8 quantization and TMA-aligned UE8M0 scales."""
69+
assert input.ndim == 2 and input.stride(1) == 1
70+
assert input.shape[1] % (2 * group_size) == 0 and group_size == triton.next_power_of_2(group_size)
71+
m, n = input.shape[0], input.shape[1] // 2
72+
quant = alloc_func((m, n), device=input.device, dtype=dtype)
73+
aligned_m = triton.cdiv(m, 4) * 4
74+
scale = alloc_func((n // group_size, aligned_m), device=input.device, dtype=torch.float32).T[:m, :]
75+
if run_config is None:
76+
run_config = {"GROUPS_PER_CTA": 4, "num_warps": 4}
77+
groups = m * n // group_size
78+
_silu_and_mul_quant_fp8[(triton.cdiv(groups, run_config["GROUPS_PER_CTA"]),)](
79+
input,
80+
quant,
81+
scale,
82+
input.stride(0),
83+
n,
84+
groups,
85+
scale.stride(1),
86+
limit,
87+
torch.finfo(dtype).max,
88+
group_size,
89+
USE_LIMIT=limit is not None,
90+
USE_GELU=ffn_use_tanh_approximate_gelu() and limit is None,
91+
**run_config,
92+
)
93+
return quant, scale
94+
95+
996
@triton.jit
1097
def _silu_and_mul_kernel_fast(
1198
input_ptr,

‎lightllm/common/quantization/deepgemm.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def apply(
9797
) -> torch.Tensor:
9898
qweight = weight_pack.weight
9999
weight_scale = weight_pack.weight_scale
100-
input_scale = None
100+
input_scale = getattr(input_tensor, "scale", None)
101101
alloc_func = torch.empty if not use_custom_tensor_mananger else self.cache_manager.empty
102102
m, k = input_tensor.shape
103103
n = qweight.shape[0]
@@ -112,6 +112,9 @@ def apply(
112112
use_ue8m0_scales=self.use_ue8m0_scales,
113113
use_packed_ue8m0=self.use_packed_ue8m0,
114114
)
115+
else:
116+
assert out is not None, "prequantized input requires an explicit floating-point output"
117+
qinput_tensor = input_tensor
115118

116119
if out is None:
117120
out = alloc_func((m, n), dtype=input_tensor.dtype, device=input_tensor.device)

‎lightllm/models/deepseek_v4/layer_infer/transformer_layer_infer.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
from lightllm.common.basemodel.attention.base_att import AttControl
55
from lightllm.common.basemodel.triton_kernel.mhc import hc_post, hc_post_pre_norm, hc_pre_norm
66
from lightllm.common.basemodel.triton_kernel.fused_moe.grouped_fused_moe_ep import use_sm100_mega_moe
7-
from lightllm.common.basemodel.triton_kernel.fused_moe.moe_silu_and_mul import silu_and_mul_fwd
7+
from lightllm.common.basemodel.triton_kernel.fused_moe.moe_silu_and_mul import (
8+
silu_and_mul_fwd,
9+
silu_and_mul_quant_fp8,
10+
)
11+
from lightllm.common.quantization.deepgemm import DeepGEMMFP8w8a8B128QuantizationMethod
812
from lightllm.models.deepseek_v4.layer_weights.transformer_layer_weight import DeepseekV4TransformerLayerWeight
913
from lightllm.utils.envs_utils import get_env_start_args
1014
from lightllm.utils.dist_utils import get_global_world_size
@@ -534,6 +538,23 @@ def _token_attention_kernel(
534538
def _ffn_tp(self, input, infer_state: DeepseekV4InferStateInfo, layer_weight: DeepseekV4TransformerLayerWeight):
535539
input = input.view(-1, self.embed_dim_)
536540
gate_up = layer_weight.gate_up_proj.mm(input)
541+
if (
542+
isinstance(layer_weight.down_proj.quant_method, DeepGEMMFP8w8a8B128QuantizationMethod)
543+
and layer_weight.down_proj.quant_method.use_ue8m0_scales
544+
and not layer_weight.down_proj.quant_method.use_packed_ue8m0
545+
):
546+
shared, scale = silu_and_mul_quant_fp8(
547+
gate_up,
548+
layer_weight.down_proj.quant_method.block_size,
549+
layer_weight.down_proj.mm_param.weight.dtype,
550+
limit=self.swiglu_limit,
551+
alloc_func=self.alloc_tensor,
552+
)
553+
shared.scale = scale
554+
del gate_up
555+
# FP8 输入不再携带原始激活 dtype,由调用方保留浮点输出类型。
556+
out = self.alloc_tensor((input.size(0), layer_weight.down_proj.mm_param.weight.shape[0]), input.dtype)
557+
return layer_weight.down_proj.mm(shared, out=out)
537558
shared = self.alloc_tensor((input.size(0), gate_up.size(1) // 2), input.dtype)
538559
silu_and_mul_fwd(
539560
gate_up,

0 commit comments

Comments
 (0)