This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new bf7bbefd36 [Python][Relax] Rotary positional embedding scaling (#17305)
bf7bbefd36 is described below
commit bf7bbefd36ac91242496d533d2bfff71570bf04a
Author: Ruihang Lai <[email protected]>
AuthorDate: Tue Aug 27 10:19:28 2024 -0400
[Python][Relax] Rotary positional embedding scaling (#17305)
This PR introduces two styles of RoPE scaling: the llama3 style
and the longrope scale.
---
python/tvm/relax/frontend/nn/llm/kv_cache.py | 396 +++++++++++++++++++--
.../relax/frontend/nn/llm/position_embedding.py | 191 +++++++++-
python/tvm/relax/frontend/nn/llm/tree_attn.py | 26 +-
...runtime_builtin_paged_attention_kv_cache_tir.py | 19 +-
4 files changed, 579 insertions(+), 53 deletions(-)
diff --git a/python/tvm/relax/frontend/nn/llm/kv_cache.py
b/python/tvm/relax/frontend/nn/llm/kv_cache.py
index 25a3a1a00d..5ddce76eab 100644
--- a/python/tvm/relax/frontend/nn/llm/kv_cache.py
+++ b/python/tvm/relax/frontend/nn/llm/kv_cache.py
@@ -20,7 +20,7 @@
# pylint:
disable=too-many-statements,too-many-lines,too-many-arguments,invalid-name
import enum
import math
-from typing import Tuple
+from typing import Any, Dict, Tuple
from tvm import relax as rx
from tvm import tir
@@ -29,7 +29,7 @@ from tvm.runtime import DataType
from tvm.script import tir as T
from tvm.target import Target
-from .position_embedding import llama_rope_with_position_map, rope_freq
+from .position_embedding import llama_rope_with_position_map,
switch_rope_freq_func
from .tree_attn import tree_attn
@@ -166,6 +166,8 @@ class FlashInferPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-me
rope_mode: RopeMode,
rope_scale: int,
rope_theta: int,
+ rope_scaling: Dict[str, Any],
+ rope_ext_factors: rx.Expr,
rotary_dim: int,
dtype: str,
target: Target,
@@ -195,6 +197,9 @@ class FlashInferPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-me
0 or 1, denoting whether the KV cache supports sliding window.
It is a symbolic variable whose concrete value is specified
at runtime.
+ layer_partition : rx.ShapeExpr
+ The KV cache layer partition for pipeline stages.
+ It is an indptr array, denoting the starting layer of each
pipeline stage.
rope_mode : RopeMode
The RoPE mode of the Paged KV cache.
If it is normal, RoPE will be applied to k before adding k to
cache.
@@ -205,6 +210,8 @@ class FlashInferPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-me
The base of rotary position embedding.
rope_scaling: Dict[str, Any]
The RoPE scaling information dict.
+ rope_ext_factors: rx.Expr
+ The RoPE extension factors when "longrope" mode RoPE scaling is
enabled.
rotary_dim : int
The number of dimensions in the embedding that RoPE is applied to.
"""
@@ -235,8 +242,8 @@ class FlashInferPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-me
bb.add_func(_kv_cache_transpose_append(num_key_value_heads,
head_dim, dtype), "kv_cache_transpose_append"),
rx.extern("flashinfer.attention_kernel_prefill_with_paged_kv_cache"),
rx.extern("flashinfer.attention_kernel_decode_with_paged_kv_cache"),
- bb.add_func(_attention_prefill(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, target),
"tir_attention_prefill_sliding_window"),
- bb.add_func(_attention_decode(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, target),
"tir_attention_decode_sliding_window"),
+ bb.add_func(_attention_prefill(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, rope_scaling, target),
"tir_attention_prefill_sliding_window"),
+ bb.add_func(_attention_decode(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, rope_scaling, target),
"tir_attention_decode_sliding_window"),
rx.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache"),
rx.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache_begin_forward"),
rx.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache_end_forward"),
@@ -245,11 +252,12 @@ class FlashInferPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-me
rx.extern("flashinfer.attention_kernel_decode_with_paged_kv_cache_begin_forward"),
rx.extern("flashinfer.attention_kernel_decode_with_paged_kv_cache_end_forward"),
rx.extern("flashinfer.merge_state_in_place"),
- bb.add_func(llama_rope_with_position_map(rope_theta, rope_scale,
head_dim, num_attention_heads, num_key_value_heads, dtype, rotary_dim),
"tir_split_rotary"),
+ bb.add_func(llama_rope_with_position_map(rope_theta, rope_scale,
head_dim, num_attention_heads, num_key_value_heads, dtype, rope_scaling,
rotary_dim), "tir_split_rotary"),
bb.add_func(_copy_single_page(num_key_value_heads, page_size,
head_dim, dtype, target), "kv_cache_copy_single_page"),
bb.add_func(_kv_cache_debug_get_kv(num_hidden_layers,
num_key_value_heads, head_dim, dtype), "kv_cache_debug_get_kv"),
bb.add_func(_compact_kv_copy(num_key_value_heads, head_dim, dtype,
target), "kv_cache_compact_kv_copy"),
- bb.add_func(tree_attn(num_key_value_heads, num_attention_heads,
head_dim, dtype, target), "tir_attention_prefill_with_tree_mask"),
+ bb.add_func(tree_attn(num_key_value_heads, num_attention_heads,
head_dim, dtype, rope_scaling, target), "tir_attention_prefill_with_tree_mask"),
+ rope_ext_factors,
# fmt: on
# pylint: enable=line-too-long
]
@@ -281,6 +289,8 @@ class TIRPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-methods
head_dim: int,
rope_scale: int,
rope_theta: int,
+ rope_scaling: Dict[str, Any],
+ rope_ext_factors: rx.Expr,
rotary_dim: int,
dtype: str,
target: Target,
@@ -321,6 +331,10 @@ class TIRPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-methods
The scale of rotary position embedding.
rope_theta : int
The base of rotary position embedding.
+ rope_scaling: Dict[str, Any]
+ The RoPE scaling information dict.
+ rope_ext_factors: rx.Expr
+ The RoPE extension factors when "longrope" mode RoPE scaling is
enabled.
rotary_dim : int
The number of dimensions in the embedding that RoPE is applied to.
target : Target
@@ -349,17 +363,18 @@ class TIRPagedKVCache(PagedKVCache): # pylint:
disable=too-few-public-methods
# pylint: disable=line-too-long
# fmt: off
bb.add_func(_kv_cache_transpose_append(num_key_value_heads,
head_dim, dtype), "kv_cache_transpose_append"),
- bb.add_func(_attention_prefill(num_key_value_heads,
num_attention_heads, head_dim, dtype, False, target), "tir_attention_prefill"),
- bb.add_func(_attention_decode(num_key_value_heads,
num_attention_heads, head_dim, dtype, False, target), "tir_attention_decode"),
- bb.add_func(_attention_prefill(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, target),
"tir_attention_prefill_sliding_window"),
- bb.add_func(_attention_decode(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, target),
"tir_attention_decode_sliding_window"),
- bb.add_func(_attention_prefill_ragged(num_key_value_heads,
num_attention_heads, head_dim, dtype, target), "tir_attention_prefill_ragged"),
+ bb.add_func(_attention_prefill(num_key_value_heads,
num_attention_heads, head_dim, dtype, False, rope_scaling, target),
"tir_attention_prefill"),
+ bb.add_func(_attention_decode(num_key_value_heads,
num_attention_heads, head_dim, dtype, False, rope_scaling, target),
"tir_attention_decode"),
+ bb.add_func(_attention_prefill(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, rope_scaling, target),
"tir_attention_prefill_sliding_window"),
+ bb.add_func(_attention_decode(num_key_value_heads,
num_attention_heads, head_dim, dtype, True, rope_scaling, target),
"tir_attention_decode_sliding_window"),
+ bb.add_func(_attention_prefill_ragged(num_key_value_heads,
num_attention_heads, head_dim, dtype, rope_scaling, target),
"tir_attention_prefill_ragged"),
bb.add_func(_merge_state_inplace(num_attention_heads, head_dim,
dtype, target), "tir_attention_merge_state"),
- bb.add_func(llama_rope_with_position_map(rope_theta, rope_scale,
head_dim, num_attention_heads, num_key_value_heads, dtype, rotary_dim),
"tir_split_rotary"),
+ bb.add_func(llama_rope_with_position_map(rope_theta, rope_scale,
head_dim, num_attention_heads, num_key_value_heads, dtype, rope_scaling,
rotary_dim), "tir_split_rotary"),
bb.add_func(_copy_single_page(num_key_value_heads, page_size,
head_dim, dtype, target), "kv_cache_copy_single_page"),
bb.add_func(_kv_cache_debug_get_kv(num_hidden_layers,
num_key_value_heads, head_dim, dtype), "kv_cache_debug_get_kv"),
bb.add_func(_compact_kv_copy(num_key_value_heads, head_dim, dtype,
target), "kv_cache_compact_kv_copy"),
- bb.add_func(tree_attn(num_key_value_heads, num_attention_heads,
head_dim, dtype, target), "tir_attention_prefill_with_tree_mask"),
+ bb.add_func(tree_attn(num_key_value_heads, num_attention_heads,
head_dim, dtype, rope_scaling, target), "tir_attention_prefill_with_tree_mask"),
+ rope_ext_factors,
# fmt: on
# pylint: enable=line-too-long
]
@@ -464,17 +479,23 @@ def _rope(
theta: tir.Var,
scale: tir.Var,
indices: Tuple[tir.Var, ...],
- qkv_dtype="float16",
+ qkv_dtype: str,
+ rope_scaling: Dict[str, Any],
):
d = indices[-1]
- cos_freq, sin_freq = rope_freq(offset * scale, d, rotary_dim, theta,
"float32")
+ cos_freq, sin_freq, var_map = switch_rope_freq_func(rope_scaling)(
+ offset * scale, d, rotary_dim, theta, "float32"
+ )
cos = cos_freq * buffer[indices].astype("float32")
sin = sin_freq * tir.if_then_else(
d < rotary_dim // 2,
-buffer[indices[:-1] + (d + rotary_dim // 2,)],
buffer[indices[:-1] + (d - rotary_dim // 2,)],
).astype("float32")
- return (cos + sin).astype(qkv_dtype)
+ expr = (cos + sin).astype(qkv_dtype)
+ for var, value in var_map.items():
+ expr = tir.Let(var, value, expr)
+ return expr
def _var(dtype):
@@ -520,7 +541,9 @@ def _get_seq_offset(pos, seq_id, length_info,
sliding_window):
)
-def _attention_prefill(h_kv, h_q, d, dtype, sliding_window: bool, target:
Target):
+def _attention_prefill(
+ h_kv, h_q, d, dtype, sliding_window: bool, rope_scaling: Dict[str, Any],
target: Target
+):
NUM_BLKS = 16
LOAD_VEC = 8 // ((DataType(dtype).bits + 7) // 8) # 8 bytes
group_size = h_q // h_kv
@@ -680,7 +703,7 @@ def _attention_prefill(h_kv, h_q, d, dtype, sliding_window:
bool, target: Target
if cur_L < q_indptr[b_idx + 1]:
Q_smem[i, j] = T.if_then_else(
rotary_mode == 1,
- _rope(q,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype),
+ _rope(q,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype,
rope_scaling),
q[cur_L, cur_H_qo, j]
)
else:
@@ -701,7 +724,7 @@ def _attention_prefill(h_kv, h_q, d, dtype, sliding_window:
bool, target: Target
page_offset:
T.int32(is_size_var=True) = T.floormod(seq_offset, 16) # type: ignore
K_smem[i, j] =
T.if_then_else(
rotary_mode == 1,
- _rope(pages,
k_rope_pos_offset[b_idx] + cur_L, d, rope_theta, rope_scale, (page_no, 0, by,
page_offset, j), dtype),
+ _rope(pages,
k_rope_pos_offset[b_idx] + cur_L, d, rope_theta, rope_scale, (page_no, 0, by,
page_offset, j), dtype, rope_scaling),
pages[page_no, 0, by,
page_offset, j]
)
else:
@@ -890,6 +913,7 @@ def _attention_decode(
head_dim,
qkv_dtype,
sliding_window: bool,
+ rope_scaling: Dict[str, Any],
target: Target,
):
qkv_dtype_bytes = 2
@@ -1023,7 +1047,7 @@ def _attention_decode(
for vec in T.vectorized(VEC_SIZE):
Q_local[vec] = T.if_then_else(
rotary_mode == 1,
- _rope(Q, q_rope_position[batch_idx],
head_dim, rope_theta, rope_scale, (bx, by * GROUP_SIZE + bz * bdy + ty, tx *
VEC_SIZE + vec), qkv_dtype),
+ _rope(Q, q_rope_position[batch_idx],
head_dim, rope_theta, rope_scale, (bx, by * GROUP_SIZE + bz * bdy + ty, tx *
VEC_SIZE + vec), qkv_dtype, rope_scaling),
Q[bx, by * GROUP_SIZE + bz * bdy + ty,
tx * VEC_SIZE + vec]
)
@@ -1043,7 +1067,7 @@ def _attention_decode(
for vec in
T.vectorized(VEC_SIZE):
K_smem[tile_start_s + j,
tx * VEC_SIZE + vec] = T.if_then_else(
rotary_mode == 1,
- _rope(pages,
k_rope_pos_offset[batch_idx] + row_g, head_dim, rope_theta, rope_scale,
(page_no, 0, by, page_offset, tx * VEC_SIZE + vec), qkv_dtype),
+ _rope(pages,
k_rope_pos_offset[batch_idx] + row_g, head_dim, rope_theta, rope_scale,
(page_no, 0, by, page_offset, tx * VEC_SIZE + vec), qkv_dtype, rope_scaling),
pages[page_no, 0, by,
page_offset, tx * VEC_SIZE + vec]
)
V_smem[tile_start_s + j,
tx * VEC_SIZE + vec] = pages[page_no, 1, by, page_offset, tx * VEC_SIZE + vec]
@@ -1210,7 +1234,331 @@ def _merge_state_inplace(num_heads, head_dim, v_dtype,
target: Target):
return merge_state_inplace
-def _attention_prefill_ragged(h_kv, h_q, d, dtype, target: Target):
+def _attention_sequence_prefill(
+ batch_size, h_kv, h_q, d, dtype, target: Target, causal=0,
attn_score_scaling_factor=1.0
+): # pylint: disable=line-too-long
+ LOAD_VEC = 8 // ((DataType(dtype).bits + 7) // 8) # 8 bytes
+ group_size = h_q // h_kv
+ sm_scale = 1.0 / math.sqrt(float(d)) * math.log2(math.exp(1))
+
+ bdx = 32
+ num_warps = 4
+ tile_x, tile_y, tile_z = 64 // ((DataType(dtype).bits + 7) // 8) // max(d
// 128, 1), d, 16
+
+ # Otherwise we would exceed maxComputeWorkgroupStorageSize
+ if (
+ str(target.kind) == "webgpu"
+ and ((d + 127) // 128) * ((DataType(dtype).bits + 15) // 16) >= 4
+ ):
+ tile_z = 8
+ num_warps = 2
+
+ # fmt: off
+ @T.prim_func
+ def batch_sequence_prefill_kv( # pylint: disable=too-many-branches
+ var_q: T.handle, # [total_len, h_q, d]
+ var_k: T.handle, # [total_len, h_kv, d]
+ var_v: T.handle, # [total_len, h_kv, d]
+ var_output: T.handle, # [total_len, h_q, d]
+ var_lse: T.handle # [total_len, h_q]
+ ):
+ qo_len = T.int32(is_size_var=True)
+ kv_len = T.int32(is_size_var=True)
+ q = T.match_buffer(var_q, (batch_size, qo_len, h_q, d), dtype)
+ k = T.match_buffer(var_k, (batch_size, kv_len, h_kv, d), dtype)
+ v = T.match_buffer(var_v, (batch_size, kv_len, h_kv, d), dtype)
+ output = T.match_buffer(var_output, (batch_size, qo_len, h_q, d),
dtype)
+ lse = T.match_buffer(var_lse, (batch_size, qo_len, h_q), dtype) #
pylint: disable=unused-variable
+
+ batch_tiles: T.int32 = T.ceildiv(qo_len * group_size, tile_x)
+
+ # kernel code
+ for lbx in T.thread_binding(T.cast(batch_size, "int32") * batch_tiles,
thread="blockIdx.x"):
+ for lby in T.thread_binding(h_kv, thread="blockIdx.y"):
+ for lty in T.thread_binding(num_warps, thread="threadIdx.y"):
+ for ltx in T.thread_binding(bdx, thread="threadIdx.x"):
+ with T.block("attn"):
+ vbx, by, ty, tx = T.axis.remap("SSSS", [lbx, lby,
lty, ltx])
+ T.reads()
+ T.writes()
+
+ Q_smem = T.alloc_buffer((tile_x, d), dtype,
scope="shared")
+ K_smem = T.alloc_buffer((tile_z, d), dtype,
scope="shared")
+ V_smem = T.alloc_buffer((tile_z, d), dtype,
scope="shared")
+ S_smem = T.alloc_buffer((tile_x, tile_z),
"float32", scope="shared")
+
+ S_local = T.alloc_buffer((tile_x, tile_z),
"float32", scope="local")
+ O_local = T.alloc_buffer((tile_x, d), "float32",
scope="local")
+
+ m_smem = T.alloc_buffer((tile_x,), "float32",
scope="shared")
+ m_prev_smem = T.alloc_buffer((tile_x,), "float32",
scope="shared")
+ d_smem = T.alloc_buffer((tile_x,), "float32",
scope="shared")
+
+ m_new = T.alloc_buffer(
+ (math.ceil(tile_x / (bdx * num_warps)),),
"float32", scope="local"
+ )
+ m_prev = T.alloc_buffer(
+ (math.ceil(tile_x / (bdx * num_warps)),),
"float32", scope="local"
+ )
+ d_new = T.alloc_buffer(
+ (math.ceil(tile_x / (bdx * num_warps)),),
"float32", scope="local"
+ )
+
+ b_idx: T.int32 = vbx // batch_tiles
+ tile_id: T.int32 = vbx % batch_tiles
+ LH_start: T.int32 = tile_id * tile_x
+ T.tvm_storage_sync("shared")
+
+ # init states
+ for i in T.serial(T.ceildiv(tile_x, bdx *
num_warps)):
+ row: T.int32 = i * bdx * num_warps + ty * bdx
+ tx
+ if row < tile_x:
+ m_smem[row] = -5e4
+ d_smem[row] = 1.0
+
+ for li, lj in T.grid(tile_x, tile_y):
+ with T.block("O_init"):
+ i, j = T.axis.remap("SS", [li, lj])
+ O_local[i, j] = 0.0
+ T.tvm_storage_sync("shared")
+
+ # Load Q from gmem to smem
+ for li, lj in T.grid(tile_x, tile_y):
+ with T.block("Q_load"):
+ i, j = T.axis.remap("SS", [li, lj])
+ T.reads()
+ T.writes()
+ cur_L = (LH_start + i) // group_size
+ cur_H_qo = by * group_size + (LH_start +
i) % group_size
+ if cur_L < qo_len:
+ Q_smem[i, j] = q[b_idx, cur_L,
cur_H_qo, j]
+ else:
+ Q_smem[i, j] = 0.0
+ T.tvm_storage_sync("shared")
+
+ for iterator in T.serial(T.ceildiv(kv_len,
tile_z)):
+ L_kv_start: T.int32 = iterator * tile_z
+ L_kv_base: T.int32 = 0
+ for lz, ly in T.grid(tile_z, tile_y):
+ with T.block("K_load"):
+ i, j = T.axis.remap("SS", [lz, ly])
+ T.reads()
+ T.writes()
+ cur_L = L_kv_start + i
+ if cur_L < kv_len:
+ K_smem[i, j] = k[
+ b_idx, L_kv_base + cur_L, by, j
+ ]
+ else:
+ K_smem[i, j] = 0.0
+ T.tvm_storage_sync("shared")
+ for lz, ly in T.grid(tile_z, tile_y):
+ with T.block("V_load"):
+ i, j = T.axis.remap("SS", [lz, ly])
+ T.reads()
+ T.writes()
+ cur_L = L_kv_start + i
+ if cur_L < kv_len:
+ V_smem[i, j] = v[
+ b_idx, L_kv_base + cur_L, by, j
+ ]
+ else:
+ V_smem[i, j] = 0.0
+ T.tvm_storage_sync("shared")
+
+ # Compute S
+ with T.block():
+ for li, lj, lk in T.grid(tile_x, tile_z,
tile_y):
+ with T.block("S_gemm"):
+ i, j, k = T.axis.remap("SSR", [li,
lj, lk])
+ with T.init():
+ S_local[i, j] = 0.0
+ S_local[i, j] += (
+ T.cast(Q_smem[i, k], "float32")
+ * T.cast(K_smem[j, k],
"float32")
+ * attn_score_scaling_factor
+ * sm_scale
+ )
+ T.tvm_storage_sync("shared")
+ for li, lj in T.grid(tile_x, tile_z):
+ with T.block("S_store"):
+ i, j = T.axis.remap("SS", [li, lj])
+ S_smem[i, j] = S_local[i, j]
+ T.tvm_storage_sync("shared")
+
+ # Update S, m, d
+ for i in T.serial(T.ceildiv(tile_x, bdx *
num_warps)):
+ row: T.int32 = i * bdx * num_warps + ty *
bdx + tx
+ if row < tile_x:
+ with T.block("update1"):
+ m_prev[i] = m_smem[row]
+ m_new[i] = m_smem[row]
+ # mask out of kv_chunk_len S
+ row_: T.int32 = (LH_start + row)
// group_size
+ for j in T.serial(tile_z):
+ if _causal_mask(
+ causal,
+ row=row_,
+ col=L_kv_start + j,
+ kv_len=kv_len,
+ qo_len=qo_len,
+ ):
+ m_new[i] = T.max(
+ m_new[i], S_smem[row,
j]
+ )
+ d_new[i] = d_smem[row] * T.exp2(
+ m_prev[i] - m_new[i]
+ )
+
+ for i in T.serial(T.ceildiv(tile_x, bdx *
num_warps)):
+ row: T.int32 = i * bdx * num_warps + ty *
bdx + tx
+ with T.block("update"):
+ for j in T.serial(tile_z):
+ # this is to avoid sync inside
condition branch
+ if row < tile_x:
+ row_: T.int32 = (
+ LH_start + row
+ ) // group_size
+ if _causal_mask(
+ causal,
+ row=row_,
+ col=L_kv_start + j,
+ kv_len=kv_len,
+ qo_len=qo_len,
+ ):
+ S_smem[row, j] = T.exp2(
+ S_smem[row, j] -
m_new[i]
+ )
+ else:
+ S_smem[row, j] =
T.exp2(-5e4 - m_new[i])
+
+ for i in T.serial(T.ceildiv(tile_x, bdx *
num_warps)):
+ row: T.int32 = i * bdx * num_warps + ty *
bdx + tx
+ if row < tile_x:
+ with T.block("update"):
+ for j in T.serial(tile_z):
+ d_new[i] += S_smem[row, j]
+ m_smem[row] = m_new[i]
+ d_smem[row] = d_new[i]
+ m_prev_smem[row] = m_prev[i]
+ T.tvm_storage_sync("shared")
+
+ # Update O
+ with T.block():
+ for li, lj, lk in T.grid(tile_x, tile_y,
tile_z):
+ with T.block("O_gemm"):
+ i, j, k = T.axis.remap("SSR", [li,
lj, lk])
+ with T.init():
+ O_local[i, j] *= T.exp2(
+ m_prev_smem[i] - m_smem[i]
+ )
+ O_local[i, j] += S_smem[i, k] *
T.cast(
+ V_smem[k, j], "float32"
+ )
+
+ # Store O from smem to gmem
+ for li, lj in T.grid(tile_x, tile_y):
+ with T.block("O_store"):
+ i, j = T.axis.remap("SS", [li, lj])
+ cur_L: T.int32 = 0 + (LH_start + i) //
group_size
+ cur_H_qo: T.int32 = (
+ by * group_size + (LH_start + i) %
group_size
+ )
+ if cur_L < qo_len:
+ output[b_idx, cur_L, cur_H_qo, j] = (
+ O_local[i, j] / d_smem[i]
+ )
+
+ # Store LSE to gmem
+ for li in T.grid(tile_x):
+ with T.block("lse_store"):
+ i = T.axis.remap("S", [li])
+ cur_L: T.int32 = 0 + (LH_start + i) //
group_size
+ cur_H_qo: T.int32 = (
+ by * group_size + (LH_start + i) %
group_size
+ )
+ if cur_L < qo_len:
+ lse[b_idx, cur_L, cur_H_qo] =
m_smem[i] + T.log2(
+ d_smem[i]
+ )
+
+ # fmt: on
+ # pylint: enable=line-too-long,too-many-branches
+ sch = tir.Schedule(batch_sequence_prefill_kv)
+
+ def get_tile_size(x, y, t):
+ cnt = (x * y) // t
+ assert (x * y) % t == 0
+ tile_y = (int)(math.ceil(math.sqrt(cnt)))
+ while (cnt % tile_y != 0 or y % tile_y != 0) and tile_y <= cnt:
+ tile_y += 1
+ assert tile_y <= cnt
+ tile_x = cnt // tile_y
+ return tile_x, tile_y
+
+ def apply_to_qkv_load(sch: tir.Schedule, block):
+ loop_x, loop_y = sch.get_loops(block)[-2:]
+ loop = sch.fuse(loop_x, loop_y)
+ _, ty, tx, vec = sch.split(
+ loop, factors=[None, num_warps, bdx, LOAD_VEC],
preserve_unit_iters=True
+ )
+ sch.bind(ty, "threadIdx.y")
+ sch.bind(tx, "threadIdx.x")
+ sch.vectorize(vec)
+
+ def apply_to_so_ewise(sch: tir.Schedule, block, tile):
+ loop_x, loop_y = sch.get_loops(block)[-2:]
+ xo, xi = sch.split(loop_x, factors=[None, tile[0]])
+ yo, yi = sch.split(loop_y, factors=[None, tile[1]])
+ sch.reorder(xo, yo, xi, yi)
+ t = sch.fuse(xo, yo)
+ ty, tx = sch.split(t, factors=[None, bdx])
+ sch.bind(ty, "threadIdx.y")
+ sch.bind(tx, "threadIdx.x")
+
+ def apply_to_gemm( # pylint: disable=unused-argument
+ sch: tir.Schedule, block, tile, read_0, read_1, r_len=8, k_major=False
+ ):
+ loop_x, loop_y, loop_z = sch.get_loops(block)[-3:]
+ xo, xi = sch.split(loop_x, factors=[None, tile[0]])
+ yo, yi = sch.split(loop_y, factors=[None, tile[1]])
+ sch.reorder(xo, yo, xi, yi)
+ t = sch.fuse(xo, yo)
+ ty, tx = sch.split(t, factors=[None, bdx])
+ sch.bind(ty, "threadIdx.y")
+ sch.bind(tx, "threadIdx.x")
+
+ ko, ki = sch.split(loop_z, factors=[None, r_len])
+ if k_major:
+ sch.reorder(ko, xi, yi, ki)
+ else:
+ sch.reorder(ko, ki, xi, yi)
+ sch.decompose_reduction(block, ty)
+
+ def apply_to_md(sch, block):
+ loop = sch.get_loops(block)[-1]
+ _, ty, tx = sch.split(loop, factors=[None, num_warps, bdx])
+ sch.bind(ty, "threadIdx.y")
+ sch.bind(tx, "threadIdx.x")
+
+ def apply_schedule(sch):
+ tile_s = get_tile_size(tile_x, tile_z, bdx * num_warps)
+ tile_o = get_tile_size(tile_x, tile_y, bdx * num_warps)
+ apply_to_gemm(sch, sch.get_block("S_gemm"), tile_s, 0, 1, k_major=True)
+ apply_to_gemm(sch, sch.get_block("O_gemm"), tile_o, 2, 3,
k_major=False)
+ apply_to_so_ewise(sch, sch.get_block("S_store"), tile_s)
+ apply_to_so_ewise(sch, sch.get_block("O_init"), tile_o)
+ apply_to_so_ewise(sch, sch.get_block("O_store"), tile_o)
+ apply_to_qkv_load(sch, sch.get_block("Q_load"))
+ apply_to_qkv_load(sch, sch.get_block("K_load"))
+ apply_to_qkv_load(sch, sch.get_block("V_load"))
+
+ apply_schedule(sch)
+ apply_to_md(sch, sch.get_block("lse_store"))
+ return sch.mod["main"].with_attr("tir.is_scheduled", 1)
+
+
+def _attention_prefill_ragged(h_kv, h_q, d, dtype, rope_scaling: Dict[str,
Any], target: Target):
# pylint: disable=line-too-long
NUM_BLKS = 16
LOAD_VEC = 8 // ((DataType(dtype).bits + 7) // 8) # 8 bytes
@@ -1344,7 +1692,7 @@ def _attention_prefill_ragged(h_kv, h_q, d, dtype,
target: Target):
if cur_L < q_indptr[b_idx + 1]:
Q_smem[i, j] = T.if_then_else(
rotary_mode == 1,
- _rope(q,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype),
+ _rope(q,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype,
rope_scaling),
q[cur_L, cur_H_qo, j]
)
else:
@@ -1363,7 +1711,7 @@ def _attention_prefill_ragged(h_kv, h_q, d, dtype,
target: Target):
if cur_L < kv_chunk_len[0]:
K_smem[i, j] =
T.if_then_else(
rotary_mode == 1,
- _rope(k,
k_rope_pos_offset[b_idx] + cur_L, d, rope_theta, rope_scale, (L_kv_base +
cur_L, by, j), dtype),
+ _rope(k,
k_rope_pos_offset[b_idx] + cur_L, d, rope_theta, rope_scale, (L_kv_base +
cur_L, by, j), dtype, rope_scaling),
k[L_kv_base + cur_L,
by, j]
)
else:
diff --git a/python/tvm/relax/frontend/nn/llm/position_embedding.py
b/python/tvm/relax/frontend/nn/llm/position_embedding.py
index b224ce04c5..4373395e32 100644
--- a/python/tvm/relax/frontend/nn/llm/position_embedding.py
+++ b/python/tvm/relax/frontend/nn/llm/position_embedding.py
@@ -17,7 +17,9 @@
"""Operators for positional embeddings, e.g. RoPE."""
-from typing import Optional, Tuple
+import math
+from functools import partial
+from typing import Any, Callable, Dict, Optional, Tuple
from tvm import tir
from tvm.relax.frontend.nn import Tensor, op
@@ -26,7 +28,7 @@ from tvm.script import tir as T
# pylint: disable=invalid-name
-def rope_freq(s: tir.Var, d: tir.Var, d_range: int, theta: float, dtype: str):
+def rope_freq_default(s: tir.Var, d: tir.Var, d_range: int, theta: float,
dtype: str):
"""Compute the inverse frequency of RoPE and then return the cosine and
sine of it.
Parameters
@@ -53,11 +55,95 @@ def rope_freq(s: tir.Var, d: tir.Var, d_range: int, theta:
float, dtype: str):
sin_freq : Tensor
The sine of the inverse frequency.
+
+ var_map: Dict[tir.Var, tir.PrimExpr]
+ The common expression map.
"""
freq = s / tir.power(theta, d * 2 % d_range / tir.const(d_range,
"float32"))
- cos_freq = tir.cos(freq).astype(dtype)
- sin_freq = tir.sin(freq).astype(dtype)
- return cos_freq, sin_freq
+ freq_var = tir.Var("freq", "float32")
+ cos_freq = tir.cos(freq_var).astype(dtype)
+ sin_freq = tir.sin(freq_var).astype(dtype)
+ return cos_freq, sin_freq, {freq_var: freq}
+
+
+def rope_freq_llama3( # pylint: disable=too-many-arguments,too-many-locals
+ s: tir.Var,
+ d: tir.Var,
+ d_range: int,
+ theta: float,
+ dtype: str,
+ factor: float,
+ low_freq_factor: float,
+ high_freq_factor: float,
+ original_max_position_embeddings: float,
+):
+ """Compute the inverse frequency of RoPE for llama3 RoPE scaling."""
+ orig_freq = tir.const(1, "float32") / tir.power(
+ theta, d * 2 % d_range / tir.const(d_range, "float32")
+ )
+ orig_freq_var = tir.Var("orig_freq", "float32")
+ inv_diff_freq_factor = 1.0 / (high_freq_factor - low_freq_factor)
+ llama3_inv_scaling_factor = 1.0 / factor
+ llama3_alpha = original_max_position_embeddings / (2 * math.pi) *
inv_diff_freq_factor
+ llama3_beta = low_freq_factor * inv_diff_freq_factor
+ smooth = tir.max(0.0, tir.min(1.0, llama3_alpha * orig_freq_var -
llama3_beta))
+ smoothed_freq = s * (
+ (1.0 - smooth) * orig_freq_var * llama3_inv_scaling_factor + smooth *
orig_freq_var
+ )
+ smoothed_freq_var = tir.Var("smoothed_freq", "float32")
+ cos_freq = tir.cos(smoothed_freq_var).astype(dtype)
+ sin_freq = tir.sin(smoothed_freq_var).astype(dtype)
+ return cos_freq, sin_freq, {smoothed_freq_var: smoothed_freq,
orig_freq_var: orig_freq}
+
+
+def rope_freq_longrope( # pylint: disable=too-many-arguments
+ s: tir.Var,
+ d: tir.Var,
+ d_range: int,
+ theta: float,
+ dtype: str,
+ max_position_embeddings: int,
+ original_max_position_embeddings: int,
+ ext_factors: Optional[T.Buffer] = None,
+):
+ """Compute the inverse frequency of RoPE for longrope scaling."""
+ scale = max_position_embeddings / original_max_position_embeddings
+ scaling_factor = (
+ math.sqrt(1 + math.log(scale) /
math.log(original_max_position_embeddings))
+ if scale > 1.0
+ else 1.0
+ )
+ divisor = tir.power(theta, d * 2 % d_range / tir.const(d_range, "float32"))
+ if ext_factors is not None:
+ divisor = ext_factors[d % (d_range // 2)] * divisor
+ freq = s / divisor
+ freq_var = tir.Var("freq", "float32")
+ cos_freq = (tir.cos(freq_var) * scaling_factor).astype(dtype)
+ sin_freq = (tir.sin(freq_var) * scaling_factor).astype(dtype)
+ return cos_freq, sin_freq, {freq_var: freq}
+
+
+def switch_rope_freq_func(rope_scaling: Dict[str, Any]) -> Callable:
+ """Return the RoPE inverse frequency computation function based
+ on the given RoPE scaling.
+ """
+ if "rope_type" not in rope_scaling:
+ return rope_freq_default
+ if rope_scaling["rope_type"] == "llama3":
+ return partial(
+ rope_freq_llama3,
+ factor=rope_scaling["factor"],
+ low_freq_factor=rope_scaling["low_freq_factor"],
+ high_freq_factor=rope_scaling["high_freq_factor"],
+
original_max_position_embeddings=rope_scaling["original_max_position_embeddings"],
+ )
+ if rope_scaling["rope_type"] == "longrope":
+ return partial(
+ rope_freq_longrope,
+ max_position_embeddings=rope_scaling["max_position_embeddings"],
+
original_max_position_embeddings=rope_scaling["original_max_position_embeddings"],
+ )
+ raise ValueError(f'Unsupported RoPE scaling type:
{rope_scaling["rope_type"]}')
# mypy: disable-error-code="attr-defined"
@@ -67,9 +153,10 @@ def llama_rope( # pylint: disable=too-many-arguments
qkv: Tensor,
total_seq_len: tir.Var,
theta: float,
+ scale: float,
num_q_heads: int,
num_kv_heads: int,
- scale: float = 1.0,
+ rope_scaling: Dict[str, Any],
rotary_dim: Optional[int] = None,
) -> Tuple[Tensor, Tensor, Tensor]:
"""Llama-style RoPE. Given a fused QKV tensor, it returns three tensors,
Q, K, and V, where Q
@@ -96,6 +183,9 @@ def llama_rope( # pylint: disable=too-many-arguments
num_kv_heads : int
The number of key/value heads. It differs from `num_q_heads` in
group-query attention.
+ rope_scaling : Dict
+ The configuration of RoPE scaling.
+
rotary_dim : Optional[int]
The number of dimensions in the embedding that RoPE is applied to. By
default, the
rotary_dim is the same as head_dim.
@@ -126,14 +216,19 @@ def llama_rope( # pylint: disable=too-many-arguments
d: tir.Var,
offset: tir.Var,
):
- cos_freq, sin_freq = rope_freq((s + offset) * scale, d, rotary_dim,
theta, dtype)
+ cos_freq, sin_freq, var_map = switch_rope_freq_func(rope_scaling)(
+ (s + offset) * scale, d, rotary_dim, theta, dtype
+ )
cos = cos_freq * x[b, s, h, d]
sin = sin_freq * tir.if_then_else(
d < rotary_dim // 2,
-x[b, s, h, d + rotary_dim // 2],
x[b, s, h, d - rotary_dim // 2],
)
- return cos + sin
+ expr = cos + sin
+ for var, value in var_map.items():
+ expr = tir.Let(var, value, expr)
+ return expr
@T.prim_func(private=True)
def fused_rope( # pylint: disable=too-many-locals
@@ -193,6 +288,7 @@ def llama_rope_with_position_map( # pylint:
disable=too-many-arguments
num_q_heads: int,
num_kv_heads: int,
dtype: str,
+ rope_scaling: Dict[str, Any],
rotary_dim: Optional[int] = None,
):
"""Return the TIR function that computes Llama-style RoPE with q position
map.
@@ -217,6 +313,9 @@ def llama_rope_with_position_map( # pylint:
disable=too-many-arguments
dtype : str
The dtype of qkv data.
+ rope_scaling : Dict
+ The configuration of RoPE scaling.
+
rotary_dim : int
The number of dimensions in the embedding that RoPE is applied to. By
default, the
rotary_dim is the same as head_dim.
@@ -225,6 +324,7 @@ def llama_rope_with_position_map( # pylint:
disable=too-many-arguments
if rotary_dim is None:
rotary_dim = head_dim
scale = tir.const(scale, "float32")
+ is_longrope_scaling = rope_scaling.get("rope_type") == "longrope"
def _rope( # pylint: disable=too-many-arguments
x: T.Buffer,
@@ -232,15 +332,24 @@ def llama_rope_with_position_map( # pylint:
disable=too-many-arguments
h: tir.Var,
d: tir.Var,
pos: tir.Var,
+ ext_factors: Optional[T.Buffer] = None,
):
- cos_freq, sin_freq = rope_freq(pos * scale, d, rotary_dim, theta,
"float32")
+ kwargs = {}
+ if ext_factors:
+ kwargs["ext_factors"] = ext_factors
+ cos_freq, sin_freq, var_map = switch_rope_freq_func(rope_scaling)(
+ pos * scale, d, rotary_dim, theta, "float32", **kwargs
+ )
cos = cos_freq * x[s, h, d].astype("float32")
sin = sin_freq * tir.if_then_else(
d < rotary_dim // 2,
-x[s, h, d + rotary_dim // 2],
x[s, h, d - rotary_dim // 2],
).astype("float32")
- return (cos + sin).astype(dtype)
+ expr = (cos + sin).astype(dtype)
+ for var, value in var_map.items():
+ expr = tir.Let(var, value, expr)
+ return expr
@T.prim_func
def fused_rope( # pylint: disable=too-many-locals
@@ -257,8 +366,8 @@ def llama_rope_with_position_map( # pylint:
disable=too-many-arguments
"tir.noalias": T.bool(True),
}
)
- seq_len = T.int64()
- position_map_elem_offset = T.int64()
+ seq_len = T.int32()
+ position_map_elem_offset = T.int32()
qkv = T.match_buffer(var_qkv, (seq_len, fused_heads, head_dim), dtype)
q = T.match_buffer(var_q, (seq_len, num_q_heads, head_dim), dtype)
k = T.match_buffer(var_k, (seq_len, num_kv_heads, head_dim), dtype)
@@ -284,4 +393,62 @@ def llama_rope_with_position_map( # pylint:
disable=too-many-arguments
else:
v[s, h - (num_q_heads + num_kv_heads), d] = qkv[s, h, d]
+ @T.prim_func
+ def fused_rope_longrope_scaling( # pylint: disable=too-many-locals
+ var_qkv: T.handle,
+ var_position_map: T.handle,
+ var_q: T.handle,
+ var_k: T.handle,
+ var_v: T.handle,
+ ext_factors: T.Buffer((head_dim // 2,), "float32"), # type: ignore
+ ):
+ T.func_attr(
+ {
+ "op_pattern": 8, # 2 means injective, 8 means opaque
+ "tir.noalias": T.bool(True),
+ }
+ )
+ seq_len = T.int64()
+ position_map_elem_offset = T.int64()
+ qkv = T.match_buffer(var_qkv, (seq_len, fused_heads, head_dim), dtype)
+ q = T.match_buffer(var_q, (seq_len, num_q_heads, head_dim), dtype)
+ k = T.match_buffer(var_k, (seq_len, num_kv_heads, head_dim), dtype)
+ v = T.match_buffer(var_v, (seq_len, num_kv_heads, head_dim), dtype)
+ position_map = T.match_buffer(
+ var_position_map, (seq_len,), "int32",
elem_offset=position_map_elem_offset
+ )
+ for iters in T.grid(seq_len, fused_heads, head_dim):
+ with T.block("llama_fused_rope"):
+ s, h, d = T.axis.remap("SSS", iters)
+ if h < num_q_heads:
+ q[s, h, d] = T.if_then_else(
+ d < rotary_dim,
+ _rope(
+ qkv,
+ s,
+ h,
+ d,
+ position_map[s],
+ ext_factors if is_longrope_scaling else None,
+ ),
+ qkv[s, h, d],
+ )
+ elif h < num_q_heads + num_kv_heads:
+ k[s, h - num_q_heads, d] = T.if_then_else(
+ d < rotary_dim,
+ _rope(
+ qkv,
+ s,
+ h,
+ d,
+ position_map[s],
+ ext_factors if is_longrope_scaling else None,
+ ),
+ qkv[s, h, d],
+ )
+ else:
+ v[s, h - (num_q_heads + num_kv_heads), d] = qkv[s, h, d]
+
+ if is_longrope_scaling:
+ return fused_rope_longrope_scaling
return fused_rope
diff --git a/python/tvm/relax/frontend/nn/llm/tree_attn.py
b/python/tvm/relax/frontend/nn/llm/tree_attn.py
index 486491dbf2..069eb48923 100644
--- a/python/tvm/relax/frontend/nn/llm/tree_attn.py
+++ b/python/tvm/relax/frontend/nn/llm/tree_attn.py
@@ -19,14 +19,14 @@
"""Operators for tree attention."""
import math
-from typing import Tuple
+from typing import Any, Dict, Tuple
from tvm import tir
from tvm.runtime import DataType
from tvm.script import tir as T
from tvm.target import Target
-from .position_embedding import rope_freq
+from .position_embedding import switch_rope_freq_func
# mypy: disable-error-code="attr-defined,valid-type,no-redef"
# pylint: disable=too-many-statements,too-many-locals,too-many-arguments
@@ -43,24 +43,30 @@ def _rope(
theta: tir.Var,
scale: tir.Var,
indices: Tuple[tir.Var, ...],
- qkv_dtype="float16",
+ qkv_dtype: str,
+ rope_scaling: Dict[str, Any],
):
d = indices[-1]
- cos_freq, sin_freq = rope_freq(offset * scale, d, rotary_dim, theta,
qkv_dtype)
- cos = cos_freq * buffer[indices]
+ cos_freq, sin_freq, var_map = switch_rope_freq_func(rope_scaling)(
+ offset * scale, d, rotary_dim, theta, "float32"
+ )
+ cos = cos_freq * buffer[indices].astype("float32")
sin = sin_freq * tir.if_then_else(
d < rotary_dim // 2,
-buffer[indices[:-1] + (d + rotary_dim // 2,)],
buffer[indices[:-1] + (d - rotary_dim // 2,)],
- )
- return cos + sin
+ ).astype("float32")
+ expr = (cos + sin).astype(qkv_dtype)
+ for var, value in var_map.items():
+ expr = tir.Let(var, value, expr)
+ return expr
def _tree_mask(row, col, mask_ptr, offset, stride, kv_len):
return tir.all(col < kv_len, mask_ptr[offset + row * stride + col] == 1)
-def tree_attn(h_kv, h_q, d, dtype, target: Target): # pylint:
disable=unused-argument
+def tree_attn(h_kv, h_q, d, dtype, rope_scaling: Dict[str, Any], target:
Target):
"""Generate tree attention kernel for batched tree attention.
Parameters
@@ -217,7 +223,7 @@ def tree_attn(h_kv, h_q, d, dtype, target: Target): #
pylint: disable=unused-ar
if cur_L < q_indptr[b_idx + 1]:
Q_smem[i, j] = T.if_then_else(
rotary_mode == 1,
- _rope(q,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype),
+ _rope(q,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype,
rope_scaling),
q[cur_L, cur_H_qo, j]
)
else:
@@ -236,7 +242,7 @@ def tree_attn(h_kv, h_q, d, dtype, target: Target): #
pylint: disable=unused-ar
if L_kv_start + i <
kv_chunk_len[0]:
K_smem[i, j] =
T.if_then_else(
rotary_mode == 1,
- _rope(k,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, by, j), dtype),
+ _rope(k,
q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, by, j), dtype,
rope_scaling),
k[cur_L, by, j]
)
V_smem[i, j] = v[cur_L,
by, j]
diff --git
a/tests/python/relax/test_runtime_builtin_paged_attention_kv_cache_tir.py
b/tests/python/relax/test_runtime_builtin_paged_attention_kv_cache_tir.py
index ff655e141b..c35b7062cd 100644
--- a/tests/python/relax/test_runtime_builtin_paged_attention_kv_cache_tir.py
+++ b/tests/python/relax/test_runtime_builtin_paged_attention_kv_cache_tir.py
@@ -49,6 +49,7 @@ num_kv_heads = 4
head_dim = None
rope_scale = 1.0
rope_theta = 1e4
+rope_scaling = {}
dtype = None
device = tvm.cuda()
@@ -113,15 +114,19 @@ def set_global_func(head_dim, dtype):
for tir_func in [
_kv_cache_transpose_append(num_kv_heads, head_dim, dtype),
_kv_cache_debug_get_kv(num_layers, num_kv_heads, head_dim, dtype),
- _attention_prefill(num_kv_heads, num_qo_heads, head_dim, dtype, False,
target),
- _attention_decode(num_kv_heads, num_qo_heads, head_dim, dtype, False,
target),
- _attention_prefill(num_kv_heads, num_qo_heads, head_dim, dtype, True,
target),
- _attention_decode(num_kv_heads, num_qo_heads, head_dim, dtype, True,
target),
- _attention_prefill_ragged(num_kv_heads, num_qo_heads, head_dim, dtype,
target),
- tree_attn(num_kv_heads, num_qo_heads, head_dim, dtype, target),
+ _attention_prefill(
+ num_kv_heads, num_qo_heads, head_dim, dtype, False, rope_scaling,
target
+ ),
+ _attention_decode(num_kv_heads, num_qo_heads, head_dim, dtype, False,
rope_scaling, target),
+ _attention_prefill(num_kv_heads, num_qo_heads, head_dim, dtype, True,
rope_scaling, target),
+ _attention_decode(num_kv_heads, num_qo_heads, head_dim, dtype, True,
rope_scaling, target),
+ _attention_prefill_ragged(
+ num_kv_heads, num_qo_heads, head_dim, dtype, rope_scaling, target
+ ),
+ tree_attn(num_kv_heads, num_qo_heads, head_dim, dtype, rope_scaling,
target),
_merge_state_inplace(num_qo_heads, head_dim, dtype, target),
llama_rope_with_position_map(
- rope_theta, rope_scale, head_dim, num_qo_heads, num_kv_heads, dtype
+ rope_theta, rope_scale, head_dim, num_qo_heads, num_kv_heads,
dtype, rope_scaling
),
_copy_single_page(num_kv_heads, page_size, head_dim, dtype, target),
_compact_kv_copy(num_kv_heads, head_dim, dtype, target),