Files
wehub-resource-sync 770d92cb1f
Lint / lint (push) Has been cancelled
Build Docs / Deploy Docs (push) Has been cancelled
Windows CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

187 lines
5.4 KiB
Python

"""Operators enabled by external modules."""
from typing import Optional
import tvm
from tvm.relax.frontend import nn
from tvm.relax.frontend.nn import Tensor, op
from mlc_llm.support import logging
from . import extern as _extern
logger = logging.getLogger(__name__)
WARN_FLASHINFER_GROUP_SIZE = False
WARN_FLASHINFER_HEAD_DIM = False
def attention(
q: nn.Tensor,
k: nn.Tensor,
v: nn.Tensor,
casual_mask: nn.Tensor,
attn_score_scaling_factor: float = 1.0,
qk_dtype: Optional[str] = None,
) -> nn.Tensor:
"""Attention with casual mask.
--- Variables ---
s: sequence length of the current query
t: total sequence length
d: head dimension
h, h_q: number of heads in query
h_kv: number of heads in key and value
b: batch size = 1
--- Shapes ---
q: [b, s, h_q, d]
k: [t, h_kv, d]
v: [t, h_kv, d]
o: [1, s, hidden = h_q * d]
--- Computation ---
.. code-block:: python
if h_kv != h_q:
k = k.repeat(h_q // h_kv, axis=1)
v = v.repeat(h_q // h_kv, axis=1)
q -> [b, h, s, d]
k, v -> [b, h, t, d]
attn = q @ k^T / sqrt(d) * attn_score_scaling_factor # [b, h, s, t]
attn = softmax_with_mask(attn, casual_mask, axis=-1)
o = attn @ v # [b, h, s, d]
o -> [b, s, h * d]
--- Other params ---
qk_dtype: if set, `matmul(Q, K, out_dtype=qk_dtype)`, (otherwise use `q.dtype` as `out_dtype`).
For FlashInfer, if "float32", sets `allow_fp16_qk_reduction` to False; otherwise no effect.
"""
assert q.ndim == 4 and k.ndim in [3, 4] and v.ndim in [3, 4]
b, s, h_q, d = q.shape
t, h_kv, _ = k.shape[-3:]
group_size = h_q // h_kv
def _fallback():
from tvm.relax.frontend.nn.llm.kv_cache import (
_attention_sequence_prefill,
)
nonlocal q, k, v, qk_dtype
if k.ndim == 3:
k = op.reshape(k, [b, t, h_kv, d])
if v.ndim == 3:
v = op.reshape(v, [b, t, h_kv, d])
if h_kv != h_q:
k = k.repeat(h_q // h_kv, axis=2)
v = v.repeat(h_q // h_kv, axis=2)
target = tvm.target.Target("cuda")
attn_output, _ = op.tensor_ir_op(
_attention_sequence_prefill(
h_kv=h_kv,
h_q=h_q,
d=d,
dtype=q.dtype,
target=target,
sm_scale=attn_score_scaling_factor / (d**0.5),
),
"sequence_prefill",
[q, k, v],
[
Tensor.placeholder([b, s, h_q, d], q.dtype),
Tensor.placeholder([b, s, h_q], q.dtype),
],
)
output = op.reshape(attn_output, shape=(b, s, h_q * d))
return output
# FlashInfer Implementation
if (
_extern.get_store().flashinfer
and attn_score_scaling_factor == 1.0
and q.dtype == "float16"
and k.dtype == "float16"
and v.dtype == "float16"
):
if group_size not in [1, 4, 6, 8]:
global WARN_FLASHINFER_GROUP_SIZE
if not WARN_FLASHINFER_GROUP_SIZE:
WARN_FLASHINFER_GROUP_SIZE = True
logger.warning(
"FlashInfer only supports group size in [1, 4, 6, 8], but got %d. Skip and "
"fallback to default implementation.",
group_size,
)
return _fallback()
if d not in [128]:
global WARN_FLASHINFER_HEAD_DIM
if not WARN_FLASHINFER_HEAD_DIM:
WARN_FLASHINFER_HEAD_DIM = True
logger.warning(
"FlashInfer only supports head_dim in [128], but got %d. Skip and fallback to "
"default implementation.",
d,
)
return _fallback()
rope_theta = 0.0
rope_scale = 1.0
qkv_layout = 0 # "NHD", N for seq_len, H for num_heads, D for head_dim
rotary_mode = 0 # "kNone"
casual = 1 # True
fp16_qk = 1 # True
if qk_dtype == "float32":
fp16_qk = 0 # False
# 32MB scratchpad
scratch = op.empty([8192 * 1024], dtype="float32")
def _decode():
return op.extern(
name="flashinfer.single_decode",
args=[
q,
k,
v,
scratch,
qkv_layout,
rotary_mode,
rope_scale,
rope_theta,
],
out=nn.Tensor.placeholder((b, s, h_q * d), dtype="float16"),
)
def _prefill():
return op.extern(
name="flashinfer.single_prefill",
args=[
q,
k,
v,
scratch,
casual,
qkv_layout,
rotary_mode,
fp16_qk,
rope_scale,
rope_theta,
],
out=nn.Tensor.placeholder((b, s, h_q * d), dtype="float16"),
)
if isinstance(s, int) and s == 1:
func = "decode"
else:
func = "prefill"
return {
"decode": _decode,
"prefill": _prefill,
}[func]()
# Fallback Implementation
return _fallback()