[AMD] Fuse sigmoid + mul attention output gate into single Triton kernel (#27630)
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from sglang.test.ci.ci_register import register_amd_ci, register_cuda_ci
|
||||||
|
|
||||||
|
register_cuda_ci(est_time=4, suite="base-b-kernel-unit-1-gpu-large")
|
||||||
|
register_amd_ci(est_time=4, suite="jit-kernel-unit-test-amd")
|
||||||
|
|
||||||
|
DEVICE = "cuda"
|
||||||
|
|
||||||
|
|
||||||
|
def reference_sigmoid_gate_mul(x, gate):
|
||||||
|
return x * torch.sigmoid(gate)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("dtype", [torch.bfloat16, torch.float16, torch.float32])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"shape",
|
||||||
|
[
|
||||||
|
(1, 4096),
|
||||||
|
(4, 4096),
|
||||||
|
(8, 4096),
|
||||||
|
(32, 8192),
|
||||||
|
(1, 128),
|
||||||
|
(16, 16384),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_sigmoid_gate_mul_correctness(shape, dtype):
|
||||||
|
from sglang.jit_kernel.triton.sigmoid_gate_mul import sigmoid_gate_mul
|
||||||
|
|
||||||
|
torch.manual_seed(42)
|
||||||
|
x = torch.randn(shape, dtype=dtype, device=DEVICE)
|
||||||
|
gate = torch.randn(shape, dtype=dtype, device=DEVICE)
|
||||||
|
|
||||||
|
ref = reference_sigmoid_gate_mul(x, gate)
|
||||||
|
out = sigmoid_gate_mul(x, gate)
|
||||||
|
|
||||||
|
rtol = 1e-2 if dtype == torch.bfloat16 else 1e-3
|
||||||
|
atol = 2e-2 if dtype == torch.bfloat16 else 1e-3
|
||||||
|
torch.testing.assert_close(out, ref, rtol=rtol, atol=atol)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("shape", [(4, 4096), (1, 128)])
|
||||||
|
def test_sigmoid_gate_mul_does_not_modify_inputs(shape):
|
||||||
|
from sglang.jit_kernel.triton.sigmoid_gate_mul import sigmoid_gate_mul
|
||||||
|
|
||||||
|
torch.manual_seed(42)
|
||||||
|
x = torch.randn(shape, dtype=torch.bfloat16, device=DEVICE)
|
||||||
|
gate = torch.randn(shape, dtype=torch.bfloat16, device=DEVICE)
|
||||||
|
x_orig = x.clone()
|
||||||
|
gate_orig = gate.clone()
|
||||||
|
|
||||||
|
sigmoid_gate_mul(x, gate)
|
||||||
|
|
||||||
|
torch.testing.assert_close(x, x_orig, rtol=0, atol=0)
|
||||||
|
torch.testing.assert_close(gate, gate_orig, rtol=0, atol=0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_sigmoid_gate_mul_output_dtype():
|
||||||
|
from sglang.jit_kernel.triton.sigmoid_gate_mul import sigmoid_gate_mul
|
||||||
|
|
||||||
|
for dtype in [torch.bfloat16, torch.float16, torch.float32]:
|
||||||
|
x = torch.randn(4, 4096, dtype=dtype, device=DEVICE)
|
||||||
|
gate = torch.randn(4, 4096, dtype=dtype, device=DEVICE)
|
||||||
|
out = sigmoid_gate_mul(x, gate)
|
||||||
|
assert out.dtype == dtype, f"Expected {dtype}, got {out.dtype}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_sigmoid_gate_mul_contiguous_output():
|
||||||
|
from sglang.jit_kernel.triton.sigmoid_gate_mul import sigmoid_gate_mul
|
||||||
|
|
||||||
|
x = torch.randn(4, 4096, dtype=torch.bfloat16, device=DEVICE)
|
||||||
|
gate = torch.randn(4, 4096, dtype=torch.bfloat16, device=DEVICE)
|
||||||
|
out = sigmoid_gate_mul(x, gate)
|
||||||
|
assert out.is_contiguous()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-v"]))
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import torch
|
||||||
|
import triton
|
||||||
|
import triton.language as tl
|
||||||
|
|
||||||
|
|
||||||
|
@triton.jit
|
||||||
|
def _sigmoid_gate_mul_kernel(
|
||||||
|
x_ptr,
|
||||||
|
gate_ptr,
|
||||||
|
out_ptr,
|
||||||
|
n_elements,
|
||||||
|
BLOCK_SIZE: tl.constexpr,
|
||||||
|
):
|
||||||
|
pid = tl.program_id(0)
|
||||||
|
offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
|
||||||
|
mask = offsets < n_elements
|
||||||
|
x = tl.load(x_ptr + offsets, mask=mask).to(tl.float32)
|
||||||
|
g = tl.load(gate_ptr + offsets, mask=mask).to(tl.float32)
|
||||||
|
out = x * tl.sigmoid(g)
|
||||||
|
tl.store(out_ptr + offsets, out.to(x_ptr.dtype.element_ty), mask=mask)
|
||||||
|
|
||||||
|
|
||||||
|
def sigmoid_gate_mul(x: torch.Tensor, gate: torch.Tensor) -> torch.Tensor:
|
||||||
|
"""Compute x * sigmoid(gate) in a single fused kernel."""
|
||||||
|
out = torch.empty_like(x)
|
||||||
|
n = x.numel()
|
||||||
|
grid = lambda meta: (triton.cdiv(n, meta["BLOCK_SIZE"]),)
|
||||||
|
_sigmoid_gate_mul_kernel[grid](x, gate, out, n, BLOCK_SIZE=1024)
|
||||||
|
return out
|
||||||
@@ -970,8 +970,15 @@ class Qwen3_5AttentionDecoderLayer(nn.Module):
|
|||||||
attn_output = self.attn(q, k, v, forward_batch)
|
attn_output = self.attn(q, k, v, forward_batch)
|
||||||
|
|
||||||
if self.attn_output_gate:
|
if self.attn_output_gate:
|
||||||
gate = torch.sigmoid(gate)
|
if _is_hip:
|
||||||
attn_output = attn_output * gate
|
from sglang.jit_kernel.triton.sigmoid_gate_mul import (
|
||||||
|
sigmoid_gate_mul,
|
||||||
|
)
|
||||||
|
|
||||||
|
attn_output = sigmoid_gate_mul(attn_output, gate)
|
||||||
|
else:
|
||||||
|
gate = torch.sigmoid(gate)
|
||||||
|
attn_output = attn_output * gate
|
||||||
|
|
||||||
output, _ = self.o_proj(attn_output)
|
output, _ = self.o_proj(attn_output)
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ from sglang.srt.utils import (
|
|||||||
cpu_has_amx_support,
|
cpu_has_amx_support,
|
||||||
is_cpu,
|
is_cpu,
|
||||||
is_cuda,
|
is_cuda,
|
||||||
|
is_hip,
|
||||||
is_npu,
|
is_npu,
|
||||||
make_layers,
|
make_layers,
|
||||||
set_weight_attrs,
|
set_weight_attrs,
|
||||||
@@ -65,6 +66,7 @@ from sglang.srt.model_executor.cuda_graph_config import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
_is_cuda = is_cuda()
|
_is_cuda = is_cuda()
|
||||||
|
_is_hip = is_hip()
|
||||||
_is_npu = is_npu()
|
_is_npu = is_npu()
|
||||||
_is_cpu = is_cpu()
|
_is_cpu = is_cpu()
|
||||||
_is_amx_available = cpu_has_amx_support()
|
_is_amx_available = cpu_has_amx_support()
|
||||||
@@ -824,8 +826,15 @@ class Qwen3HybridAttentionDecoderLayer(nn.Module):
|
|||||||
attn_output = self.attn(q, k, v, forward_batch)
|
attn_output = self.attn(q, k, v, forward_batch)
|
||||||
|
|
||||||
if self.attn_output_gate:
|
if self.attn_output_gate:
|
||||||
gate = torch.sigmoid(gate)
|
if _is_hip:
|
||||||
attn_output = attn_output * gate
|
from sglang.jit_kernel.triton.sigmoid_gate_mul import (
|
||||||
|
sigmoid_gate_mul,
|
||||||
|
)
|
||||||
|
|
||||||
|
attn_output = sigmoid_gate_mul(attn_output, gate)
|
||||||
|
else:
|
||||||
|
gate = torch.sigmoid(gate)
|
||||||
|
attn_output = attn_output * gate
|
||||||
|
|
||||||
output, _ = self.o_proj(attn_output)
|
output, _ = self.o_proj(attn_output)
|
||||||
return output
|
return output
|
||||||
|
|||||||
Reference in New Issue
Block a user