Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

911 lines
25 KiB
Python

# -*- coding: utf-8 -*-
"""
Copyright (c) Ant Financial Service Group and its affiliates.
"""
# Copied from https://code.alipay.com/pia/PainlessInferenceAcceleration/blob/v0.0.6/flood/flood/ops/seg_la.py
from dataclasses import dataclass
from typing import Optional
import torch
import triton
import triton.language as tl
# arg `meta` of `seg_la_fwd` is SegLaMeta
@dataclass
class SegLaMeta:
batch_size: int # batch size, num of requests
max_q_length: int # max(seq_lens)
q_offsets: torch.Tensor # [bs+1], query_start_locations,
s_offsets: torch.Tensor # [bs], slot_ids
q_lengths: torch.Tensor # [bs], query length
s_scales: torch.Tensor # [bs], prefill = 0, decode = 1
s_offsets_stride: int = 0
q_offsets_stride: int = 0
s_scales_stride: int = 0
decay_scales_stride: int = 0
mask: Optional[torch.Tensor] = None # Currently not supported
# fused
@triton.jit
def seg_la_kernel(
Q,
K,
V,
S,
Out,
softmax_scale,
stride_q,
stride_k,
stride_v,
stride_s,
stride_o,
s_offsets,
q_offsets,
q_lengths,
s_scales,
decay_scales,
HEAD_DIM: tl.constexpr,
SPLIT_DIM: tl.constexpr,
BLOCK: tl.constexpr,
EVEN: tl.constexpr,
DECOUPLE: tl.constexpr,
):
bid = tl.program_id(0)
hid = tl.program_id(1)
sid = tl.program_id(2)
# s_scale is 0 (prefill) or 1 (decode)
s_scale = tl.load(s_scales + bid)
q_length = tl.load(q_lengths + bid)
q_offset = tl.load(q_offsets + bid)
s_offset = tl.load(s_offsets + bid)
decay_scale = -tl.load(decay_scales + hid)
offs_b = tl.arange(0, BLOCK)
offs_d = tl.arange(0, HEAD_DIM)
offs_s = tl.arange(0, SPLIT_DIM)
if s_offset == -1:
return
q_ptrs = (
Q
+ q_offset * stride_q
+ hid * HEAD_DIM
+ (offs_b[:, None] * stride_q + offs_d[None, :])
)
k_ptrs = (
K
+ q_offset * stride_k
+ hid * HEAD_DIM
+ (offs_b[:, None] * stride_k + offs_d[None, :])
)
v_ptrs = (
V
+ q_offset * stride_v
+ hid * HEAD_DIM
+ sid * SPLIT_DIM
+ (offs_b[:, None] * stride_v + offs_s[None, :])
)
out_ptrs = (
Out
+ q_offset * stride_o
+ hid * HEAD_DIM
+ sid * SPLIT_DIM
+ (offs_b[:, None] * stride_o + offs_s[None, :])
)
s_ptrs = (
S
+ s_offset * stride_s
+ hid * HEAD_DIM * HEAD_DIM
+ sid * SPLIT_DIM
+ (offs_d[:, None] * HEAD_DIM + offs_s[None, :])
)
state = tl.load(s_ptrs, mask=s_scale > 0).to(tl.float32)
if BLOCK > 1:
for n in range(0, q_length, BLOCK):
n = tl.multiple_of(n, BLOCK)
if EVEN:
q = tl.load(q_ptrs + n * stride_q).to(tl.float32)
k = tl.trans(tl.load(k_ptrs + n * stride_k)).to(tl.float32)
v = tl.load(v_ptrs + n * stride_k).to(tl.float32)
else:
q = tl.load(
q_ptrs + n * stride_q,
mask=(n + offs_b)[:, None] < q_length,
other=0.0,
).to(tl.float32)
k = tl.trans(
tl.load(
k_ptrs + n * stride_k,
mask=(n + offs_b)[:, None] < q_length,
other=0.0,
)
).to(tl.float32)
v = tl.load(
v_ptrs + n * stride_k,
mask=(n + offs_b)[:, None] < q_length,
other=0.0,
).to(tl.float32)
if DECOUPLE:
# only work with small scales
if EVEN:
b = BLOCK
else:
b = min(BLOCK, q_length - n)
b_offs = b - 1 - offs_b
edb = tl.exp(decay_scale * b_offs)
decays = tl.where(b_offs >= 0, edb, 0)
inv_decays = tl.where(b_offs >= 0, 1 / edb, 0)
q = q * inv_decays[:, None]
k = k * decays[None, :]
qk = tl.dot(q, k) * softmax_scale
qk = tl.where(offs_b[None, :] <= offs_b[:, None], qk, 0.0)
o = tl.dot(qk, v)
block_decay = tl.exp(decay_scale * b)
block_decay_plus = block_decay * softmax_scale
o = tl.dot(q, state) * block_decay_plus + o
state = state * block_decay + tl.dot(k, v)
else:
qk = tl.dot(q, k) * softmax_scale
decays = tl.exp(decay_scale * (offs_b[:, None] - offs_b[None, :]))
decays = tl.where(offs_b[None, :] <= offs_b[:, None], decays, 0.0)
qk *= decays
o = tl.dot(qk, v)
decay_arr = tl.exp(decay_scale * (offs_b[:, None] + 1)) * softmax_scale
o = tl.dot(q * decay_arr, state, acc=o)
if EVEN:
b = BLOCK
else:
b = min(BLOCK, q_length - n)
b_offs = b - 1 - offs_b
b_offs = tl.where(b_offs >= 0, b_offs, 10000)
decays = tl.exp(decay_scale * b_offs)
block_decay = tl.exp(decay_scale * b)
state = state * block_decay + tl.dot(k * decays[None, :], v)
if EVEN:
tl.store(out_ptrs + n * stride_o, o.to(Out.dtype.element_ty))
else:
tl.store(
out_ptrs + n * stride_o,
o.to(Out.dtype.element_ty),
mask=(n + offs_b)[:, None] < q_length,
)
tl.store(s_ptrs, state.to(S.dtype.element_ty))
else:
q = tl.trans(tl.load(q_ptrs)).to(tl.float32) * softmax_scale
k = tl.trans(tl.load(k_ptrs)).to(tl.float32)
v = tl.load(v_ptrs).to(tl.float32)
state = state * tl.exp(decay_scale) + k * v
o = tl.sum(q * state, axis=0, keep_dims=True)
tl.store(out_ptrs, o.to(Out.dtype.element_ty))
tl.store(s_ptrs, state.to(S.dtype.element_ty))
# used for prefilling
@triton.jit
def seg_la_p_kernel(
Q,
K,
V,
S,
Out,
softmax_scale,
stride_q,
stride_k,
stride_v,
stride_s,
stride_o,
s_offsets,
q_offsets,
q_lengths,
s_scales,
decay_scales,
HEAD_DIM: tl.constexpr,
K_SPLIT_DIM: tl.constexpr,
V_SPLIT_DIM: tl.constexpr,
BLOCK: tl.constexpr,
EVEN: tl.constexpr,
):
bid = tl.program_id(0)
hid = tl.program_id(1)
kvid = tl.program_id(2)
N = HEAD_DIM // V_SPLIT_DIM
kid = kvid // N
vid = kvid % N
H = tl.num_programs(1)
# s_scale is 0 (first prefill chunk) or 1 (next prefill chunk)
s_scale = tl.load(s_scales + bid)
q_length = tl.load(q_lengths + bid)
q_offset = tl.load(q_offsets + bid)
s_offset = tl.load(s_offsets + bid)
decay_scale = -tl.load(decay_scales + hid)
offs_b = tl.arange(0, BLOCK)
offs_k = tl.arange(0, K_SPLIT_DIM)
offs_v = tl.arange(0, V_SPLIT_DIM)
if s_offset == -1:
return
q_ptrs = (
Q
+ q_offset * stride_q
+ hid * HEAD_DIM
+ kid * K_SPLIT_DIM
+ (offs_b[:, None] * stride_q + offs_k[None, :])
)
k_ptrs = (
K
+ q_offset * stride_k
+ hid * HEAD_DIM
+ kid * K_SPLIT_DIM
+ (offs_b[:, None] * stride_k + offs_k[None, :])
)
v_ptrs = (
V
+ q_offset * stride_v
+ hid * HEAD_DIM
+ vid * V_SPLIT_DIM
+ (offs_b[:, None] * stride_v + offs_v[None, :])
)
# (num_dim_block, length, qo_heads, d)
out_ptrs = (
Out
+ kid * stride_o
+ q_offset * HEAD_DIM * H
+ hid * HEAD_DIM
+ vid * V_SPLIT_DIM
+ (offs_b[:, None] * H * HEAD_DIM + offs_v[None, :])
)
s_ptrs = (
S
+ s_offset * stride_s
+ hid * HEAD_DIM * HEAD_DIM
+ kid * HEAD_DIM * K_SPLIT_DIM
+ vid * V_SPLIT_DIM
+ (offs_k[:, None] * HEAD_DIM + offs_v[None, :])
)
state = tl.load(s_ptrs, mask=s_scale > 0).to(tl.float32)
for n in range(0, q_length, BLOCK):
n = tl.multiple_of(n, BLOCK)
if EVEN:
q = tl.load(q_ptrs + n * stride_q).to(tl.float32)
k = tl.trans(tl.load(k_ptrs + n * stride_k)).to(tl.float32)
v = tl.load(v_ptrs + n * stride_v).to(tl.float32)
b = BLOCK
b_offs = b - 1 - offs_b
decays = tl.exp(decay_scale * b_offs)
inv_decays = 1 / decays
else:
q = tl.load(
q_ptrs + n * stride_q, mask=(n + offs_b)[:, None] < q_length, other=0.0
).to(tl.float32)
k = tl.trans(
tl.load(
k_ptrs + n * stride_k,
mask=(n + offs_b)[:, None] < q_length,
other=0.0,
)
).to(tl.float32)
v = tl.load(
v_ptrs + n * stride_v, mask=(n + offs_b)[:, None] < q_length, other=0.0
).to(tl.float32)
b = min(BLOCK, q_length - n)
b_offs = b - 1 - offs_b
block_decays = tl.exp(decay_scale * b_offs)
decays = tl.where(b_offs >= 0, block_decays, 0)
inv_decays = tl.where(b_offs >= 0, 1 / block_decays, 0)
q = q * inv_decays[:, None]
k = k * decays[None, :]
qk = tl.dot(q, k) * softmax_scale
qk = tl.where(offs_b[None, :] <= offs_b[:, None], qk, 0.0)
o = tl.dot(qk, v)
block_decay = tl.exp(decay_scale * b)
o = tl.dot(q, state) * block_decay * softmax_scale + o
state = state * block_decay + tl.dot(k, v)
if EVEN:
tl.store(out_ptrs + n * H * HEAD_DIM, o.to(Out.dtype.element_ty))
else:
tl.store(
out_ptrs + n * H * HEAD_DIM,
o.to(Out.dtype.element_ty),
mask=(n + offs_b)[:, None] < q_length,
)
tl.store(s_ptrs, state.to(S.dtype.element_ty))
# used for speculative
@triton.jit
def seg_la_s_kernel(
Q,
K,
V,
S,
Out,
Mask,
softmax_scale,
stride_q,
stride_k,
stride_v,
stride_s,
stride_o,
s_offsets,
q_offsets,
q_lengths,
s_scales,
decay_scales,
HEAD_DIM: tl.constexpr,
K_SPLIT_DIM: tl.constexpr,
V_SPLIT_DIM: tl.constexpr,
BLOCK: tl.constexpr,
EVEN: tl.constexpr,
):
bid = tl.program_id(0)
hid = tl.program_id(1)
kvid = tl.program_id(2)
N = HEAD_DIM // V_SPLIT_DIM
kid = kvid // N
vid = kvid % N
H = tl.num_programs(1)
# s_scale is 0 (first prefill chunk) or 1 (next prefill chunk)
s_scale = tl.load(s_scales + bid)
q_length = tl.load(q_lengths + bid)
q_offset = tl.load(q_offsets + bid)
s_offset = tl.load(s_offsets + bid)
decay_scale = -tl.load(decay_scales + hid)
offs_b = tl.arange(0, BLOCK)
offs_k = tl.arange(0, K_SPLIT_DIM)
offs_v = tl.arange(0, V_SPLIT_DIM)
if s_offset == -1:
return
q_ptrs = (
Q
+ q_offset * stride_q
+ hid * HEAD_DIM
+ kid * K_SPLIT_DIM
+ (offs_b[:, None] * stride_q + offs_k[None, :])
)
k_ptrs = (
K
+ q_offset * stride_k
+ hid * HEAD_DIM
+ kid * K_SPLIT_DIM
+ (offs_b[:, None] * stride_k + offs_k[None, :])
)
v_ptrs = (
V
+ q_offset * stride_v
+ hid * HEAD_DIM
+ vid * V_SPLIT_DIM
+ (offs_b[:, None] * stride_v + offs_v[None, :])
)
# (num_dim_block, length, qo_heads, d)
out_ptrs = (
Out
+ kid * stride_o
+ q_offset * HEAD_DIM * H
+ hid * HEAD_DIM
+ vid * V_SPLIT_DIM
+ (offs_b[:, None] * H * HEAD_DIM + offs_v[None, :])
)
s_ptrs = (
S
+ s_offset * stride_s
+ hid * HEAD_DIM * HEAD_DIM
+ kid * HEAD_DIM * K_SPLIT_DIM
+ vid * V_SPLIT_DIM
+ (offs_k[:, None] * HEAD_DIM + offs_v[None, :])
)
state = tl.load(s_ptrs, mask=s_scale > 0).to(tl.float32)
if EVEN:
q = tl.load(q_ptrs).to(tl.float32)
k = tl.trans(tl.load(k_ptrs)).to(tl.float32)
v = tl.load(v_ptrs).to(tl.float32)
mask = tl.load(
Mask
+ bid * BLOCK * BLOCK
+ tl.arange(0, BLOCK)[:, None] * BLOCK
+ tl.arange(0, BLOCK)[None, :]
).to(tl.int32)
positions = tl.sum(mask, 1) - 1
max_pos = tl.max(positions)
b_offs = max_pos - positions
else:
q = tl.load(q_ptrs, mask=offs_b[:, None] < q_length).to(tl.float32)
k = tl.trans(tl.load(k_ptrs, mask=offs_b[:, None] < q_length)).to(tl.float32)
v = tl.load(v_ptrs, mask=offs_b[:, None] < q_length).to(tl.float32)
mask = tl.load(
Mask
+ bid * q_length * q_length
+ tl.arange(0, BLOCK)[:, None] * q_length
+ tl.arange(0, BLOCK)[None, :],
mask=(tl.arange(0, BLOCK)[:, None] < q_length)
& (tl.arange(0, BLOCK)[None, :] < q_length),
).to(tl.int32)
positions = tl.sum(mask, 1) - 1
max_pos = tl.max(positions)
b_offs = max_pos - positions
decays = tl.exp(decay_scale * b_offs)
inv_decays = 1 / decays
q = q * inv_decays[:, None]
k = k * decays[None, :]
qk = tl.dot(q, k) * softmax_scale
qk = qk * mask.to(tl.float32)
o = tl.dot(qk, v)
block_decay = tl.exp(decay_scale * (max_pos + 1))
o = tl.dot(q, state) * block_decay * softmax_scale + o
if EVEN:
tl.store(out_ptrs, o.to(Out.dtype.element_ty))
else:
tl.store(out_ptrs, o.to(Out.dtype.element_ty), mask=offs_b[:, None] < q_length)
# used for decode
@triton.jit
def seg_la_d_kernel(
Q,
K,
V,
S,
Out,
softmax_scale,
stride_q,
stride_k,
stride_v,
stride_s,
stride_o,
s_offsets,
decay_scales,
HEAD_DIM: tl.constexpr,
K_SPLIT_DIM: tl.constexpr,
V_SPLIT_DIM: tl.constexpr,
):
bid = tl.program_id(0)
hid = tl.program_id(1)
kvid = tl.program_id(2)
N = HEAD_DIM // V_SPLIT_DIM
kid = kvid // N
vid = kvid % N
H = tl.num_programs(1)
# s_scale is 0 (first prefill chunk) or 1 (next prefill chunk)
s_offset = tl.load(s_offsets + bid)
if s_offset == -1:
return
decay_scale = -tl.load(decay_scales + hid)
offs_k = tl.arange(0, K_SPLIT_DIM)
offs_v = tl.arange(0, V_SPLIT_DIM)
q_ptrs = Q + bid * stride_q + hid * HEAD_DIM + kid * K_SPLIT_DIM + (offs_k)
k_ptrs = K + bid * stride_k + hid * HEAD_DIM + kid * K_SPLIT_DIM + (offs_k)
v_ptrs = V + bid * stride_v + hid * HEAD_DIM + vid * V_SPLIT_DIM + (offs_v)
# (num_dim_block, length, qo_heads, d)
out_ptrs = (
Out
+ kid * stride_o
+ bid * H * HEAD_DIM
+ hid * HEAD_DIM
+ vid * V_SPLIT_DIM
+ (offs_v)
)
s_ptrs = (
S
+ s_offset * stride_s
+ hid * HEAD_DIM * HEAD_DIM
+ kid * HEAD_DIM * K_SPLIT_DIM
+ vid * V_SPLIT_DIM
+ (offs_k[:, None] * HEAD_DIM + offs_v[None, :])
)
state = tl.load(s_ptrs).to(tl.float32)
k = tl.load(k_ptrs).to(tl.float32)
v = tl.load(v_ptrs).to(tl.float32)
q = tl.load(q_ptrs).to(tl.float32) * softmax_scale
state = state * tl.exp(decay_scale) + k[:, None] * v
o = tl.sum(q[:, None] * state, axis=0)
tl.store(out_ptrs, o.to(Out.dtype.element_ty))
tl.store(s_ptrs, state.to(S.dtype.element_ty))
# used for MTP with only spec-topk=1.
@triton.jit
def seg_la_mtp_kernel(
Q,
K,
V,
S,
CACHES,
Out,
softmax_scale,
stride_q,
stride_k,
stride_v,
stride_s,
stride_c,
stride_o,
s_offsets,
cache_indices,
decay_scales,
step,
HEAD_DIM: tl.constexpr,
K_SPLIT_DIM: tl.constexpr,
V_SPLIT_DIM: tl.constexpr,
):
bid = tl.program_id(0)
hid = tl.program_id(1)
kvid = tl.program_id(2)
N = HEAD_DIM // V_SPLIT_DIM
kid = kvid // N
vid = kvid % N
H = tl.num_programs(1)
s_offset = tl.load(s_offsets + bid)
if s_offset == -1:
return
decay_scale = tl.exp(-tl.load(decay_scales + hid))
offs_k = tl.arange(0, K_SPLIT_DIM)
offs_v = tl.arange(0, V_SPLIT_DIM)
# (length, qo_heads, d)
q_ptrs = Q + bid * step * stride_q + hid * HEAD_DIM + kid * K_SPLIT_DIM + (offs_k)
k_ptrs = K + bid * step * stride_k + hid * HEAD_DIM + kid * K_SPLIT_DIM + (offs_k)
v_ptrs = V + bid * step * stride_v + hid * HEAD_DIM + vid * V_SPLIT_DIM + (offs_v)
# (num_dim_block, length, qo_heads, d)
out_ptrs = (
Out
+ kid * stride_o
+ bid * step * H * HEAD_DIM
+ hid * HEAD_DIM
+ vid * V_SPLIT_DIM
+ (offs_v)
)
# (bs, qo_heads, d, d)
s_ptrs = (
S
+ s_offset * stride_s
+ hid * HEAD_DIM * HEAD_DIM
+ kid * HEAD_DIM * K_SPLIT_DIM
+ vid * V_SPLIT_DIM
+ (offs_k[:, None] * HEAD_DIM + offs_v[None, :])
)
state = tl.load(s_ptrs).to(tl.float32)
# (bs, step, kv_heads, d, d)
cache_indices = tl.load(cache_indices + bid)
c_ptrs = (
CACHES
+ cache_indices * stride_c
+ hid * HEAD_DIM * HEAD_DIM
+ kid * HEAD_DIM * K_SPLIT_DIM
+ vid * V_SPLIT_DIM
+ (offs_k[:, None] * HEAD_DIM + offs_v[None, :])
)
for i in range(step):
q = tl.load(q_ptrs).to(tl.float32) * softmax_scale
k = tl.load(k_ptrs).to(tl.float32)
v = tl.load(v_ptrs).to(tl.float32)
state = state * decay_scale + k[:, None] * v
o = tl.sum(q[:, None] * state, axis=0)
tl.store(out_ptrs, o.to(Out.dtype.element_ty))
tl.store(c_ptrs, state.to(CACHES.dtype.element_ty))
q_ptrs += stride_q
k_ptrs += stride_k
v_ptrs += stride_v
out_ptrs += H * HEAD_DIM
c_ptrs += H * HEAD_DIM * HEAD_DIM
# (k_dim_block, length, qo_heads, d)
@triton.jit
def seg_la_sum_kernel(T, O, DIM: tl.constexpr, NUM_BLOCK: tl.constexpr):
pid = tl.program_id(0)
length = tl.num_programs(0)
x = tl.zeros((DIM,), dtype=tl.float32)
for i in range(NUM_BLOCK):
x += tl.load(T + i * length * DIM + pid * DIM + tl.arange(0, DIM)).to(
tl.float32
)
tl.store(O + pid * DIM + tl.arange(0, DIM), x)
def seg_la_fwd(
q,
k,
v,
s,
decay_scales,
meta,
caches=None,
cache_indices=None,
softmax_scale=None,
decouple=False,
):
length, qo_heads, HEAD_DIM = q.shape
_, kv_heads, _ = k.shape
bs = meta.batch_size
if softmax_scale is None:
softmax_scale = HEAD_DIM ** (-0.5)
# MAX_LENGTH = meta.max_q_length
MAX_LENGTH = triton.cdiv(length, bs)
assert qo_heads == kv_heads, "seg_la does NOT support GQA currently"
if MAX_LENGTH > 1:
# prefill with partitioning q/k/v
# BLOCK should <= 64 with decouple
K_SPLIT_DIM = 32
V_SPLIT_DIM = 32 if bs <= 2 else 64
num_warps = 2 # 2
num_stages = 3 # 3
k_dim_block = HEAD_DIM // K_SPLIT_DIM
v_dim_block = HEAD_DIM // V_SPLIT_DIM
tmp = torch.empty(
(k_dim_block, length, qo_heads, HEAD_DIM), device=q.device, dtype=q.dtype
)
grid = (bs, kv_heads, k_dim_block * v_dim_block)
if caches is not None:
# mtp
EVEN = False
BLOCK = 32
step = length // bs
seg_la_mtp_kernel[grid](
q,
k,
v,
s,
caches,
tmp,
softmax_scale,
q.stride(0),
k.stride(0),
v.stride(0),
s.stride(0),
caches.stride(0),
tmp.stride(0),
meta.s_offsets,
cache_indices,
decay_scales,
step,
HEAD_DIM=HEAD_DIM,
K_SPLIT_DIM=K_SPLIT_DIM,
V_SPLIT_DIM=V_SPLIT_DIM,
num_warps=num_warps,
num_stages=num_stages,
)
elif meta.mask is not None:
# spec
ms = meta.mask.size(-1)
BLOCK = (ms + 15) // 16 * 16
EVEN = BLOCK == ms
seg_la_s_kernel[grid](
q,
k,
v,
s,
tmp,
meta.mask,
softmax_scale,
q.stride(0),
k.stride(0),
v.stride(0),
s.stride(0),
tmp.stride(0),
meta.s_offsets,
meta.q_offsets,
meta.q_lengths,
meta.s_scales,
decay_scales,
HEAD_DIM=HEAD_DIM,
K_SPLIT_DIM=K_SPLIT_DIM,
V_SPLIT_DIM=V_SPLIT_DIM,
BLOCK=BLOCK,
EVEN=EVEN,
num_warps=num_warps,
num_stages=num_stages,
)
else:
# prefill
BLOCK = 32
EVEN = MAX_LENGTH % BLOCK == 0 if bs == 1 else False
seg_la_p_kernel[grid](
q,
k,
v,
s,
tmp,
softmax_scale,
q.stride(0),
k.stride(0),
v.stride(0),
s.stride(0),
tmp.stride(0),
meta.s_offsets,
meta.q_offsets,
meta.q_lengths,
meta.s_scales,
decay_scales,
HEAD_DIM=HEAD_DIM,
K_SPLIT_DIM=K_SPLIT_DIM,
V_SPLIT_DIM=V_SPLIT_DIM,
BLOCK=BLOCK,
EVEN=EVEN,
num_warps=num_warps,
num_stages=num_stages,
)
if k_dim_block > 1:
if length < 2048:
o = tmp.sum(0)
else:
o = torch.empty(
(length, qo_heads, HEAD_DIM), device=q.device, dtype=q.dtype
)
seg_la_sum_kernel[(length,)](
tmp,
o,
DIM=qo_heads * HEAD_DIM,
NUM_BLOCK=k_dim_block,
num_warps=2,
num_stages=3,
)
else:
o = tmp[0]
else:
# decode with partitioning q/k/v
if bs <= 128:
K_SPLIT_DIM = 128 # 128
V_SPLIT_DIM = 32 # 32
num_warps = 2 # 2
num_stages = 2 # 3
else:
K_SPLIT_DIM = 128 # 128
V_SPLIT_DIM = 64 # 32
num_warps = 2 # 2
num_stages = 3 # 3
k_dim_block = HEAD_DIM // K_SPLIT_DIM
v_dim_block = HEAD_DIM // V_SPLIT_DIM
tmp = torch.empty(
(k_dim_block, length, qo_heads, HEAD_DIM), device=q.device, dtype=q.dtype
)
grid = (bs, kv_heads, k_dim_block * v_dim_block)
seg_la_d_kernel[grid](
q,
k,
v,
s,
tmp,
softmax_scale,
q.stride(0),
k.stride(0),
v.stride(0),
s.stride(0),
tmp.stride(0),
meta.s_offsets,
decay_scales,
HEAD_DIM=HEAD_DIM,
K_SPLIT_DIM=K_SPLIT_DIM,
V_SPLIT_DIM=V_SPLIT_DIM,
num_warps=num_warps,
num_stages=num_stages,
)
if k_dim_block > 1:
o = tmp.sum(0)
else:
o = tmp[0]
# if fallback:
# # prefill/decode with partitioning v only
# o = torch.empty(q.shape, device=q.device, dtype=q.dtype)
# if MAX_LENGTH == 1:
# # decode
# BLOCK = 1
# EVEN = False
# SPLIT_DIM = 32
# num_warps = 8
# num_stages = 2
# num_dim_block = HEAD_DIM // SPLIT_DIM
# grid = (batch, kv_heads, num_dim_block)
# else:
# # prefill
# if decouple:
# BLOCK = 64
# SPLIT_DIM = 16
# else:
# BLOCK = HEAD_DIM
# SPLIT_DIM = 32
# # EVEN = all([x % BLOCK == 0 for x in meta.qls])
# EVEN = False
# num_warps = 8
# num_stages = 2
# # prop = torch.cuda.get_device_properties(q.device.index)
# # arch = prop.major * 10 + prop.minor
# # if arch not in (80, 90):
# # num_stages = 1
# num_dim_block = HEAD_DIM // SPLIT_DIM
# grid = (batch, kv_heads, num_dim_block)
# seg_la_kernel[grid](
# q,
# k,
# v,
# s,
# o,
# softmax_scale,
# q.stride(0),
# k.stride(0),
# v.stride(0),
# s.stride(0),
# o.stride(0),
# meta.s_offsets,
# meta.q_offsets,
# meta.q_lengths,
# meta.s_scales,
# decay_scales,
# HEAD_DIM=HEAD_DIM,
# SPLIT_DIM=SPLIT_DIM,
# BLOCK=BLOCK,
# EVEN=EVEN,
# DECOUPLE=decouple,
# num_warps=num_warps,
# num_stages=num_stages
# )
return o