Kernels:
Trusted publisher
Uploaded using `kernel-builder`.
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- build/torch-rocm/__init__.py +96 -0
- build/torch-rocm/_aiter_compat/__init__.py +30 -0
- build/torch-rocm/_aiter_compat/chip_info.py +48 -0
- build/torch-rocm/_aiter_compat/dtypes.py +45 -0
- build/torch-rocm/_aiter_compat/torch_guard.py +30 -0
- build/torch-rocm/_aiter_compat/triton_metadata_redirect.py +165 -0
- build/torch-rocm/_gluon_kernels/__init__.py +2 -0
- build/torch-rocm/_gluon_kernels/gfx1250/fusions/__init__.py +0 -0
- build/torch-rocm/_gluon_kernels/gfx1250/fusions/fused_kv_cache.py +680 -0
- build/torch-rocm/_gluon_kernels/gfx1250/gemm/basic/__init__.py +0 -0
- build/torch-rocm/_gluon_kernels/gfx1250/gemm/basic/gemm_a16w16.py +703 -0
- build/torch-rocm/_gluon_kernels/gfx1250/gemm/basic/gemm_mxfp4.py +401 -0
- build/torch-rocm/_gluon_kernels/gfx1250/moe/__init__.py +0 -0
- build/torch-rocm/_gluon_kernels/gfx1250/moe/moe_op_gemm_a8w4.py +1170 -0
- build/torch-rocm/_gluon_kernels/gfx1250/norm/__init__.py +0 -0
- build/torch-rocm/_gluon_kernels/gfx1250/norm/fused_rmsnorm_add.py +122 -0
- build/torch-rocm/_gluon_kernels/gfx942/__init__.py +0 -0
- build/torch-rocm/_gluon_kernels/gfx942/moe/__init__.py +0 -0
- build/torch-rocm/_gluon_kernels/gfx942/moe/moe_op_gemm_int8_smoothquant.py +273 -0
- build/torch-rocm/_ops.py +38 -0
- build/torch-rocm/_triton_kernels/__init__.py +0 -0
- build/torch-rocm/_triton_kernels/activation.py +317 -0
- build/torch-rocm/_triton_kernels/causal_conv1d.py +631 -0
- build/torch-rocm/_triton_kernels/causal_conv1d_update_single_token.py +554 -0
- build/torch-rocm/_triton_kernels/common/__init__.py +0 -0
- build/torch-rocm/_triton_kernels/common/splitk_reduce.py +89 -0
- build/torch-rocm/_triton_kernels/fusions/__init__.py +20 -0
- build/torch-rocm/_triton_kernels/fusions/fused_bmm_rope_kv_cache.py +1043 -0
- build/torch-rocm/_triton_kernels/fusions/fused_clamp_act_mul.py +173 -0
- build/torch-rocm/_triton_kernels/fusions/fused_kv_cache.py +1124 -0
- build/torch-rocm/_triton_kernels/fusions/fused_mul_add.py +47 -0
- build/torch-rocm/_triton_kernels/fusions/fused_qk_concat.py +285 -0
- build/torch-rocm/_triton_kernels/fusions/fused_reduce_qk_norm_rope_swa_write.py +289 -0
- build/torch-rocm/_triton_kernels/fusions/fused_routing_from_topk.py +175 -0
- build/torch-rocm/_triton_kernels/fusions/mhc.py +1277 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/__init__.py +50 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/decode/__init__.py +21 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/decode/causal_conv1d_split_qkv.py +1098 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/decode/fused_rearrange_sigmoid_gdr.py +165 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/decode/fused_recurrent.py +191 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/decode/fused_sigmoid_gating_recurrent.py +266 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/fused_qkvzba_split.py +580 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/gated_delta_rule_utils.py +580 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/__init__.py +43 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/causal_conv1d_fwd_split_qkv.py +399 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/chunk.py +374 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/chunk_delta_h.py +1455 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/chunk_o.py +1197 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/fused_cumsum_kkt.py +339 -0
- build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/fused_gdn_gating_prefill.py +80 -0
build/torch-rocm/__init__.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
"""AITER Triton kernels for AMD ROCm.
|
| 4 |
+
|
| 5 |
+
Repackaged from the ``aiter/ops/triton/**`` subtree of the
|
| 6 |
+
`ROCm/aiter <https://github.com/ROCm/aiter>`_ project as a self-contained
|
| 7 |
+
Hugging Face Hub kernel. Each subpackage under :mod:`aiter_kernels` maps 1:1
|
| 8 |
+
to the equivalent upstream module under ``aiter.ops.triton``.
|
| 9 |
+
|
| 10 |
+
Flash Attention is **not** included here — it lives in
|
| 11 |
+
``kernels-community/aiter-flash-attn`` and is synced separately.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
from . import _aiter_compat # noqa: F401 (must be importable before ops)
|
| 17 |
+
|
| 18 |
+
from . import activation
|
| 19 |
+
from . import causal_conv1d
|
| 20 |
+
from . import causal_conv1d_update_single_token
|
| 21 |
+
from . import fusions
|
| 22 |
+
from . import gated_delta_net
|
| 23 |
+
from . import gather_kv_b_proj
|
| 24 |
+
from . import gemm
|
| 25 |
+
from . import gluon
|
| 26 |
+
from . import gmm
|
| 27 |
+
from . import kv_cache
|
| 28 |
+
from . import moe
|
| 29 |
+
from . import normalization
|
| 30 |
+
from . import quant
|
| 31 |
+
from . import rope
|
| 32 |
+
# Top-level re-exports for drop-in parity with the standalone ``aiter-rope`` repo.
|
| 33 |
+
from .rope import RotateStyle, apply_rotary_transformers
|
| 34 |
+
from . import softmax
|
| 35 |
+
from . import topk
|
| 36 |
+
from . import utils
|
| 37 |
+
|
| 38 |
+
# ``comms`` pulls in iris and is optional — make it import-safe.
|
| 39 |
+
try:
|
| 40 |
+
from . import comms
|
| 41 |
+
|
| 42 |
+
# Re-export communication primitives at this level for convenience
|
| 43 |
+
from .comms import (
|
| 44 |
+
IrisCommContext,
|
| 45 |
+
reduce_scatter,
|
| 46 |
+
all_gather,
|
| 47 |
+
reduce_scatter_rmsnorm_quant_all_gather,
|
| 48 |
+
IRIS_COMM_AVAILABLE,
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
_COMMS_AVAILABLE = True
|
| 52 |
+
except ImportError:
|
| 53 |
+
_COMMS_AVAILABLE = False
|
| 54 |
+
IRIS_COMM_AVAILABLE = False
|
| 55 |
+
comms = None # type: ignore[assignment]
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
__kernel_metadata__ = {
|
| 59 |
+
"license": "mit",
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
__all__ = [
|
| 64 |
+
"__kernel_metadata__",
|
| 65 |
+
"RotateStyle",
|
| 66 |
+
"apply_rotary_transformers",
|
| 67 |
+
"activation",
|
| 68 |
+
"causal_conv1d",
|
| 69 |
+
"causal_conv1d_update_single_token",
|
| 70 |
+
"comms",
|
| 71 |
+
"fusions",
|
| 72 |
+
"gated_delta_net",
|
| 73 |
+
"gather_kv_b_proj",
|
| 74 |
+
"gemm",
|
| 75 |
+
"gluon",
|
| 76 |
+
"gmm",
|
| 77 |
+
"kv_cache",
|
| 78 |
+
"moe",
|
| 79 |
+
"normalization",
|
| 80 |
+
"quant",
|
| 81 |
+
"rope",
|
| 82 |
+
"softmax",
|
| 83 |
+
"topk",
|
| 84 |
+
"utils",
|
| 85 |
+
]
|
| 86 |
+
|
| 87 |
+
if _COMMS_AVAILABLE:
|
| 88 |
+
__all__.extend(
|
| 89 |
+
[
|
| 90 |
+
"IrisCommContext",
|
| 91 |
+
"reduce_scatter",
|
| 92 |
+
"all_gather",
|
| 93 |
+
"reduce_scatter_rmsnorm_quant_all_gather",
|
| 94 |
+
"IRIS_COMM_AVAILABLE",
|
| 95 |
+
]
|
| 96 |
+
)
|
build/torch-rocm/_aiter_compat/__init__.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility shim layer for aiter cross-tree dependencies.
|
| 2 |
+
|
| 3 |
+
The upstream ``aiter/ops/triton/**`` Triton ops import a handful of helpers
|
| 4 |
+
from outside the triton subtree:
|
| 5 |
+
|
| 6 |
+
- ``aiter.dtypes`` / ``aiter.utility.dtypes`` — dtype constants keyed by GPU arch.
|
| 7 |
+
- ``aiter.jit.utils.torch_guard.torch_compile_guard`` — torch.compile/torch.library
|
| 8 |
+
registration helper.
|
| 9 |
+
- ``aiter.utility.triton.triton_metadata_redirect.AOTMetadataContext`` — AOT
|
| 10 |
+
metadata-path redirector.
|
| 11 |
+
- ``aiter.jit.utils.chip_info.get_gfx`` — GPU arch query.
|
| 12 |
+
|
| 13 |
+
In a Hub-kernel build we don't ship the rest of upstream aiter — we vendor
|
| 14 |
+
just enough here that the Triton ops import cleanly. The runtime hot paths
|
| 15 |
+
(the actual Triton kernels) do not rely on these helpers; they're used for
|
| 16 |
+
torch.library schema registration, AOT caching, and dtype lookups that we
|
| 17 |
+
re-derive from torch / Triton directly.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from . import dtypes
|
| 21 |
+
from . import chip_info
|
| 22 |
+
from . import torch_guard
|
| 23 |
+
from . import triton_metadata_redirect
|
| 24 |
+
|
| 25 |
+
__all__ = [
|
| 26 |
+
"dtypes",
|
| 27 |
+
"chip_info",
|
| 28 |
+
"torch_guard",
|
| 29 |
+
"triton_metadata_redirect",
|
| 30 |
+
]
|
build/torch-rocm/_aiter_compat/chip_info.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Minimal ``get_gfx()`` replacement.
|
| 2 |
+
|
| 3 |
+
Upstream lives in ``aiter/jit/utils/chip_info.py`` and pulls in the JIT C++
|
| 4 |
+
build machinery. We only need the chip-arch string here.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import functools
|
| 10 |
+
import os
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@functools.lru_cache(maxsize=1)
|
| 14 |
+
def get_gfx() -> str:
|
| 15 |
+
"""Return the active GPU arch string (e.g. ``"gfx942"``).
|
| 16 |
+
|
| 17 |
+
Resolution order:
|
| 18 |
+
1. ``GFX_ARCH`` env var (explicit override).
|
| 19 |
+
2. Triton's active driver target.
|
| 20 |
+
3. ``torch.cuda.get_device_properties(0).gcnArchName`` (ROCm/HIP only).
|
| 21 |
+
4. ``""`` if none are available — callers using this for fast-path
|
| 22 |
+
selection should treat that as "unknown arch, use safe defaults".
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
env = os.environ.get("GFX_ARCH")
|
| 26 |
+
if env:
|
| 27 |
+
return env
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
import triton
|
| 31 |
+
|
| 32 |
+
target = triton.runtime.driver.active.get_current_target()
|
| 33 |
+
arch = getattr(target, "arch", None)
|
| 34 |
+
if isinstance(arch, str) and arch.startswith("gfx"):
|
| 35 |
+
return arch
|
| 36 |
+
except Exception:
|
| 37 |
+
pass
|
| 38 |
+
|
| 39 |
+
try:
|
| 40 |
+
import torch
|
| 41 |
+
|
| 42 |
+
if torch.cuda.is_available():
|
| 43 |
+
name = torch.cuda.get_device_properties(0).gcnArchName
|
| 44 |
+
return name.split(":")[0] if name else ""
|
| 45 |
+
except Exception:
|
| 46 |
+
pass
|
| 47 |
+
|
| 48 |
+
return ""
|
build/torch-rocm/_aiter_compat/dtypes.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Minimal local replacement for ``aiter.utility.dtypes``.
|
| 2 |
+
|
| 3 |
+
Upstream parses a generated C header (``csrc/include/aiter_enum.h``) to derive
|
| 4 |
+
its dtype table and depends on ``aiter.ops.enum`` (which in turn pulls the
|
| 5 |
+
JIT C++ extension). For a Triton-only Hub kernel we only need the dtype
|
| 6 |
+
constants the Triton ops actually reference — none of the enum / header
|
| 7 |
+
machinery is required.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
from .chip_info import get_gfx
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
_FP8_BY_ARCH = {
|
| 18 |
+
"gfx942": getattr(torch, "float8_e4m3fnuz", None),
|
| 19 |
+
"gfx950": getattr(torch, "float8_e4m3fn", None),
|
| 20 |
+
"gfx1200": getattr(torch, "float8_e4m3fn", None),
|
| 21 |
+
"gfx1201": getattr(torch, "float8_e4m3fn", None),
|
| 22 |
+
"gfx1250": getattr(torch, "float8_e4m3fn", None),
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
_8BIT_FALLBACK = torch.uint8
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def get_dtype_fp8() -> torch.dtype:
|
| 29 |
+
return _FP8_BY_ARCH.get(get_gfx()) or _8BIT_FALLBACK
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
i4x2 = getattr(torch, "int4", _8BIT_FALLBACK)
|
| 33 |
+
fp4x2 = getattr(torch, "float4_e2m1fn_x2", _8BIT_FALLBACK)
|
| 34 |
+
fp8 = get_dtype_fp8()
|
| 35 |
+
fp8_e8m0 = getattr(torch, "float8_e8m0fnu", _8BIT_FALLBACK)
|
| 36 |
+
fp16 = torch.float16
|
| 37 |
+
bf16 = torch.bfloat16
|
| 38 |
+
fp32 = torch.float32
|
| 39 |
+
u32 = torch.uint32
|
| 40 |
+
i32 = torch.int32
|
| 41 |
+
i16 = torch.int16
|
| 42 |
+
i8 = torch.int8
|
| 43 |
+
u8 = torch.uint8
|
| 44 |
+
i64 = torch.int64
|
| 45 |
+
u64 = torch.uint64
|
build/torch-rocm/_aiter_compat/torch_guard.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""No-op replacement for ``aiter.jit.utils.torch_guard``.
|
| 2 |
+
|
| 3 |
+
Upstream's ``torch_compile_guard`` registers each decorated function with
|
| 4 |
+
``torch.library`` under the global ``"aiter"`` namespace and routes calls
|
| 5 |
+
through ``torch.ops.aiter.<name>``. That's appropriate for the full aiter
|
| 6 |
+
install (which ships a C++ extension that also registers ops there) but
|
| 7 |
+
would clash with a parallel ``import aiter`` in the same process and is
|
| 8 |
+
unnecessary for Triton-only kernels.
|
| 9 |
+
|
| 10 |
+
This stub returns the decorated function unmodified — the Triton ops still
|
| 11 |
+
run, just without the ``torch.ops.aiter.*`` indirection.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
from typing import Any, Callable, Optional, Union
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def torch_compile_guard(
|
| 20 |
+
mutates_args: Union[list, str] = "unknown",
|
| 21 |
+
device: str = "cpu",
|
| 22 |
+
calling_func_: Optional[Callable[..., Any]] = None,
|
| 23 |
+
gen_fake: Optional[Callable[..., Any]] = None,
|
| 24 |
+
):
|
| 25 |
+
"""No-op decorator factory: returns the function unchanged."""
|
| 26 |
+
|
| 27 |
+
def decorator(func):
|
| 28 |
+
return func
|
| 29 |
+
|
| 30 |
+
return decorator
|
build/torch-rocm/_aiter_compat/triton_metadata_redirect.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2025, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
"""
|
| 5 |
+
Triton Metadata Redirect Module
|
| 6 |
+
|
| 7 |
+
This module provides decorators and utilities for customizing Triton kernel
|
| 8 |
+
metadata file paths during compilation. It allows redirecting .json and
|
| 9 |
+
.hsaco files to custom directories.
|
| 10 |
+
|
| 11 |
+
Example usage0 for jit:
|
| 12 |
+
from aiter.utility.triton_metadata_redirect import with_custom_metadata_path
|
| 13 |
+
|
| 14 |
+
@with_custom_metadata_path("/custom/path")
|
| 15 |
+
@triton.jit
|
| 16 |
+
def my_kernel(...):
|
| 17 |
+
...
|
| 18 |
+
|
| 19 |
+
Example usage1 for aot:
|
| 20 |
+
from aiter.utility.triton_metadata_redirect import AOTMetadataContext
|
| 21 |
+
|
| 22 |
+
with AOTMetadataContext("kernel_name", "/custom/path"):
|
| 23 |
+
kernel = compile_kernel(...)
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
import functools
|
| 27 |
+
import os
|
| 28 |
+
from typing import Callable, Dict, Optional
|
| 29 |
+
import threading
|
| 30 |
+
import triton.compiler.compiler as triton_compiler
|
| 31 |
+
|
| 32 |
+
# Use thread-local storage to avoid multi-threading race conditions
|
| 33 |
+
_thread_local = threading.local()
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _get_thread_registry():
|
| 37 |
+
"""Get the registry for the current thread"""
|
| 38 |
+
if not hasattr(_thread_local, "replacement_registry"):
|
| 39 |
+
_thread_local.replacement_registry = {}
|
| 40 |
+
return _thread_local.replacement_registry
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
# Lock to ensure patching happens only once
|
| 44 |
+
_patch_lock = threading.Lock()
|
| 45 |
+
# Flag indicating whether patching has been performed
|
| 46 |
+
_patched = False
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _ensure_patched():
|
| 50 |
+
"""Ensure CompiledKernel.__init__ method is patched only once"""
|
| 51 |
+
global _patched
|
| 52 |
+
with _patch_lock:
|
| 53 |
+
if not _patched:
|
| 54 |
+
# Save the original __init__ method
|
| 55 |
+
_original_compiled_kernel_init = triton_compiler.CompiledKernel.__init__
|
| 56 |
+
|
| 57 |
+
def _replacement_init(self, src, metadata_group, hash):
|
| 58 |
+
# Find kernel name from metadata group
|
| 59 |
+
kernel_name = None
|
| 60 |
+
for key in metadata_group:
|
| 61 |
+
if key.endswith(".json"):
|
| 62 |
+
kernel_name = key[:-5] # Remove '.json' suffix
|
| 63 |
+
break
|
| 64 |
+
|
| 65 |
+
# Replace metadata paths using thread-local registry
|
| 66 |
+
if kernel_name:
|
| 67 |
+
registry = _get_thread_registry()
|
| 68 |
+
if kernel_name in registry:
|
| 69 |
+
dir = registry[kernel_name]
|
| 70 |
+
metadata_group[kernel_name + ".json"] = os.path.join(
|
| 71 |
+
dir, f"{kernel_name}.json"
|
| 72 |
+
)
|
| 73 |
+
metadata_group[kernel_name + ".hsaco"] = os.path.join(
|
| 74 |
+
dir, f"{kernel_name}.hsaco"
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
# Call the original initialization method
|
| 78 |
+
_original_compiled_kernel_init(self, src, metadata_group, hash)
|
| 79 |
+
|
| 80 |
+
# Replace the original __init__ method with our patched version
|
| 81 |
+
triton_compiler.CompiledKernel.__init__ = _replacement_init
|
| 82 |
+
_patched = True
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
# Ensure patching is done when module is loaded
|
| 86 |
+
_ensure_patched()
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class AOTMetadataContext:
|
| 90 |
+
"""
|
| 91 |
+
Context manager for AOT compilation with custom metadata paths
|
| 92 |
+
|
| 93 |
+
Uses thread-local storage to avoid multi-threading race conditions.
|
| 94 |
+
|
| 95 |
+
Example usage:
|
| 96 |
+
with AOTMetadataContext("kernel_name", "/custom/path"):
|
| 97 |
+
kernel = compile_kernel(...)
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
def __init__(self, kernel_name: str, dir: str):
|
| 101 |
+
self.kernel_name = kernel_name
|
| 102 |
+
self.dir = dir
|
| 103 |
+
self._previously_registered = False
|
| 104 |
+
self._previous_dir: Optional[str] = None
|
| 105 |
+
|
| 106 |
+
def __enter__(self):
|
| 107 |
+
registry = _get_thread_registry()
|
| 108 |
+
|
| 109 |
+
# Save previous registration if it exists
|
| 110 |
+
if self.kernel_name in registry:
|
| 111 |
+
self._previously_registered = True
|
| 112 |
+
self._previous_dir = registry[self.kernel_name]
|
| 113 |
+
|
| 114 |
+
# Register the new path
|
| 115 |
+
registry[self.kernel_name] = self.dir
|
| 116 |
+
|
| 117 |
+
return self
|
| 118 |
+
|
| 119 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 120 |
+
registry = _get_thread_registry()
|
| 121 |
+
|
| 122 |
+
# Restore previous registration or remove current one
|
| 123 |
+
if self._previously_registered:
|
| 124 |
+
registry[self.kernel_name] = self._previous_dir
|
| 125 |
+
else:
|
| 126 |
+
# Remove our registration if it wasn't previously registered
|
| 127 |
+
registry.pop(self.kernel_name, None)
|
| 128 |
+
|
| 129 |
+
# Don't suppress exceptions
|
| 130 |
+
return False
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def with_custom_metadata_path(dir):
|
| 134 |
+
"""
|
| 135 |
+
Decorator to register a kernel for metadata path replacement
|
| 136 |
+
|
| 137 |
+
This decorator uses thread-local storage to ensure consistency
|
| 138 |
+
with the context manager approach.
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
dir: Directory path where kernel metadata files should be stored
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
def decorator(func: Callable) -> Callable:
|
| 145 |
+
# Save the original function
|
| 146 |
+
original_func = func
|
| 147 |
+
|
| 148 |
+
# Create a wrapper function
|
| 149 |
+
# Currently does nothing but maintains signature
|
| 150 |
+
@functools.wraps(func)
|
| 151 |
+
def wrapper(*args, **kwargs):
|
| 152 |
+
return original_func(*args, **kwargs)
|
| 153 |
+
|
| 154 |
+
# Register the kernel in the thread-local registry
|
| 155 |
+
registry = _get_thread_registry()
|
| 156 |
+
registry[original_func.__name__] = dir
|
| 157 |
+
|
| 158 |
+
# Add decorator markers
|
| 159 |
+
wrapper._with_custom_metadata_path_applied = True
|
| 160 |
+
wrapper._metadata_directory = dir
|
| 161 |
+
|
| 162 |
+
# Return the original function to avoid interfering with @triton.jit
|
| 163 |
+
return original_func
|
| 164 |
+
|
| 165 |
+
return decorator
|
build/torch-rocm/_gluon_kernels/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2026, Advanced Micro Devices, Inc. All rights reserved.
|
build/torch-rocm/_gluon_kernels/gfx1250/fusions/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_gluon_kernels/gfx1250/fusions/fused_kv_cache.py
ADDED
|
@@ -0,0 +1,680 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
"""
|
| 4 |
+
Gluon (gfx1250) port of ``_fused_qk_rope_cat_and_cache_mla_kernel``.
|
| 5 |
+
|
| 6 |
+
This mirrors the Triton kernel in
|
| 7 |
+
``aiter/ops/triton/_triton_kernels/fusions/fused_kv_cache.py`` but is written in
|
| 8 |
+
Gluon for explicit control over layouts and load scheduling.
|
| 9 |
+
|
| 10 |
+
The contiguous input tiles (q_nope / q_pe / k_nope / k_pe) are streamed through
|
| 11 |
+
LDS with the gfx1250 TDM engine: ``tdm.async_load`` is issued as early as
|
| 12 |
+
possible (right after the per-program offsets are known, before the scalar
|
| 13 |
+
``pos`` load / cos-sin gather), and the shared->register ``load`` uses the exact
|
| 14 |
+
downstream distributed layout (``L_NOPE`` / ``L_PE``) so no ``convert_layout`` is
|
| 15 |
+
needed. ``tdm.async_wait`` drains the loads just before the values are consumed,
|
| 16 |
+
overlapping the global-memory latency with the index math and the cos/sin load.
|
| 17 |
+
|
| 18 |
+
``cos`` / ``sin`` stay on ``buffer_load``: with ``reuse_freqs_front_part`` they are
|
| 19 |
+
a gather (64 positions mapped onto 32 cached freqs), which the contiguous TDM
|
| 20 |
+
tile load cannot express.
|
| 21 |
+
|
| 22 |
+
The RoPE rotation (``_get_neox_rotated_x_1D`` / ``_get_gptj_rotated_x_1D``) and the
|
| 23 |
+
NVFP4 quantizer (``_nvfp4_quant_op``) are reused from the Triton ``@triton.jit``
|
| 24 |
+
helpers.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
from triton.experimental import gluon
|
| 28 |
+
from triton.experimental.gluon import language as gl
|
| 29 |
+
|
| 30 |
+
from ...._triton_kernels.rope.rope import (
|
| 31 |
+
_get_neox_rotated_x_1D,
|
| 32 |
+
_get_gptj_rotated_x_1D,
|
| 33 |
+
)
|
| 34 |
+
from ...._triton_kernels.quant.quant import _nvfp4_quant_op
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@gluon.constexpr_function
|
| 38 |
+
def _store_blocked_layout(R, C):
|
| 39 |
+
"""Pick a wave32 blocked layout for an (R, C) shuffled store tile.
|
| 40 |
+
|
| 41 |
+
Lanes are spread over the row dim first (one row per lane, contiguous
|
| 42 |
+
``C``-chunk per thread), matching the Triton-generated layouts:
|
| 43 |
+
(64, 8) -> [1,8]/[32,1] (8, 8) -> [1,2]/[8,4]
|
| 44 |
+
"""
|
| 45 |
+
lanes_row = min(R, 32)
|
| 46 |
+
lanes_col = 32 // lanes_row
|
| 47 |
+
spt_col = max(1, C // lanes_col)
|
| 48 |
+
return gl.BlockedLayout(
|
| 49 |
+
size_per_thread=[1, spt_col],
|
| 50 |
+
threads_per_warp=[lanes_row, lanes_col],
|
| 51 |
+
warps_per_cta=[1, 1],
|
| 52 |
+
order=[1, 0],
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
@gluon.jit
|
| 57 |
+
def _make_tdm_desc_1d(base_ptr, stride, N: gl.constexpr, layout: gl.constexpr):
|
| 58 |
+
"""Issue an async TDM load of a contiguous 1D tile (base already offset)."""
|
| 59 |
+
desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 60 |
+
base=base_ptr,
|
| 61 |
+
shape=[N],
|
| 62 |
+
strides=[stride],
|
| 63 |
+
block_shape=[N],
|
| 64 |
+
layout=layout,
|
| 65 |
+
)
|
| 66 |
+
return desc
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
@gluon.jit
|
| 70 |
+
def _issue_tdm_load_1d(desc, offset, smem):
|
| 71 |
+
"""Issue an async TDM load of a contiguous 1D tile (base already offset)."""
|
| 72 |
+
gl.amd.gfx1250.tdm.async_load(desc, [offset], smem)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
@gluon.jit
|
| 76 |
+
def _store_mla_kv_cache(
|
| 77 |
+
kv_cache_ptr,
|
| 78 |
+
pid_t_slot,
|
| 79 |
+
pid_hk,
|
| 80 |
+
pid_blk,
|
| 81 |
+
d_nope_offs,
|
| 82 |
+
d_pe_offs,
|
| 83 |
+
kv_cache_stride_b,
|
| 84 |
+
kv_cache_stride_h,
|
| 85 |
+
kv_cache_stride_d,
|
| 86 |
+
k_nope,
|
| 87 |
+
k_pe,
|
| 88 |
+
BLOCK_D_nope: gl.constexpr,
|
| 89 |
+
BLOCK_D_pe: gl.constexpr,
|
| 90 |
+
BLOCK_SIZE: gl.constexpr,
|
| 91 |
+
SHUFFLED_KV_CACHE: gl.constexpr,
|
| 92 |
+
SCALE_K_WIDTH_NOPE: gl.constexpr,
|
| 93 |
+
SCALE_K_WIDTH_ROPE: gl.constexpr,
|
| 94 |
+
L_NOPE: gl.constexpr,
|
| 95 |
+
L_PE: gl.constexpr,
|
| 96 |
+
):
|
| 97 |
+
if SHUFFLED_KV_CACHE:
|
| 98 |
+
if kv_cache_ptr.dtype.element_ty == gl.bfloat16:
|
| 99 |
+
# BF16
|
| 100 |
+
K_WIDTH: gl.constexpr = 8
|
| 101 |
+
else:
|
| 102 |
+
# FP8 E4M3 or packed FP4 E2M1
|
| 103 |
+
K_WIDTH: gl.constexpr = 16
|
| 104 |
+
|
| 105 |
+
if kv_cache_ptr.dtype.element_ty == gl.uint8:
|
| 106 |
+
NVFP4_QUANT_BLOCK_SIZE: gl.constexpr = 16
|
| 107 |
+
k_nope, k_nope_scales = _nvfp4_quant_op(
|
| 108 |
+
k_nope, BLOCK_D_nope, 1, NVFP4_QUANT_BLOCK_SIZE
|
| 109 |
+
)
|
| 110 |
+
k_pe, k_pe_scales = _nvfp4_quant_op(
|
| 111 |
+
k_pe, BLOCK_D_pe, 1, NVFP4_QUANT_BLOCK_SIZE
|
| 112 |
+
)
|
| 113 |
+
BLOCK_D_nope_STORE: gl.constexpr = BLOCK_D_nope // 2
|
| 114 |
+
BLOCK_D_pe_STORE: gl.constexpr = BLOCK_D_pe // 2
|
| 115 |
+
else:
|
| 116 |
+
BLOCK_D_nope_STORE: gl.constexpr = BLOCK_D_nope
|
| 117 |
+
BLOCK_D_pe_STORE: gl.constexpr = BLOCK_D_pe
|
| 118 |
+
|
| 119 |
+
R_nope: gl.constexpr = BLOCK_D_nope_STORE // K_WIDTH
|
| 120 |
+
R_pe: gl.constexpr = BLOCK_D_pe_STORE // K_WIDTH
|
| 121 |
+
PARENT_NOPE: gl.constexpr = _store_blocked_layout(R_nope, K_WIDTH)
|
| 122 |
+
PARENT_PE: gl.constexpr = _store_blocked_layout(R_pe, K_WIDTH)
|
| 123 |
+
|
| 124 |
+
d_nope_offs_shfl = gl.arange(0, R_nope, layout=gl.SliceLayout(1, PARENT_NOPE))
|
| 125 |
+
d_pe_offs_shfl = gl.arange(0, R_pe, layout=gl.SliceLayout(1, PARENT_PE))
|
| 126 |
+
k_width_shfl_nope = gl.arange(0, K_WIDTH, layout=gl.SliceLayout(0, PARENT_NOPE))
|
| 127 |
+
k_width_shfl_pe = gl.arange(0, K_WIDTH, layout=gl.SliceLayout(0, PARENT_PE))
|
| 128 |
+
|
| 129 |
+
k_nope = gl.convert_layout(gl.reshape(k_nope, [R_nope, K_WIDTH]), PARENT_NOPE)
|
| 130 |
+
k_pe = gl.convert_layout(gl.reshape(k_pe, [R_pe, K_WIDTH]), PARENT_PE)
|
| 131 |
+
|
| 132 |
+
kv_cache_base = (
|
| 133 |
+
kv_cache_ptr + pid_t_slot * kv_cache_stride_b + pid_hk * kv_cache_stride_h
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
kv_cache_nope_offs = (
|
| 137 |
+
(pid_blk // 16) * BLOCK_D_nope_STORE * 16
|
| 138 |
+
+ (pid_blk % 16) * K_WIDTH
|
| 139 |
+
+ d_nope_offs_shfl[:, None] * K_WIDTH * 16
|
| 140 |
+
+ k_width_shfl_nope[None, :]
|
| 141 |
+
) * kv_cache_stride_d
|
| 142 |
+
|
| 143 |
+
if kv_cache_ptr.dtype.element_ty == gl.uint8:
|
| 144 |
+
nope_scale_offset: gl.constexpr = BLOCK_D_nope // NVFP4_QUANT_BLOCK_SIZE
|
| 145 |
+
else:
|
| 146 |
+
nope_scale_offset: gl.constexpr = 0
|
| 147 |
+
kv_cache_pe_offs = (
|
| 148 |
+
BLOCK_SIZE * (BLOCK_D_nope_STORE + nope_scale_offset)
|
| 149 |
+
+ (pid_blk // 16) * BLOCK_D_pe_STORE * 16
|
| 150 |
+
+ (pid_blk % 16) * K_WIDTH
|
| 151 |
+
+ d_pe_offs_shfl[:, None] * K_WIDTH * 16
|
| 152 |
+
+ k_width_shfl_pe[None, :]
|
| 153 |
+
) * kv_cache_stride_d
|
| 154 |
+
|
| 155 |
+
gl.amd.cdna4.buffer_store(
|
| 156 |
+
k_nope.to(kv_cache_ptr.dtype.element_ty),
|
| 157 |
+
ptr=kv_cache_base,
|
| 158 |
+
offsets=kv_cache_nope_offs.to(gl.int32),
|
| 159 |
+
)
|
| 160 |
+
gl.amd.cdna4.buffer_store(
|
| 161 |
+
k_pe.to(kv_cache_ptr.dtype.element_ty),
|
| 162 |
+
ptr=kv_cache_base,
|
| 163 |
+
offsets=kv_cache_pe_offs.to(gl.int32),
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
if kv_cache_ptr.dtype.element_ty == gl.uint8:
|
| 167 |
+
BLOCK_D_nope_scales: gl.constexpr = BLOCK_D_nope // NVFP4_QUANT_BLOCK_SIZE
|
| 168 |
+
BLOCK_D_pe_scales: gl.constexpr = BLOCK_D_pe // NVFP4_QUANT_BLOCK_SIZE
|
| 169 |
+
R_ns: gl.constexpr = BLOCK_D_nope_scales // SCALE_K_WIDTH_NOPE
|
| 170 |
+
R_ps: gl.constexpr = BLOCK_D_pe_scales // SCALE_K_WIDTH_ROPE
|
| 171 |
+
PARENT_NS: gl.constexpr = _store_blocked_layout(R_ns, SCALE_K_WIDTH_NOPE)
|
| 172 |
+
PARENT_PS: gl.constexpr = _store_blocked_layout(R_ps, SCALE_K_WIDTH_ROPE)
|
| 173 |
+
|
| 174 |
+
d_nope_scales_shfl = gl.arange(0, R_ns, layout=gl.SliceLayout(1, PARENT_NS))
|
| 175 |
+
d_pe_scales_shfl = gl.arange(0, R_ps, layout=gl.SliceLayout(1, PARENT_PS))
|
| 176 |
+
k_nope_width_shfl = gl.arange(
|
| 177 |
+
0, SCALE_K_WIDTH_NOPE, layout=gl.SliceLayout(0, PARENT_NS)
|
| 178 |
+
)
|
| 179 |
+
k_pe_width_shfl = gl.arange(
|
| 180 |
+
0, SCALE_K_WIDTH_ROPE, layout=gl.SliceLayout(0, PARENT_PS)
|
| 181 |
+
)
|
| 182 |
+
k_nope_scales = gl.convert_layout(
|
| 183 |
+
gl.reshape(k_nope_scales, [R_ns, SCALE_K_WIDTH_NOPE]), PARENT_NS
|
| 184 |
+
)
|
| 185 |
+
k_pe_scales = gl.convert_layout(
|
| 186 |
+
gl.reshape(k_pe_scales, [R_ps, SCALE_K_WIDTH_ROPE]), PARENT_PS
|
| 187 |
+
)
|
| 188 |
+
pid_sub_blk = pid_blk % 128
|
| 189 |
+
kv_cache_nope_scales_offs = (
|
| 190 |
+
BLOCK_SIZE * BLOCK_D_nope_STORE
|
| 191 |
+
+ (pid_blk // 128) * BLOCK_D_nope_scales * 128
|
| 192 |
+
+ d_nope_scales_shfl[:, None] * SCALE_K_WIDTH_NOPE * 128
|
| 193 |
+
+ (pid_sub_blk % 32) * 4 * SCALE_K_WIDTH_NOPE
|
| 194 |
+
+ (pid_sub_blk // 32) * SCALE_K_WIDTH_NOPE
|
| 195 |
+
+ k_nope_width_shfl[None, :]
|
| 196 |
+
) * kv_cache_stride_d
|
| 197 |
+
kv_cache_pe_scales_offs = (
|
| 198 |
+
BLOCK_SIZE
|
| 199 |
+
* (BLOCK_D_nope_STORE + BLOCK_D_nope_scales + BLOCK_D_pe_STORE)
|
| 200 |
+
+ (pid_blk // 128) * BLOCK_D_pe_scales * 128
|
| 201 |
+
+ d_pe_scales_shfl[:, None] * SCALE_K_WIDTH_ROPE * 128
|
| 202 |
+
+ (pid_sub_blk % 32) * 4 * SCALE_K_WIDTH_ROPE
|
| 203 |
+
+ (pid_sub_blk // 32) * SCALE_K_WIDTH_ROPE
|
| 204 |
+
+ k_pe_width_shfl[None, :]
|
| 205 |
+
) * kv_cache_stride_d
|
| 206 |
+
e4m3_dtype: gl.constexpr = gl.float8e4nv
|
| 207 |
+
gl.amd.cdna4.buffer_store(
|
| 208 |
+
k_nope_scales.to(e4m3_dtype).to(
|
| 209 |
+
kv_cache_ptr.dtype.element_ty, bitcast=True
|
| 210 |
+
),
|
| 211 |
+
ptr=kv_cache_base,
|
| 212 |
+
offsets=kv_cache_nope_scales_offs.to(gl.int32),
|
| 213 |
+
)
|
| 214 |
+
gl.amd.cdna4.buffer_store(
|
| 215 |
+
k_pe_scales.to(e4m3_dtype).to(
|
| 216 |
+
kv_cache_ptr.dtype.element_ty, bitcast=True
|
| 217 |
+
),
|
| 218 |
+
ptr=kv_cache_base,
|
| 219 |
+
offsets=kv_cache_pe_scales_offs.to(gl.int32),
|
| 220 |
+
)
|
| 221 |
+
else:
|
| 222 |
+
# non-shuffled KV cache
|
| 223 |
+
kv_cache_base = (
|
| 224 |
+
kv_cache_ptr + pid_t_slot * kv_cache_stride_b + pid_hk * kv_cache_stride_h
|
| 225 |
+
)
|
| 226 |
+
kv_cache_nope_offs = d_nope_offs * kv_cache_stride_d
|
| 227 |
+
kv_cache_pe_offs = (d_pe_offs + BLOCK_D_nope) * kv_cache_stride_d
|
| 228 |
+
gl.amd.cdna4.buffer_store(
|
| 229 |
+
k_nope.to(kv_cache_ptr.dtype.element_ty),
|
| 230 |
+
ptr=kv_cache_base,
|
| 231 |
+
offsets=kv_cache_nope_offs.to(gl.int32),
|
| 232 |
+
)
|
| 233 |
+
gl.amd.cdna4.buffer_store(
|
| 234 |
+
k_pe.to(kv_cache_ptr.dtype.element_ty),
|
| 235 |
+
ptr=kv_cache_base,
|
| 236 |
+
offsets=kv_cache_pe_offs.to(gl.int32),
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
# Note: the async_store drain (tdm.async_wait) is done by the CALLER after
|
| 240 |
+
# any downstream ops, so the async_store latency can overlap with the
|
| 241 |
+
# post-helper work (decode_q_pe / zeros buffer_stores, etc.) instead of
|
| 242 |
+
# being exposed right at the helper return.
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
@gluon.jit
|
| 246 |
+
def _freq_from_shared(
|
| 247 |
+
smem,
|
| 248 |
+
REUSE_FREQS_FRONT_PART: gl.constexpr,
|
| 249 |
+
IS_NEOX: gl.constexpr,
|
| 250 |
+
BLOCK_D_pe: gl.constexpr,
|
| 251 |
+
L_PE: gl.constexpr,
|
| 252 |
+
L_FREQ: gl.constexpr,
|
| 253 |
+
):
|
| 254 |
+
"""Rebuild the BLOCK_D_pe cos/sin vector from a contiguous freq slice in LDS.
|
| 255 |
+
|
| 256 |
+
The cached freq buffer is gathered by ``d_cos_offs`` in the Triton kernel; here
|
| 257 |
+
we TDM-load the contiguous slice and rebuild the gather in registers:
|
| 258 |
+
|
| 259 |
+
* REUSE & NEOX -> concat(f, f) = reshape(trans(join(f, f)))
|
| 260 |
+
* REUSE & GPTJ -> interleave(f, f) = reshape(join(f, f))
|
| 261 |
+
* not REUSE -> already the full BLOCK_D_pe vector
|
| 262 |
+
"""
|
| 263 |
+
if REUSE_FREQS_FRONT_PART:
|
| 264 |
+
f = smem.load(L_FREQ)
|
| 265 |
+
j = gl.join(f, f)
|
| 266 |
+
if IS_NEOX:
|
| 267 |
+
out = gl.reshape(gl.permute(j, [1, 0]), [BLOCK_D_pe])
|
| 268 |
+
else:
|
| 269 |
+
out = gl.reshape(j, [BLOCK_D_pe])
|
| 270 |
+
return gl.convert_layout(out, L_PE)
|
| 271 |
+
else:
|
| 272 |
+
return smem.load(L_PE)
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
@gluon.jit
|
| 276 |
+
def _rope_pe(
|
| 277 |
+
x_pe,
|
| 278 |
+
cos,
|
| 279 |
+
sin,
|
| 280 |
+
d_pe_offs,
|
| 281 |
+
IS_NEOX: gl.constexpr,
|
| 282 |
+
BLOCK_D_pe: gl.constexpr,
|
| 283 |
+
BLOCK_D_HALF_pe: gl.constexpr,
|
| 284 |
+
):
|
| 285 |
+
"""RoPE on an already-loaded 1D pe vector. Reuses the Triton rotation helper."""
|
| 286 |
+
if IS_NEOX:
|
| 287 |
+
x_rotated_mask = d_pe_offs < BLOCK_D_HALF_pe
|
| 288 |
+
x_pe_rotated = _get_neox_rotated_x_1D(
|
| 289 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 290 |
+
)
|
| 291 |
+
else:
|
| 292 |
+
x_rotated_mask = d_pe_offs % 2 == 0
|
| 293 |
+
x_pe_rotated = _get_gptj_rotated_x_1D(
|
| 294 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 295 |
+
)
|
| 296 |
+
return x_pe * cos + x_pe_rotated * sin
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
@gluon.jit
|
| 300 |
+
def _fused_qk_rope_cat_and_cache_mla_kernel(
|
| 301 |
+
q_nope_ptr,
|
| 302 |
+
q_pe_ptr,
|
| 303 |
+
k_nope_ptr,
|
| 304 |
+
k_pe_ptr,
|
| 305 |
+
pos_ptr,
|
| 306 |
+
cos_ptr,
|
| 307 |
+
sin_ptr,
|
| 308 |
+
q_out_ptr,
|
| 309 |
+
decode_q_pe_out_ptr,
|
| 310 |
+
k_pe_out_ptr,
|
| 311 |
+
q_nope_zeros_out_ptr,
|
| 312 |
+
kv_cache_ptr,
|
| 313 |
+
slot_mapping_ptr,
|
| 314 |
+
B,
|
| 315 |
+
B_slot,
|
| 316 |
+
num_decode_toks_for_zeros,
|
| 317 |
+
q_nope_stride_b,
|
| 318 |
+
q_nope_stride_h,
|
| 319 |
+
q_nope_stride_d,
|
| 320 |
+
q_pe_stride_b,
|
| 321 |
+
q_pe_stride_h,
|
| 322 |
+
q_pe_stride_d,
|
| 323 |
+
k_nope_stride_b,
|
| 324 |
+
k_nope_stride_h,
|
| 325 |
+
k_nope_stride_d,
|
| 326 |
+
k_pe_stride_b,
|
| 327 |
+
k_pe_stride_h,
|
| 328 |
+
k_pe_stride_d,
|
| 329 |
+
pos_stride_b,
|
| 330 |
+
cos_stride_b,
|
| 331 |
+
cos_stride_d,
|
| 332 |
+
q_out_stride_b,
|
| 333 |
+
q_out_stride_h,
|
| 334 |
+
q_out_stride_d,
|
| 335 |
+
decode_q_pe_out_stride_b,
|
| 336 |
+
decode_q_pe_out_stride_h,
|
| 337 |
+
decode_q_pe_out_stride_d,
|
| 338 |
+
k_pe_out_stride_b,
|
| 339 |
+
k_pe_out_stride_h,
|
| 340 |
+
k_pe_out_stride_d,
|
| 341 |
+
q_nope_zeros_out_stride_b,
|
| 342 |
+
q_nope_zeros_out_stride_h,
|
| 343 |
+
q_nope_zeros_out_stride_d,
|
| 344 |
+
kv_cache_stride_b,
|
| 345 |
+
kv_cache_stride_h,
|
| 346 |
+
kv_cache_stride_d,
|
| 347 |
+
k_scale_ptr,
|
| 348 |
+
QH_PER_KH: gl.constexpr,
|
| 349 |
+
QH: gl.constexpr,
|
| 350 |
+
KH: gl.constexpr,
|
| 351 |
+
REUSE_FREQS_FRONT_PART: gl.constexpr,
|
| 352 |
+
IS_NEOX: gl.constexpr,
|
| 353 |
+
BLOCK_D_nope: gl.constexpr,
|
| 354 |
+
BLOCK_D_pe: gl.constexpr,
|
| 355 |
+
BLOCK_D_HALF_pe: gl.constexpr,
|
| 356 |
+
BLOCK_SIZE: gl.constexpr = 1,
|
| 357 |
+
SHUFFLED_KV_CACHE: gl.constexpr = False,
|
| 358 |
+
SCALE_K_WIDTH_NOPE: gl.constexpr = 4,
|
| 359 |
+
SCALE_K_WIDTH_ROPE: gl.constexpr = 4,
|
| 360 |
+
OUTPUT_Q_NOPE_ZEROS_AND_Q_PE: gl.constexpr = False,
|
| 361 |
+
HAVE_K_SCALE: gl.constexpr = False,
|
| 362 |
+
UPCAST_OPERAND: gl.constexpr = False,
|
| 363 |
+
):
|
| 364 |
+
# 1-warp (wave32) blocked layouts matching the Triton-generated ttgir.
|
| 365 |
+
L_NOPE: gl.constexpr = gl.BlockedLayout(
|
| 366 |
+
size_per_thread=[8], threads_per_warp=[32], warps_per_cta=[1], order=[0]
|
| 367 |
+
)
|
| 368 |
+
L_PE: gl.constexpr = gl.BlockedLayout(
|
| 369 |
+
size_per_thread=[2], threads_per_warp=[32], warps_per_cta=[1], order=[0]
|
| 370 |
+
)
|
| 371 |
+
# Identity (un-swizzled) shared layout for the 1D TDM staging buffers.
|
| 372 |
+
SH: gl.constexpr = gl.SwizzledSharedLayout(1, 1, 1, order=[0])
|
| 373 |
+
|
| 374 |
+
# cos/sin: TDM-load the contiguous freq slice, then rebuild in registers.
|
| 375 |
+
FREQ_W: gl.constexpr = BLOCK_D_HALF_pe if REUSE_FREQS_FRONT_PART else BLOCK_D_pe
|
| 376 |
+
FREQ_SPT: gl.constexpr = BLOCK_D_HALF_pe // 32 if BLOCK_D_HALF_pe >= 32 else 1
|
| 377 |
+
L_FREQ: gl.constexpr = gl.BlockedLayout(
|
| 378 |
+
size_per_thread=[FREQ_SPT], threads_per_warp=[32], warps_per_cta=[1], order=[0]
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
pid = gl.program_id(0)
|
| 382 |
+
|
| 383 |
+
d_nope_offs = gl.arange(0, BLOCK_D_nope, layout=L_NOPE).to(gl.int64)
|
| 384 |
+
d_pe_offs = gl.arange(0, BLOCK_D_pe, layout=L_PE).to(gl.int64)
|
| 385 |
+
|
| 386 |
+
# When q_out has the same dtype as q_nope/q_pe we can stage the passthrough
|
| 387 |
+
# q_nope straight from its load buffer (no cast). When it differs we need
|
| 388 |
+
# separate q_out-dtype staging buffers and an explicit cast on store.
|
| 389 |
+
Q_OUT_MATCHES: gl.constexpr = (
|
| 390 |
+
q_out_ptr.dtype.element_ty == q_nope_ptr.dtype.element_ty
|
| 391 |
+
)
|
| 392 |
+
|
| 393 |
+
# Shared staging buffers (static allocation; only a subset is used per pid).
|
| 394 |
+
qn_smem = gl.allocate_shared_memory(q_nope_ptr.dtype.element_ty, [BLOCK_D_nope], SH)
|
| 395 |
+
qpe_smem = gl.allocate_shared_memory(q_pe_ptr.dtype.element_ty, [BLOCK_D_pe], SH)
|
| 396 |
+
kn_smem = gl.allocate_shared_memory(k_nope_ptr.dtype.element_ty, [BLOCK_D_nope], SH)
|
| 397 |
+
kpe_smem = gl.allocate_shared_memory(k_pe_ptr.dtype.element_ty, [BLOCK_D_pe], SH)
|
| 398 |
+
cos_smem = gl.allocate_shared_memory(cos_ptr.dtype.element_ty, [FREQ_W], SH)
|
| 399 |
+
sin_smem = gl.allocate_shared_memory(sin_ptr.dtype.element_ty, [FREQ_W], SH)
|
| 400 |
+
if not Q_OUT_MATCHES:
|
| 401 |
+
# q_out-dtype staging buffers for the cast path.
|
| 402 |
+
qn_smem_out = gl.allocate_shared_memory(
|
| 403 |
+
q_out_ptr.dtype.element_ty, [BLOCK_D_nope], SH
|
| 404 |
+
)
|
| 405 |
+
qpe_smem_out = gl.allocate_shared_memory(
|
| 406 |
+
q_out_ptr.dtype.element_ty, [BLOCK_D_pe], SH
|
| 407 |
+
)
|
| 408 |
+
|
| 409 |
+
if pid < B * QH:
|
| 410 |
+
# pid_b = pid // QH
|
| 411 |
+
# pid_hq = pid % QH
|
| 412 |
+
# This is a new optimization that prioritized heavy workload WGs first
|
| 413 |
+
pid_hq = pid // B
|
| 414 |
+
pid_b = pid % B
|
| 415 |
+
|
| 416 |
+
# Issue ``pos`` first — it's used immediately by the cos/sin TDM
|
| 417 |
+
# descriptors. pid_slot / k_scale are only consumed later in the
|
| 418 |
+
# k-store path, so they sit behind pos in the issue stream.
|
| 419 |
+
pos = gl.load(pos_ptr + pid_b * pos_stride_b)
|
| 420 |
+
pid_slot = gl.load(slot_mapping_ptr + pid_b).to(gl.int64)
|
| 421 |
+
|
| 422 |
+
q_nope_desc = _make_tdm_desc_1d(
|
| 423 |
+
q_nope_ptr + pid_b * q_nope_stride_b + pid_hq * q_nope_stride_h,
|
| 424 |
+
q_nope_stride_d,
|
| 425 |
+
BLOCK_D_nope,
|
| 426 |
+
SH,
|
| 427 |
+
)
|
| 428 |
+
_issue_tdm_load_1d(q_nope_desc, 0, qn_smem)
|
| 429 |
+
if HAVE_K_SCALE:
|
| 430 |
+
k_scale = gl.load(k_scale_ptr)
|
| 431 |
+
else:
|
| 432 |
+
k_scale = 1.0
|
| 433 |
+
|
| 434 |
+
# cos/sin: TDM-load the contiguous freq slice (base depends on pos),
|
| 435 |
+
# rebuilt into the BLOCK_D_pe vector after the wait. The slice is
|
| 436 |
+
# contiguous (no d_cos_offs gather), so it streams through LDS like the
|
| 437 |
+
# other inputs. Empirically faster than the buffer_load gather despite
|
| 438 |
+
# adding 2 to the TDM-load FIFO depth (the [FIFO full] stall on the
|
| 439 |
+
# 6th issue is an overlap stall — kernel keeps doing useful work).
|
| 440 |
+
cos_desc = _make_tdm_desc_1d(
|
| 441 |
+
cos_ptr + pos * cos_stride_b, cos_stride_d, FREQ_W, SH
|
| 442 |
+
)
|
| 443 |
+
sin_desc = _make_tdm_desc_1d(
|
| 444 |
+
sin_ptr + pos * cos_stride_b, cos_stride_d, FREQ_W, SH
|
| 445 |
+
)
|
| 446 |
+
_issue_tdm_load_1d(cos_desc, 0, cos_smem)
|
| 447 |
+
_issue_tdm_load_1d(sin_desc, 0, sin_smem)
|
| 448 |
+
|
| 449 |
+
# --- Issue all TDM loads as early as possible ---
|
| 450 |
+
q_pe_desc = _make_tdm_desc_1d(
|
| 451 |
+
q_pe_ptr + pid_b * q_pe_stride_b + pid_hq * q_pe_stride_h,
|
| 452 |
+
q_pe_stride_d,
|
| 453 |
+
BLOCK_D_pe,
|
| 454 |
+
SH,
|
| 455 |
+
)
|
| 456 |
+
_issue_tdm_load_1d(q_pe_desc, 0, qpe_smem)
|
| 457 |
+
|
| 458 |
+
# pid_hk = pid_hq // QH_PER_KH
|
| 459 |
+
# is_kv = pid_hq % QH_PER_KH == 0
|
| 460 |
+
# This is a new optimization that prioritized heavy workload WGs first
|
| 461 |
+
pid_hk = pid_hq
|
| 462 |
+
is_kv = pid_hk < KH
|
| 463 |
+
|
| 464 |
+
q_out_base = pid_b * q_out_stride_b + pid_hq * q_out_stride_h
|
| 465 |
+
|
| 466 |
+
if is_kv:
|
| 467 |
+
k_nope_desc = _make_tdm_desc_1d(
|
| 468 |
+
k_nope_ptr + pid_b * k_nope_stride_b + pid_hk * k_nope_stride_h,
|
| 469 |
+
k_nope_stride_d,
|
| 470 |
+
BLOCK_D_nope,
|
| 471 |
+
SH,
|
| 472 |
+
)
|
| 473 |
+
_issue_tdm_load_1d(k_nope_desc, 0, kn_smem)
|
| 474 |
+
k_pe_desc = _make_tdm_desc_1d(
|
| 475 |
+
k_pe_ptr + pid_b * k_pe_stride_b + pid_hk * k_pe_stride_h,
|
| 476 |
+
k_pe_stride_d,
|
| 477 |
+
BLOCK_D_pe,
|
| 478 |
+
SH,
|
| 479 |
+
)
|
| 480 |
+
_issue_tdm_load_1d(k_pe_desc, 0, kpe_smem)
|
| 481 |
+
|
| 482 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 483 |
+
# Rebuild the BLOCK_D_pe cos/sin from the contiguous freq slice in LDS.
|
| 484 |
+
cos = _freq_from_shared(
|
| 485 |
+
cos_smem, REUSE_FREQS_FRONT_PART, IS_NEOX, BLOCK_D_pe, L_PE, L_FREQ
|
| 486 |
+
)
|
| 487 |
+
sin = _freq_from_shared(
|
| 488 |
+
sin_smem, REUSE_FREQS_FRONT_PART, IS_NEOX, BLOCK_D_pe, L_PE, L_FREQ
|
| 489 |
+
)
|
| 490 |
+
if UPCAST_OPERAND:
|
| 491 |
+
cos = cos.to(gl.float32)
|
| 492 |
+
sin = sin.to(gl.float32)
|
| 493 |
+
|
| 494 |
+
q_pe_in = qpe_smem.load(L_PE)
|
| 495 |
+
q_pe = _rope_pe(
|
| 496 |
+
q_pe_in, cos, sin, d_pe_offs, IS_NEOX, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
q_out_nope_desc = _make_tdm_desc_1d(
|
| 500 |
+
q_out_ptr + q_out_base,
|
| 501 |
+
q_out_stride_d,
|
| 502 |
+
BLOCK_D_nope,
|
| 503 |
+
SH,
|
| 504 |
+
)
|
| 505 |
+
q_out_pe_desc = _make_tdm_desc_1d(
|
| 506 |
+
q_out_ptr + q_out_base + BLOCK_D_nope * q_out_stride_d,
|
| 507 |
+
q_out_stride_d,
|
| 508 |
+
BLOCK_D_pe,
|
| 509 |
+
SH,
|
| 510 |
+
)
|
| 511 |
+
if Q_OUT_MATCHES:
|
| 512 |
+
# Same dtype: qn_smem already holds the bit-identical q_nope from the
|
| 513 |
+
# async_load, so TDM-store directly (skip the LDS round-trip).
|
| 514 |
+
qpe_smem.store(q_pe.to(q_out_ptr.dtype.element_ty))
|
| 515 |
+
gl.amd.gfx1250.tdm.async_store(q_out_nope_desc, [0], qn_smem)
|
| 516 |
+
gl.amd.gfx1250.tdm.async_store(q_out_pe_desc, [0], qpe_smem)
|
| 517 |
+
else:
|
| 518 |
+
# Differing dtype: load q_nope to registers, cast to the q_out dtype
|
| 519 |
+
# and stage into the q_out-dtype buffers before the TDM-store.
|
| 520 |
+
q_nope = qn_smem.load(L_NOPE)
|
| 521 |
+
qn_smem_out.store(q_nope.to(q_out_ptr.dtype.element_ty))
|
| 522 |
+
qpe_smem_out.store(q_pe.to(q_out_ptr.dtype.element_ty))
|
| 523 |
+
gl.amd.gfx1250.tdm.async_store(q_out_nope_desc, [0], qn_smem_out)
|
| 524 |
+
gl.amd.gfx1250.tdm.async_store(q_out_pe_desc, [0], qpe_smem_out)
|
| 525 |
+
|
| 526 |
+
if is_kv:
|
| 527 |
+
if pid_slot >= 0:
|
| 528 |
+
if BLOCK_SIZE > 1:
|
| 529 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 530 |
+
pid_blk = pid_slot % BLOCK_SIZE
|
| 531 |
+
else:
|
| 532 |
+
pid_t_slot = pid_slot
|
| 533 |
+
pid_blk = 0
|
| 534 |
+
|
| 535 |
+
k_nope = kn_smem.load(L_NOPE)
|
| 536 |
+
k_pe_in = kpe_smem.load(L_PE)
|
| 537 |
+
k_pe = _rope_pe(
|
| 538 |
+
k_pe_in, cos, sin, d_pe_offs, IS_NEOX, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 539 |
+
)
|
| 540 |
+
k_pe_out_base = pid_b * k_pe_out_stride_b + pid_hk * k_pe_out_stride_h
|
| 541 |
+
gl.amd.cdna4.buffer_store(
|
| 542 |
+
k_pe.to(k_pe_out_ptr.dtype.element_ty),
|
| 543 |
+
ptr=k_pe_out_ptr,
|
| 544 |
+
offsets=(k_pe_out_base + d_pe_offs * k_pe_out_stride_d).to(
|
| 545 |
+
gl.int32
|
| 546 |
+
),
|
| 547 |
+
)
|
| 548 |
+
k_scale_rcprl = (1 / k_scale).to(gl.float32)
|
| 549 |
+
k_nope = k_nope.to(gl.float32) * k_scale_rcprl
|
| 550 |
+
k_pe = k_pe.to(gl.float32) * k_scale_rcprl
|
| 551 |
+
|
| 552 |
+
_store_mla_kv_cache(
|
| 553 |
+
kv_cache_ptr,
|
| 554 |
+
pid_t_slot,
|
| 555 |
+
pid_hk,
|
| 556 |
+
pid_blk,
|
| 557 |
+
d_nope_offs,
|
| 558 |
+
d_pe_offs,
|
| 559 |
+
kv_cache_stride_b,
|
| 560 |
+
kv_cache_stride_h,
|
| 561 |
+
kv_cache_stride_d,
|
| 562 |
+
k_nope,
|
| 563 |
+
k_pe,
|
| 564 |
+
BLOCK_D_nope,
|
| 565 |
+
BLOCK_D_pe,
|
| 566 |
+
BLOCK_SIZE,
|
| 567 |
+
SHUFFLED_KV_CACHE,
|
| 568 |
+
SCALE_K_WIDTH_NOPE,
|
| 569 |
+
SCALE_K_WIDTH_ROPE,
|
| 570 |
+
L_NOPE,
|
| 571 |
+
L_PE,
|
| 572 |
+
)
|
| 573 |
+
|
| 574 |
+
# OUTPUT block at tail (after the kv-store path): both stores via
|
| 575 |
+
# buffer_store. Empirically beats moving the block earlier or putting
|
| 576 |
+
# decode_q_pe on TDM async_store — those alternatives lower per-WGP
|
| 577 |
+
# SIMD-instruction count but degrade IPC enough that wall-clock
|
| 578 |
+
# dispatch time grows.
|
| 579 |
+
if OUTPUT_Q_NOPE_ZEROS_AND_Q_PE:
|
| 580 |
+
if pid < num_decode_toks_for_zeros * QH:
|
| 581 |
+
decode_q_pe_base = (
|
| 582 |
+
pid_b * decode_q_pe_out_stride_b + pid_hq * decode_q_pe_out_stride_h
|
| 583 |
+
)
|
| 584 |
+
gl.amd.cdna4.buffer_store(
|
| 585 |
+
q_pe.to(decode_q_pe_out_ptr.dtype.element_ty),
|
| 586 |
+
ptr=decode_q_pe_out_ptr,
|
| 587 |
+
offsets=(
|
| 588 |
+
decode_q_pe_base + d_pe_offs * decode_q_pe_out_stride_d
|
| 589 |
+
).to(gl.int32),
|
| 590 |
+
)
|
| 591 |
+
z = gl.zeros(
|
| 592 |
+
[BLOCK_D_nope],
|
| 593 |
+
dtype=q_nope_zeros_out_ptr.dtype.element_ty,
|
| 594 |
+
layout=L_NOPE,
|
| 595 |
+
)
|
| 596 |
+
zeros_base = (
|
| 597 |
+
pid_b * q_nope_zeros_out_stride_b
|
| 598 |
+
+ pid_hq * q_nope_zeros_out_stride_h
|
| 599 |
+
)
|
| 600 |
+
gl.amd.cdna4.buffer_store(
|
| 601 |
+
z,
|
| 602 |
+
ptr=q_nope_zeros_out_ptr,
|
| 603 |
+
offsets=(zeros_base + d_nope_offs * q_nope_zeros_out_stride_d).to(
|
| 604 |
+
gl.int32
|
| 605 |
+
),
|
| 606 |
+
)
|
| 607 |
+
|
| 608 |
+
# Drain the in-flight q_out async_stores.
|
| 609 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 610 |
+
else:
|
| 611 |
+
pid = pid - B * QH + B * KH
|
| 612 |
+
if pid < B_slot * KH:
|
| 613 |
+
pid_b = pid // KH
|
| 614 |
+
pid_hk = pid % KH
|
| 615 |
+
|
| 616 |
+
k_nope_desc = _make_tdm_desc_1d(
|
| 617 |
+
k_nope_ptr + pid_b * k_nope_stride_b + pid_hk * k_nope_stride_h,
|
| 618 |
+
k_nope_stride_d,
|
| 619 |
+
BLOCK_D_nope,
|
| 620 |
+
SH,
|
| 621 |
+
)
|
| 622 |
+
_issue_tdm_load_1d(k_nope_desc, 0, kn_smem)
|
| 623 |
+
k_pe_desc = _make_tdm_desc_1d(
|
| 624 |
+
k_pe_ptr + pid_b * k_pe_stride_b + pid_hk * k_pe_stride_h,
|
| 625 |
+
k_pe_stride_d,
|
| 626 |
+
BLOCK_D_pe,
|
| 627 |
+
SH,
|
| 628 |
+
)
|
| 629 |
+
_issue_tdm_load_1d(k_pe_desc, 0, kpe_smem)
|
| 630 |
+
|
| 631 |
+
pid_slot = gl.load(slot_mapping_ptr + pid_b).to(gl.int64)
|
| 632 |
+
if pid_slot >= 0:
|
| 633 |
+
if BLOCK_SIZE > 1:
|
| 634 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 635 |
+
pid_blk = pid_slot % BLOCK_SIZE
|
| 636 |
+
else:
|
| 637 |
+
pid_t_slot = pid_slot
|
| 638 |
+
pid_blk = 0
|
| 639 |
+
if HAVE_K_SCALE:
|
| 640 |
+
k_scale = gl.load(k_scale_ptr)
|
| 641 |
+
else:
|
| 642 |
+
k_scale = 1.0
|
| 643 |
+
|
| 644 |
+
k_pe_out_base = pid_b * k_pe_out_stride_b + pid_hk * k_pe_out_stride_h
|
| 645 |
+
|
| 646 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 647 |
+
k_nope = kn_smem.load(L_NOPE)
|
| 648 |
+
k_pe = kpe_smem.load(L_PE)
|
| 649 |
+
gl.amd.cdna4.buffer_store(
|
| 650 |
+
k_pe.to(k_pe_out_ptr.dtype.element_ty),
|
| 651 |
+
ptr=k_pe_out_ptr,
|
| 652 |
+
offsets=(k_pe_out_base + d_pe_offs * k_pe_out_stride_d).to(
|
| 653 |
+
gl.int32
|
| 654 |
+
),
|
| 655 |
+
)
|
| 656 |
+
k_scale_rcprl = (1 / k_scale).to(gl.float32)
|
| 657 |
+
k_nope = k_nope.to(gl.float32) * k_scale_rcprl
|
| 658 |
+
k_pe = k_pe.to(gl.float32) * k_scale_rcprl
|
| 659 |
+
|
| 660 |
+
_store_mla_kv_cache(
|
| 661 |
+
kv_cache_ptr,
|
| 662 |
+
pid_t_slot,
|
| 663 |
+
pid_hk,
|
| 664 |
+
pid_blk,
|
| 665 |
+
d_nope_offs,
|
| 666 |
+
d_pe_offs,
|
| 667 |
+
kv_cache_stride_b,
|
| 668 |
+
kv_cache_stride_h,
|
| 669 |
+
kv_cache_stride_d,
|
| 670 |
+
k_nope,
|
| 671 |
+
k_pe,
|
| 672 |
+
BLOCK_D_nope,
|
| 673 |
+
BLOCK_D_pe,
|
| 674 |
+
BLOCK_SIZE,
|
| 675 |
+
SHUFFLED_KV_CACHE,
|
| 676 |
+
SCALE_K_WIDTH_NOPE,
|
| 677 |
+
SCALE_K_WIDTH_ROPE,
|
| 678 |
+
L_NOPE,
|
| 679 |
+
L_PE,
|
| 680 |
+
)
|
build/torch-rocm/_gluon_kernels/gfx1250/gemm/basic/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_gluon_kernels/gfx1250/gemm/basic/gemm_a16w16.py
ADDED
|
@@ -0,0 +1,703 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
import math
|
| 5 |
+
from triton.experimental import gluon
|
| 6 |
+
import triton.experimental.gluon.language as gl
|
| 7 |
+
from .....utils._triton.kernel_repr import make_kernel_repr
|
| 8 |
+
|
| 9 |
+
_GLUON_REPR_KEYS = [
|
| 10 |
+
"BLOCK_M",
|
| 11 |
+
"BLOCK_N",
|
| 12 |
+
"BLOCK_K",
|
| 13 |
+
"NUM_BUFFERS",
|
| 14 |
+
"LAYOUT",
|
| 15 |
+
"USE_ACTIVATION",
|
| 16 |
+
"ADD_BIAS",
|
| 17 |
+
]
|
| 18 |
+
|
| 19 |
+
_gemm_a16w16_bandwidth_bound_repr = make_kernel_repr(
|
| 20 |
+
"_gemm_a16w16_gfx1250_bandwidth_bound_kernel", _GLUON_REPR_KEYS
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
_gemm_a16w16_compute_bound_repr = make_kernel_repr(
|
| 24 |
+
"_gemm_a16w16_gfx1250_compute_bound_kernel", _GLUON_REPR_KEYS
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def create_shared_layouts(
|
| 29 |
+
BLOCK_M: gl.constexpr,
|
| 30 |
+
BLOCK_N: gl.constexpr,
|
| 31 |
+
BLOCK_K: gl.constexpr,
|
| 32 |
+
LAYOUT: gl.constexpr,
|
| 33 |
+
):
|
| 34 |
+
if LAYOUT[0] == "T":
|
| 35 |
+
SHARED_LAYOUT_A: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 36 |
+
[[BLOCK_K, 8]], [BLOCK_M, BLOCK_K], [1, 0]
|
| 37 |
+
)
|
| 38 |
+
else:
|
| 39 |
+
SHARED_LAYOUT_A: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 40 |
+
[[BLOCK_M, 8]], [BLOCK_K, BLOCK_M], [1, 0]
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
if LAYOUT[1] == "T":
|
| 44 |
+
SHARED_LAYOUT_B: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 45 |
+
[[BLOCK_N, 16]], [BLOCK_K, BLOCK_N], [1, 0]
|
| 46 |
+
)
|
| 47 |
+
else:
|
| 48 |
+
SHARED_LAYOUT_B: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 49 |
+
[[BLOCK_K, 8]], [BLOCK_N, BLOCK_K], [1, 0]
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
return (SHARED_LAYOUT_A, SHARED_LAYOUT_B)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def create_wmma_layouts(num_warps):
|
| 56 |
+
warp_bases = [(0, 1)]
|
| 57 |
+
for i in range(int(math.log2(num_warps // 2))):
|
| 58 |
+
warp_bases.append((1 << i, 0))
|
| 59 |
+
warp_bases = tuple(warp_bases)
|
| 60 |
+
|
| 61 |
+
wmma_layout = gl.amd.AMDWMMALayout(
|
| 62 |
+
version=3, transposed=True, warp_bases=warp_bases, instr_shape=[16, 16, 32]
|
| 63 |
+
)
|
| 64 |
+
operand_a = gl.DotOperandLayout(operand_index=0, parent=wmma_layout, k_width=8)
|
| 65 |
+
operand_b = gl.DotOperandLayout(operand_index=1, parent=wmma_layout, k_width=8)
|
| 66 |
+
return (wmma_layout, operand_a, operand_b)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
@gluon.jit(repr=_gemm_a16w16_bandwidth_bound_repr)
|
| 70 |
+
def _gemm_a16w16_bandwidth_bound_kernel(
|
| 71 |
+
a_ptr,
|
| 72 |
+
b_ptr,
|
| 73 |
+
c_ptr,
|
| 74 |
+
bias_ptr,
|
| 75 |
+
M,
|
| 76 |
+
N,
|
| 77 |
+
K,
|
| 78 |
+
stride_am,
|
| 79 |
+
stride_ak,
|
| 80 |
+
stride_bk,
|
| 81 |
+
stride_bn,
|
| 82 |
+
stride_cm,
|
| 83 |
+
stride_cn,
|
| 84 |
+
BLOCK_M: gl.constexpr,
|
| 85 |
+
BLOCK_N: gl.constexpr,
|
| 86 |
+
BLOCK_K: gl.constexpr,
|
| 87 |
+
NUM_BUFFERS: gl.constexpr,
|
| 88 |
+
LAYOUT: gl.constexpr,
|
| 89 |
+
SHARED_LAYOUT_A: gl.constexpr,
|
| 90 |
+
SHARED_LAYOUT_B: gl.constexpr,
|
| 91 |
+
WMMA_LAYOUT: gl.constexpr,
|
| 92 |
+
OPERAND_LAYOUT_A: gl.constexpr,
|
| 93 |
+
OPERAND_LAYOUT_B: gl.constexpr,
|
| 94 |
+
activation: gl.constexpr,
|
| 95 |
+
USE_ACTIVATION: gl.constexpr,
|
| 96 |
+
ADD_BIAS: gl.constexpr,
|
| 97 |
+
):
|
| 98 |
+
pid = gl.program_id(axis=0)
|
| 99 |
+
num_pid_m = gl.cdiv(M, BLOCK_M)
|
| 100 |
+
pid_m = pid % num_pid_m
|
| 101 |
+
pid_n = pid // num_pid_m
|
| 102 |
+
|
| 103 |
+
# Descriptors start at this block's (M, N) offset by biasing the base
|
| 104 |
+
# pointer — subsequent async_loads use [0, 0] and step only along K.
|
| 105 |
+
a_base = a_ptr + pid_m * BLOCK_M * stride_am
|
| 106 |
+
b_base = b_ptr + pid_n * BLOCK_N * stride_bn
|
| 107 |
+
|
| 108 |
+
if LAYOUT[0] == "T":
|
| 109 |
+
a_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 110 |
+
base=a_base,
|
| 111 |
+
shape=(M - pid_m * BLOCK_M, K),
|
| 112 |
+
strides=(stride_am, stride_ak),
|
| 113 |
+
block_shape=(BLOCK_M, BLOCK_K),
|
| 114 |
+
layout=SHARED_LAYOUT_A,
|
| 115 |
+
)
|
| 116 |
+
else:
|
| 117 |
+
a_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 118 |
+
base=a_base,
|
| 119 |
+
shape=(K, M - pid_m * BLOCK_M),
|
| 120 |
+
strides=(stride_ak, stride_am),
|
| 121 |
+
block_shape=(BLOCK_K, BLOCK_M),
|
| 122 |
+
layout=SHARED_LAYOUT_A,
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
if LAYOUT[1] == "T":
|
| 126 |
+
b_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 127 |
+
base=b_base,
|
| 128 |
+
shape=(K, N - pid_n * BLOCK_N),
|
| 129 |
+
strides=(stride_bk, stride_bn),
|
| 130 |
+
block_shape=(BLOCK_K, BLOCK_N),
|
| 131 |
+
layout=SHARED_LAYOUT_B,
|
| 132 |
+
)
|
| 133 |
+
else:
|
| 134 |
+
b_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 135 |
+
base=b_base,
|
| 136 |
+
shape=(N - pid_n * BLOCK_N, K),
|
| 137 |
+
strides=(stride_bn, stride_bk),
|
| 138 |
+
block_shape=(BLOCK_N, BLOCK_K),
|
| 139 |
+
layout=SHARED_LAYOUT_B,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
if LAYOUT[0] == "T":
|
| 143 |
+
a_buffer = gl.allocate_shared_memory(
|
| 144 |
+
a_ptr.type.element_ty,
|
| 145 |
+
shape=[NUM_BUFFERS, BLOCK_M, BLOCK_K],
|
| 146 |
+
layout=SHARED_LAYOUT_A,
|
| 147 |
+
)
|
| 148 |
+
else:
|
| 149 |
+
a_buffer = gl.allocate_shared_memory(
|
| 150 |
+
a_ptr.type.element_ty,
|
| 151 |
+
shape=[NUM_BUFFERS, BLOCK_K, BLOCK_M],
|
| 152 |
+
layout=SHARED_LAYOUT_A,
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
if LAYOUT[1] == "T":
|
| 156 |
+
b_buffer = gl.allocate_shared_memory(
|
| 157 |
+
b_ptr.type.element_ty,
|
| 158 |
+
shape=[NUM_BUFFERS, BLOCK_K, BLOCK_N],
|
| 159 |
+
layout=SHARED_LAYOUT_B,
|
| 160 |
+
)
|
| 161 |
+
else:
|
| 162 |
+
b_buffer = gl.allocate_shared_memory(
|
| 163 |
+
b_ptr.type.element_ty,
|
| 164 |
+
shape=[NUM_BUFFERS, BLOCK_N, BLOCK_K],
|
| 165 |
+
layout=SHARED_LAYOUT_B,
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
load_idx = 0
|
| 169 |
+
compute_idx = 0
|
| 170 |
+
|
| 171 |
+
accumulator = gl.zeros((BLOCK_M, BLOCK_N), dtype=gl.float32, layout=WMMA_LAYOUT)
|
| 172 |
+
|
| 173 |
+
# Fill the pipeline
|
| 174 |
+
for _ in gl.static_range(NUM_BUFFERS - 1):
|
| 175 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 176 |
+
a_desc, [0, 0], a_buffer.index(load_idx % NUM_BUFFERS)
|
| 177 |
+
)
|
| 178 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 179 |
+
b_desc, [0, 0], b_buffer.index(load_idx % NUM_BUFFERS)
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
# Walk the descriptors forward one K tile.
|
| 183 |
+
if LAYOUT[0] == "T":
|
| 184 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 185 |
+
a_desc, add_offsets=[0, BLOCK_K]
|
| 186 |
+
)
|
| 187 |
+
else:
|
| 188 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 189 |
+
a_desc, add_offsets=[BLOCK_K, 0]
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
if LAYOUT[1] == "T":
|
| 193 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 194 |
+
b_desc, add_offsets=[BLOCK_K, 0]
|
| 195 |
+
)
|
| 196 |
+
else:
|
| 197 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 198 |
+
b_desc, add_offsets=[0, BLOCK_K]
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
load_idx += 1
|
| 202 |
+
|
| 203 |
+
# Main pipeline loop
|
| 204 |
+
num_k_tiles = gl.cdiv(K, BLOCK_K)
|
| 205 |
+
|
| 206 |
+
for _ in range(num_k_tiles - (NUM_BUFFERS - 1)):
|
| 207 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 208 |
+
a_desc, [0, 0], a_buffer.index(load_idx % NUM_BUFFERS)
|
| 209 |
+
)
|
| 210 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 211 |
+
b_desc, [0, 0], b_buffer.index(load_idx % NUM_BUFFERS)
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * 2)
|
| 215 |
+
|
| 216 |
+
# Walk the descriptors forward one K tile.
|
| 217 |
+
if LAYOUT[0] == "T":
|
| 218 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 219 |
+
a_desc, add_offsets=[0, BLOCK_K]
|
| 220 |
+
)
|
| 221 |
+
else:
|
| 222 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 223 |
+
a_desc, add_offsets=[BLOCK_K, 0]
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
if LAYOUT[1] == "T":
|
| 227 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 228 |
+
b_desc, add_offsets=[BLOCK_K, 0]
|
| 229 |
+
)
|
| 230 |
+
else:
|
| 231 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 232 |
+
b_desc, add_offsets=[0, BLOCK_K]
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
load_idx += 1
|
| 236 |
+
|
| 237 |
+
if LAYOUT[0] == "T":
|
| 238 |
+
cur_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 239 |
+
a_buffer.index(compute_idx % NUM_BUFFERS), OPERAND_LAYOUT_A
|
| 240 |
+
)
|
| 241 |
+
else:
|
| 242 |
+
cur_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 243 |
+
a_buffer.index(compute_idx % NUM_BUFFERS).permute([1, 0]),
|
| 244 |
+
OPERAND_LAYOUT_A,
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
if LAYOUT[1] == "T":
|
| 248 |
+
cur_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 249 |
+
b_buffer.index(compute_idx % NUM_BUFFERS), OPERAND_LAYOUT_B
|
| 250 |
+
)
|
| 251 |
+
else:
|
| 252 |
+
cur_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 253 |
+
b_buffer.index(compute_idx % NUM_BUFFERS).permute([1, 0]),
|
| 254 |
+
OPERAND_LAYOUT_B,
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
accumulator = gl.amd.gfx1250.wmma(cur_a, cur_b, accumulator)
|
| 258 |
+
|
| 259 |
+
compute_idx += 1
|
| 260 |
+
|
| 261 |
+
# Epilogue: no more loads
|
| 262 |
+
for i in gl.static_range(NUM_BUFFERS - 1):
|
| 263 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 2 - i) * 2)
|
| 264 |
+
|
| 265 |
+
if LAYOUT[0] == "T":
|
| 266 |
+
cur_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 267 |
+
a_buffer.index(compute_idx % NUM_BUFFERS), OPERAND_LAYOUT_A
|
| 268 |
+
)
|
| 269 |
+
else:
|
| 270 |
+
cur_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 271 |
+
a_buffer.index(compute_idx % NUM_BUFFERS).permute([1, 0]),
|
| 272 |
+
OPERAND_LAYOUT_A,
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
if LAYOUT[1] == "T":
|
| 276 |
+
cur_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 277 |
+
b_buffer.index(compute_idx % NUM_BUFFERS), OPERAND_LAYOUT_B
|
| 278 |
+
)
|
| 279 |
+
else:
|
| 280 |
+
cur_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 281 |
+
b_buffer.index(compute_idx % NUM_BUFFERS).permute([1, 0]),
|
| 282 |
+
OPERAND_LAYOUT_B,
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
accumulator = gl.amd.gfx1250.wmma(cur_a, cur_b, accumulator)
|
| 286 |
+
compute_idx += 1
|
| 287 |
+
|
| 288 |
+
# Bias
|
| 289 |
+
if ADD_BIAS:
|
| 290 |
+
offs_bias = pid_n * BLOCK_N + gl.arange(
|
| 291 |
+
0, BLOCK_N, layout=gl.SliceLayout(0, WMMA_LAYOUT)
|
| 292 |
+
)
|
| 293 |
+
bias_vals = gl.load(bias_ptr + offs_bias, mask=offs_bias < N, other=0.0)
|
| 294 |
+
accumulator = accumulator + bias_vals[None, :]
|
| 295 |
+
|
| 296 |
+
# Activation
|
| 297 |
+
if USE_ACTIVATION:
|
| 298 |
+
accumulator = activation(accumulator)
|
| 299 |
+
|
| 300 |
+
offs_cm = pid_m * BLOCK_M + gl.arange(
|
| 301 |
+
0, BLOCK_M, layout=gl.SliceLayout(1, WMMA_LAYOUT)
|
| 302 |
+
)
|
| 303 |
+
offs_cn = pid_n * BLOCK_N + gl.arange(
|
| 304 |
+
0, BLOCK_N, layout=gl.SliceLayout(0, WMMA_LAYOUT)
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
offs_c = stride_cm * offs_cm[:, None] + stride_cn * offs_cn[None, :]
|
| 308 |
+
|
| 309 |
+
mask_c = (offs_cm[:, None] < M) & (offs_cn[None, :] < N)
|
| 310 |
+
|
| 311 |
+
# Store
|
| 312 |
+
gl.amd.gfx1250.buffer_store(
|
| 313 |
+
accumulator.to(c_ptr.type.element_ty), c_ptr, offs_c, mask=mask_c
|
| 314 |
+
)
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
@gluon.jit(repr=_gemm_a16w16_compute_bound_repr)
|
| 318 |
+
def _gemm_a16w16_compute_bound_kernel(
|
| 319 |
+
a_ptr,
|
| 320 |
+
b_ptr,
|
| 321 |
+
c_ptr,
|
| 322 |
+
bias_ptr,
|
| 323 |
+
M,
|
| 324 |
+
N,
|
| 325 |
+
K,
|
| 326 |
+
stride_am,
|
| 327 |
+
stride_ak,
|
| 328 |
+
stride_bk,
|
| 329 |
+
stride_bn,
|
| 330 |
+
stride_cm,
|
| 331 |
+
stride_cn,
|
| 332 |
+
BLOCK_M: gl.constexpr,
|
| 333 |
+
BLOCK_N: gl.constexpr,
|
| 334 |
+
BLOCK_K: gl.constexpr,
|
| 335 |
+
NUM_BUFFERS: gl.constexpr,
|
| 336 |
+
LAYOUT: gl.constexpr,
|
| 337 |
+
SHARED_LAYOUT_A: gl.constexpr,
|
| 338 |
+
SHARED_LAYOUT_B: gl.constexpr,
|
| 339 |
+
WMMA_LAYOUT: gl.constexpr,
|
| 340 |
+
OPERAND_LAYOUT_A: gl.constexpr,
|
| 341 |
+
OPERAND_LAYOUT_B: gl.constexpr,
|
| 342 |
+
activation: gl.constexpr,
|
| 343 |
+
USE_ACTIVATION: gl.constexpr,
|
| 344 |
+
ADD_BIAS: gl.constexpr,
|
| 345 |
+
):
|
| 346 |
+
"""Local-load pipelining across K-tiles.
|
| 347 |
+
|
| 348 |
+
Manually places load_shared_relaxed for tile i+1 *before* the wmma for
|
| 349 |
+
tile i so the hardware LDS unit and matrix unit can run in parallel.
|
| 350 |
+
LLVM fails to schedule this reordering on its own in the bandwidth_bound kernel.
|
| 351 |
+
|
| 352 |
+
Requires NUM_BUFFERS >= 2. With NUM_BUFFERS == 2 the TDM must complete
|
| 353 |
+
fully before each ds_read batch (async_wait(0)), but the ds_read/wmma
|
| 354 |
+
overlap is still preserved. NUM_BUFFERS >= 3 is recommended.
|
| 355 |
+
"""
|
| 356 |
+
gl.static_assert(NUM_BUFFERS >= 2, "compute_bound kernel requires NUM_BUFFERS >= 2")
|
| 357 |
+
|
| 358 |
+
pid = gl.program_id(axis=0)
|
| 359 |
+
num_pid_m = gl.cdiv(M, BLOCK_M)
|
| 360 |
+
pid_m = pid % num_pid_m
|
| 361 |
+
pid_n = pid // num_pid_m
|
| 362 |
+
|
| 363 |
+
# Descriptors start at this block's (M, N) offset by biasing the base
|
| 364 |
+
# pointer — subsequent async_loads use [0, 0] and step only along K.
|
| 365 |
+
a_base = a_ptr + pid_m * BLOCK_M * stride_am
|
| 366 |
+
b_base = b_ptr + pid_n * BLOCK_N * stride_bn
|
| 367 |
+
|
| 368 |
+
if LAYOUT[0] == "T":
|
| 369 |
+
a_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 370 |
+
base=a_base,
|
| 371 |
+
shape=(M - pid_m * BLOCK_M, K),
|
| 372 |
+
strides=(stride_am, stride_ak),
|
| 373 |
+
block_shape=(BLOCK_M, BLOCK_K),
|
| 374 |
+
layout=SHARED_LAYOUT_A,
|
| 375 |
+
)
|
| 376 |
+
else:
|
| 377 |
+
a_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 378 |
+
base=a_base,
|
| 379 |
+
shape=(K, M - pid_m * BLOCK_M),
|
| 380 |
+
strides=(stride_ak, stride_am),
|
| 381 |
+
block_shape=(BLOCK_K, BLOCK_M),
|
| 382 |
+
layout=SHARED_LAYOUT_A,
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
if LAYOUT[1] == "T":
|
| 386 |
+
b_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 387 |
+
base=b_base,
|
| 388 |
+
shape=(K, N - pid_n * BLOCK_N),
|
| 389 |
+
strides=(stride_bk, stride_bn),
|
| 390 |
+
block_shape=(BLOCK_K, BLOCK_N),
|
| 391 |
+
layout=SHARED_LAYOUT_B,
|
| 392 |
+
)
|
| 393 |
+
else:
|
| 394 |
+
b_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 395 |
+
base=b_base,
|
| 396 |
+
shape=(N - pid_n * BLOCK_N, K),
|
| 397 |
+
strides=(stride_bn, stride_bk),
|
| 398 |
+
block_shape=(BLOCK_N, BLOCK_K),
|
| 399 |
+
layout=SHARED_LAYOUT_B,
|
| 400 |
+
)
|
| 401 |
+
|
| 402 |
+
if LAYOUT[0] == "T":
|
| 403 |
+
a_buffer = gl.allocate_shared_memory(
|
| 404 |
+
a_ptr.type.element_ty,
|
| 405 |
+
shape=[NUM_BUFFERS, BLOCK_M, BLOCK_K],
|
| 406 |
+
layout=SHARED_LAYOUT_A,
|
| 407 |
+
)
|
| 408 |
+
else:
|
| 409 |
+
a_buffer = gl.allocate_shared_memory(
|
| 410 |
+
a_ptr.type.element_ty,
|
| 411 |
+
shape=[NUM_BUFFERS, BLOCK_K, BLOCK_M],
|
| 412 |
+
layout=SHARED_LAYOUT_A,
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
if LAYOUT[1] == "T":
|
| 416 |
+
b_buffer = gl.allocate_shared_memory(
|
| 417 |
+
b_ptr.type.element_ty,
|
| 418 |
+
shape=[NUM_BUFFERS, BLOCK_K, BLOCK_N],
|
| 419 |
+
layout=SHARED_LAYOUT_B,
|
| 420 |
+
)
|
| 421 |
+
else:
|
| 422 |
+
b_buffer = gl.allocate_shared_memory(
|
| 423 |
+
b_ptr.type.element_ty,
|
| 424 |
+
shape=[NUM_BUFFERS, BLOCK_N, BLOCK_K],
|
| 425 |
+
layout=SHARED_LAYOUT_B,
|
| 426 |
+
)
|
| 427 |
+
|
| 428 |
+
load_idx = 0
|
| 429 |
+
compute_idx = 0
|
| 430 |
+
|
| 431 |
+
accumulator = gl.zeros((BLOCK_M, BLOCK_N), dtype=gl.float32, layout=WMMA_LAYOUT)
|
| 432 |
+
|
| 433 |
+
# TDM prologue: fill the pipeline with NUM_BUFFERS-1 tiles
|
| 434 |
+
for _ in gl.static_range(NUM_BUFFERS):
|
| 435 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 436 |
+
a_desc, [0, 0], a_buffer.index(load_idx % NUM_BUFFERS)
|
| 437 |
+
)
|
| 438 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 439 |
+
b_desc, [0, 0], b_buffer.index(load_idx % NUM_BUFFERS)
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
# Walk the descriptors forward one K tile.
|
| 443 |
+
if LAYOUT[0] == "T":
|
| 444 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 445 |
+
a_desc, add_offsets=[0, BLOCK_K]
|
| 446 |
+
)
|
| 447 |
+
else:
|
| 448 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 449 |
+
a_desc, add_offsets=[BLOCK_K, 0]
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
if LAYOUT[1] == "T":
|
| 453 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 454 |
+
b_desc, add_offsets=[BLOCK_K, 0]
|
| 455 |
+
)
|
| 456 |
+
else:
|
| 457 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 458 |
+
b_desc, add_offsets=[0, BLOCK_K]
|
| 459 |
+
)
|
| 460 |
+
|
| 461 |
+
load_idx += 1
|
| 462 |
+
|
| 463 |
+
num_k_tiles = gl.cdiv(K, BLOCK_K)
|
| 464 |
+
|
| 465 |
+
# Register pre-load prologue: wait for tile 0 then read it into cur_a/cur_b.
|
| 466 |
+
# After TDM prologue there are (NUM_BUFFERS-1)*2 ops in-flight; waiting for
|
| 467 |
+
# (NUM_BUFFERS-2)*2 lets exactly one tile (tile 0) complete.
|
| 468 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * 2)
|
| 469 |
+
|
| 470 |
+
if LAYOUT[0] == "T":
|
| 471 |
+
cur_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 472 |
+
a_buffer.index(compute_idx % NUM_BUFFERS), OPERAND_LAYOUT_A
|
| 473 |
+
)
|
| 474 |
+
else:
|
| 475 |
+
cur_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 476 |
+
a_buffer.index(compute_idx % NUM_BUFFERS).permute([1, 0]),
|
| 477 |
+
OPERAND_LAYOUT_A,
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
if LAYOUT[1] == "T":
|
| 481 |
+
cur_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 482 |
+
b_buffer.index(compute_idx % NUM_BUFFERS), OPERAND_LAYOUT_B
|
| 483 |
+
)
|
| 484 |
+
else:
|
| 485 |
+
cur_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 486 |
+
b_buffer.index(compute_idx % NUM_BUFFERS).permute([1, 0]),
|
| 487 |
+
OPERAND_LAYOUT_B,
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
# Main pipeline loop — first iteration peeled out below, then loop runs
|
| 491 |
+
# for (num_k_tiles - (NUM_BUFFERS - 1) - 1) remaining iterations.
|
| 492 |
+
|
| 493 |
+
# ---- Peeled first iteration ----
|
| 494 |
+
# WMMA for the current tile — uses operands pre-loaded in the
|
| 495 |
+
# *previous* iteration so no ds_read stall before the matrix op.
|
| 496 |
+
accumulator = gl.amd.gfx1250.wmma(cur_a, cur_b, accumulator)
|
| 497 |
+
|
| 498 |
+
# Issue TDM for the tile that is (NUM_BUFFERS-1) steps ahead
|
| 499 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 500 |
+
a_desc, [0, 0], a_buffer.index(load_idx % NUM_BUFFERS)
|
| 501 |
+
)
|
| 502 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 503 |
+
b_desc, [0, 0], b_buffer.index(load_idx % NUM_BUFFERS)
|
| 504 |
+
)
|
| 505 |
+
|
| 506 |
+
# Walk the descriptors forward one K tile.
|
| 507 |
+
if LAYOUT[0] == "T":
|
| 508 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 509 |
+
a_desc, add_offsets=[0, BLOCK_K]
|
| 510 |
+
)
|
| 511 |
+
else:
|
| 512 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 513 |
+
a_desc, add_offsets=[BLOCK_K, 0]
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
if LAYOUT[1] == "T":
|
| 517 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 518 |
+
b_desc, add_offsets=[BLOCK_K, 0]
|
| 519 |
+
)
|
| 520 |
+
else:
|
| 521 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 522 |
+
b_desc, add_offsets=[0, BLOCK_K]
|
| 523 |
+
)
|
| 524 |
+
|
| 525 |
+
# Tighter wait: after issuing the new TDM there are (NUM_BUFFERS-1)*2
|
| 526 |
+
# ops in-flight. Waiting for (NUM_BUFFERS-2)*2 guarantees that tile
|
| 527 |
+
# compute_idx+1 has landed in LDS.
|
| 528 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * 2)
|
| 529 |
+
|
| 530 |
+
load_idx += 1
|
| 531 |
+
|
| 532 |
+
# Pre-load the NEXT tile's operands into registers *before* the WMMA
|
| 533 |
+
# below. The hardware can run LDS reads and the matrix unit in
|
| 534 |
+
# parallel, hiding the ds_read latency inside the WMMA execution.
|
| 535 |
+
if LAYOUT[0] == "T":
|
| 536 |
+
next_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 537 |
+
a_buffer.index((compute_idx + 1) % NUM_BUFFERS), OPERAND_LAYOUT_A
|
| 538 |
+
)
|
| 539 |
+
else:
|
| 540 |
+
next_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 541 |
+
a_buffer.index((compute_idx + 1) % NUM_BUFFERS).permute([1, 0]),
|
| 542 |
+
OPERAND_LAYOUT_A,
|
| 543 |
+
)
|
| 544 |
+
|
| 545 |
+
if LAYOUT[1] == "T":
|
| 546 |
+
next_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 547 |
+
b_buffer.index((compute_idx + 1) % NUM_BUFFERS), OPERAND_LAYOUT_B
|
| 548 |
+
)
|
| 549 |
+
else:
|
| 550 |
+
next_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 551 |
+
b_buffer.index((compute_idx + 1) % NUM_BUFFERS).permute([1, 0]),
|
| 552 |
+
OPERAND_LAYOUT_B,
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
cur_a = next_a
|
| 556 |
+
cur_b = next_b
|
| 557 |
+
compute_idx += 1
|
| 558 |
+
|
| 559 |
+
# ---- Remaining main-loop iterations ----
|
| 560 |
+
for _ in range(num_k_tiles - NUM_BUFFERS - 1):
|
| 561 |
+
|
| 562 |
+
# WMMA for the current tile — uses operands pre-loaded in the
|
| 563 |
+
# *previous* iteration so no ds_read stall before the matrix op.
|
| 564 |
+
accumulator = gl.amd.gfx1250.wmma(cur_a, cur_b, accumulator)
|
| 565 |
+
|
| 566 |
+
# Issue TDM for the tile that is (NUM_BUFFERS-1) steps ahead
|
| 567 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 568 |
+
a_desc, [0, 0], a_buffer.index(load_idx % NUM_BUFFERS)
|
| 569 |
+
)
|
| 570 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 571 |
+
b_desc, [0, 0], b_buffer.index(load_idx % NUM_BUFFERS)
|
| 572 |
+
)
|
| 573 |
+
|
| 574 |
+
# Walk the descriptors forward one K tile.
|
| 575 |
+
if LAYOUT[0] == "T":
|
| 576 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 577 |
+
a_desc, add_offsets=[0, BLOCK_K]
|
| 578 |
+
)
|
| 579 |
+
else:
|
| 580 |
+
a_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 581 |
+
a_desc, add_offsets=[BLOCK_K, 0]
|
| 582 |
+
)
|
| 583 |
+
|
| 584 |
+
if LAYOUT[1] == "T":
|
| 585 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 586 |
+
b_desc, add_offsets=[BLOCK_K, 0]
|
| 587 |
+
)
|
| 588 |
+
else:
|
| 589 |
+
b_desc = gl.amd.gfx1250.tdm.update_tensor_descriptor(
|
| 590 |
+
b_desc, add_offsets=[0, BLOCK_K]
|
| 591 |
+
)
|
| 592 |
+
|
| 593 |
+
# Tighter wait: after issuing the new TDM there are (NUM_BUFFERS-1)*2
|
| 594 |
+
# ops in-flight. Waiting for (NUM_BUFFERS-2)*2 guarantees that tile
|
| 595 |
+
# compute_idx+1 has landed in LDS.
|
| 596 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * 2)
|
| 597 |
+
|
| 598 |
+
load_idx += 1
|
| 599 |
+
|
| 600 |
+
# Pre-load the NEXT tile's operands into registers *before* the WMMA
|
| 601 |
+
# below. The hardware can run LDS reads and the matrix unit in
|
| 602 |
+
# parallel, hiding the ds_read latency inside the WMMA execution.
|
| 603 |
+
if LAYOUT[0] == "T":
|
| 604 |
+
next_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 605 |
+
a_buffer.index((compute_idx + 1) % NUM_BUFFERS), OPERAND_LAYOUT_A
|
| 606 |
+
)
|
| 607 |
+
else:
|
| 608 |
+
next_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 609 |
+
a_buffer.index((compute_idx + 1) % NUM_BUFFERS).permute([1, 0]),
|
| 610 |
+
OPERAND_LAYOUT_A,
|
| 611 |
+
)
|
| 612 |
+
|
| 613 |
+
if LAYOUT[1] == "T":
|
| 614 |
+
next_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 615 |
+
b_buffer.index((compute_idx + 1) % NUM_BUFFERS), OPERAND_LAYOUT_B
|
| 616 |
+
)
|
| 617 |
+
else:
|
| 618 |
+
next_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 619 |
+
b_buffer.index((compute_idx + 1) % NUM_BUFFERS).permute([1, 0]),
|
| 620 |
+
OPERAND_LAYOUT_B,
|
| 621 |
+
)
|
| 622 |
+
|
| 623 |
+
cur_a = next_a
|
| 624 |
+
cur_b = next_b
|
| 625 |
+
compute_idx += 1
|
| 626 |
+
|
| 627 |
+
# Epilogue: no more TDM loads; drain the remaining NUM_BUFFERS-1 tiles.
|
| 628 |
+
# The first NUM_BUFFERS-2 iterations still use the pre-load / WMMA pattern.
|
| 629 |
+
for i in gl.static_range(NUM_BUFFERS - 1):
|
| 630 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 2 - i) * 2)
|
| 631 |
+
|
| 632 |
+
if LAYOUT[0] == "T":
|
| 633 |
+
next_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 634 |
+
a_buffer.index((compute_idx + 1) % NUM_BUFFERS), OPERAND_LAYOUT_A
|
| 635 |
+
)
|
| 636 |
+
else:
|
| 637 |
+
next_a = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 638 |
+
a_buffer.index((compute_idx + 1) % NUM_BUFFERS).permute([1, 0]),
|
| 639 |
+
OPERAND_LAYOUT_A,
|
| 640 |
+
)
|
| 641 |
+
|
| 642 |
+
if LAYOUT[1] == "T":
|
| 643 |
+
next_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 644 |
+
b_buffer.index((compute_idx + 1) % NUM_BUFFERS), OPERAND_LAYOUT_B
|
| 645 |
+
)
|
| 646 |
+
else:
|
| 647 |
+
next_b = gl.amd.cdna4.async_copy.load_shared_relaxed(
|
| 648 |
+
b_buffer.index((compute_idx + 1) % NUM_BUFFERS).permute([1, 0]),
|
| 649 |
+
OPERAND_LAYOUT_B,
|
| 650 |
+
)
|
| 651 |
+
accumulator = gl.amd.gfx1250.wmma(cur_a, cur_b, accumulator)
|
| 652 |
+
|
| 653 |
+
cur_a = next_a
|
| 654 |
+
cur_b = next_b
|
| 655 |
+
compute_idx += 1
|
| 656 |
+
|
| 657 |
+
# Final WMMA for the last pre-loaded tile
|
| 658 |
+
accumulator = gl.amd.gfx1250.wmma(cur_a, cur_b, accumulator)
|
| 659 |
+
|
| 660 |
+
# if NUM_BUFFERS > 2:
|
| 661 |
+
# gl.amd.sched_barrier(0)
|
| 662 |
+
|
| 663 |
+
# Bias
|
| 664 |
+
if ADD_BIAS:
|
| 665 |
+
offs_bias = pid_n * BLOCK_N + gl.arange(
|
| 666 |
+
0, BLOCK_N, layout=gl.SliceLayout(0, WMMA_LAYOUT)
|
| 667 |
+
)
|
| 668 |
+
bias_vals = gl.load(bias_ptr + offs_bias, mask=offs_bias < N, other=0.0)
|
| 669 |
+
accumulator = accumulator + bias_vals[None, :]
|
| 670 |
+
|
| 671 |
+
# Activation
|
| 672 |
+
if USE_ACTIVATION:
|
| 673 |
+
accumulator = activation(accumulator)
|
| 674 |
+
|
| 675 |
+
# TDM Store: accumulator → shared memory → global memory
|
| 676 |
+
SHARED_LAYOUT_C: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 677 |
+
[[BLOCK_N, 8]], [BLOCK_M, BLOCK_N], [1, 0]
|
| 678 |
+
)
|
| 679 |
+
c_buffer = gl.allocate_shared_memory(
|
| 680 |
+
c_ptr.type.element_ty,
|
| 681 |
+
shape=[BLOCK_M, BLOCK_N],
|
| 682 |
+
layout=SHARED_LAYOUT_C,
|
| 683 |
+
)
|
| 684 |
+
c_buffer.store(accumulator.to(c_ptr.type.element_ty))
|
| 685 |
+
|
| 686 |
+
# Ensure all wavefronts have finished writing to LDS before TDM reads it.
|
| 687 |
+
gl.barrier()
|
| 688 |
+
|
| 689 |
+
c_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 690 |
+
base=c_ptr,
|
| 691 |
+
shape=(M, N),
|
| 692 |
+
strides=(stride_cm, stride_cn),
|
| 693 |
+
block_shape=(BLOCK_M, BLOCK_N),
|
| 694 |
+
layout=SHARED_LAYOUT_C,
|
| 695 |
+
)
|
| 696 |
+
gl.amd.gfx1250.tdm.async_store(c_desc, [pid_m * BLOCK_M, pid_n * BLOCK_N], c_buffer)
|
| 697 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 698 |
+
|
| 699 |
+
|
| 700 |
+
_KERNEL_MAP = {
|
| 701 |
+
"bandwidth_bound": _gemm_a16w16_bandwidth_bound_kernel,
|
| 702 |
+
"compute_bound": _gemm_a16w16_compute_bound_kernel,
|
| 703 |
+
}
|
build/torch-rocm/_gluon_kernels/gfx1250/gemm/basic/gemm_mxfp4.py
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from triton.experimental import gluon
|
| 2 |
+
import triton.experimental.gluon.language as gl
|
| 3 |
+
from .....utils._triton.kernel_repr import make_kernel_repr
|
| 4 |
+
|
| 5 |
+
SCALE_GROUP_ELEMS = 32
|
| 6 |
+
PRESHUFFLE_FACTOR = 16 # rows packed per scale-preshuffle stripe
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def get_gemm_afp4wfp4_preshuffle_layouts(num_warps, BLOCK_M, BLOCK_N, BLOCK_K):
|
| 10 |
+
K_GROUPS = BLOCK_K // SCALE_GROUP_ELEMS
|
| 11 |
+
BLOCK_K_BYTES = BLOCK_K // 2
|
| 12 |
+
|
| 13 |
+
# Warp/register layout bases depend on warp count
|
| 14 |
+
if num_warps == 2:
|
| 15 |
+
warp_bases = [[1, 0]]
|
| 16 |
+
reg_bases = []
|
| 17 |
+
elif num_warps == 4:
|
| 18 |
+
warp_bases = [[0, 1], [2, 0]]
|
| 19 |
+
reg_bases = [[1, 0]]
|
| 20 |
+
else:
|
| 21 |
+
warp_bases = [[1, 0], [0, 1], [2, 0]]
|
| 22 |
+
reg_bases = []
|
| 23 |
+
|
| 24 |
+
# e2m1 uses instr_shape [16,16,64] for operands
|
| 25 |
+
wmma_layout = gl.amd.AMDWMMALayout(
|
| 26 |
+
version=3,
|
| 27 |
+
transposed=True,
|
| 28 |
+
warp_bases=warp_bases,
|
| 29 |
+
reg_bases=reg_bases,
|
| 30 |
+
instr_shape=[32, 16, 64],
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
wmma_acc_layout = gl.amd.AMDWMMALayout(
|
| 34 |
+
version=3,
|
| 35 |
+
transposed=True,
|
| 36 |
+
warp_bases=warp_bases,
|
| 37 |
+
reg_bases=reg_bases,
|
| 38 |
+
instr_shape=[32, 16, 128],
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
# Shared memory layouts
|
| 42 |
+
PAD_INTERVAL_A = 256 if BLOCK_K_BYTES <= 256 else BLOCK_K_BYTES
|
| 43 |
+
shared_A = gl.PaddedSharedLayout.with_identity_for(
|
| 44 |
+
[[PAD_INTERVAL_A, 16]], [BLOCK_M, BLOCK_K_BYTES], [1, 0]
|
| 45 |
+
)
|
| 46 |
+
shared_B = gl.SwizzledSharedLayout(vec=1, per_phase=1, max_phase=1, order=[1, 0])
|
| 47 |
+
shared_S = gl.SwizzledSharedLayout(vec=1, per_phase=1, max_phase=1, order=[1, 0])
|
| 48 |
+
|
| 49 |
+
# Output staging layout for the TDM store (acc -> LDS -> HBM)
|
| 50 |
+
shared_C = gl.SwizzledSharedLayout(vec=1, per_phase=1, max_phase=1, order=[1, 0])
|
| 51 |
+
|
| 52 |
+
# Register layouts for WMMA operands
|
| 53 |
+
dot_a = gl.DotOperandLayout(operand_index=0, parent=wmma_layout, k_width=16)
|
| 54 |
+
dot_b = gl.DotOperandLayout(operand_index=1, parent=wmma_layout, k_width=16)
|
| 55 |
+
|
| 56 |
+
# Register layouts for WMMA scale operands
|
| 57 |
+
scale_a = gl.amd.gfx1250.get_wmma_scale_layout(
|
| 58 |
+
dot_a, [BLOCK_M, K_GROUPS], scale_factor=SCALE_GROUP_ELEMS
|
| 59 |
+
)
|
| 60 |
+
scale_b = gl.amd.gfx1250.get_wmma_scale_layout(
|
| 61 |
+
dot_b, [BLOCK_N, K_GROUPS], scale_factor=SCALE_GROUP_ELEMS
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
return {
|
| 65 |
+
"wmma_layout": wmma_layout,
|
| 66 |
+
"wmma_acc_layout": wmma_acc_layout,
|
| 67 |
+
"shared_A": shared_A,
|
| 68 |
+
"shared_B": shared_B,
|
| 69 |
+
"shared_S": shared_S,
|
| 70 |
+
"shared_C": shared_C,
|
| 71 |
+
"dot_a_layout": dot_a,
|
| 72 |
+
"dot_b_layout": dot_b,
|
| 73 |
+
"a_scale_layout": scale_a,
|
| 74 |
+
"b_scale_layout": scale_b,
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
# ---------------------------------------------------------------------------
|
| 79 |
+
# View transforms for preshuffled data in LDS
|
| 80 |
+
# These are zero-cost (no data movement) — they just reindex the LDS view
|
| 81 |
+
# so load_shared_relaxed reads bytes in the order WMMA expects.
|
| 82 |
+
# ---------------------------------------------------------------------------
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
@gluon.jit
|
| 86 |
+
def depreshuffle_scales(
|
| 87 |
+
smem_scales,
|
| 88 |
+
BLOCK_M: gl.constexpr,
|
| 89 |
+
K_GROUPS: gl.constexpr,
|
| 90 |
+
):
|
| 91 |
+
# Inverse of host shuffle_scales_gfx1250: PRESHUFFLE_FACTOR rows are packed
|
| 92 |
+
# per stripe, SCALE_KWIDTH scale-groups contiguous per row.
|
| 93 |
+
PRESHUFFLE_FACTOR: gl.constexpr = 16
|
| 94 |
+
SCALE_KWIDTH: gl.constexpr = 4
|
| 95 |
+
NUM_STRIPES: gl.constexpr = K_GROUPS // SCALE_KWIDTH
|
| 96 |
+
return (
|
| 97 |
+
smem_scales.reshape(
|
| 98 |
+
(BLOCK_M // PRESHUFFLE_FACTOR, NUM_STRIPES, PRESHUFFLE_FACTOR, SCALE_KWIDTH)
|
| 99 |
+
)
|
| 100 |
+
.permute((0, 2, 1, 3))
|
| 101 |
+
.reshape((BLOCK_M, K_GROUPS))
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
@gluon.jit
|
| 106 |
+
def depreshuffle_b_raw_to_kn(
|
| 107 |
+
b_raw,
|
| 108 |
+
BLOCK_N: gl.constexpr,
|
| 109 |
+
BLOCK_K_BYTES: gl.constexpr,
|
| 110 |
+
):
|
| 111 |
+
# raw -> logical [BLOCK_K_BYTES, BLOCK_N]
|
| 112 |
+
return (
|
| 113 |
+
b_raw.reshape((BLOCK_N // 16, BLOCK_K_BYTES // 32, 2, 16, 16))
|
| 114 |
+
.permute((0, 3, 1, 2, 4))
|
| 115 |
+
.reshape((BLOCK_N, BLOCK_K_BYTES))
|
| 116 |
+
.permute((1, 0))
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
_gemm_mxfp4_preshuffle_gfx1250_repr = make_kernel_repr(
|
| 121 |
+
"_gemm_mxfp4_preshuffle_gfx1250_kernel",
|
| 122 |
+
[
|
| 123 |
+
"BLOCK_SIZE_M",
|
| 124 |
+
"BLOCK_SIZE_N",
|
| 125 |
+
"BLOCK_SIZE_K",
|
| 126 |
+
"num_warps",
|
| 127 |
+
"NUM_BUFFERS",
|
| 128 |
+
],
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
@gluon.jit(repr=_gemm_mxfp4_preshuffle_gfx1250_repr)
|
| 133 |
+
def gemm_mxfp4_preshuffle_gfx1250(
|
| 134 |
+
a_fp4_ptr,
|
| 135 |
+
b_preshuf_ptr,
|
| 136 |
+
c_ptr,
|
| 137 |
+
a_scale_ptr,
|
| 138 |
+
b_scale_ptr,
|
| 139 |
+
M,
|
| 140 |
+
N,
|
| 141 |
+
K_elems,
|
| 142 |
+
stride_a_m,
|
| 143 |
+
stride_a_kbytes,
|
| 144 |
+
stride_b_n16,
|
| 145 |
+
stride_b_kshuf,
|
| 146 |
+
stride_c_k,
|
| 147 |
+
stride_c_m,
|
| 148 |
+
stride_c_n,
|
| 149 |
+
stride_as_m,
|
| 150 |
+
stride_as_k,
|
| 151 |
+
stride_bs_n,
|
| 152 |
+
stride_bs_k,
|
| 153 |
+
BLOCK_SIZE_M: gl.constexpr,
|
| 154 |
+
BLOCK_SIZE_N: gl.constexpr,
|
| 155 |
+
BLOCK_SIZE_K: gl.constexpr,
|
| 156 |
+
num_warps: gl.constexpr,
|
| 157 |
+
NUM_BUFFERS: gl.constexpr,
|
| 158 |
+
wmma_layout: gl.constexpr,
|
| 159 |
+
wmma_acc_layout: gl.constexpr,
|
| 160 |
+
shared_A: gl.constexpr,
|
| 161 |
+
shared_B: gl.constexpr,
|
| 162 |
+
shared_S: gl.constexpr,
|
| 163 |
+
shared_C: gl.constexpr,
|
| 164 |
+
dot_a_layout: gl.constexpr,
|
| 165 |
+
dot_b_layout: gl.constexpr,
|
| 166 |
+
a_scale_layout: gl.constexpr,
|
| 167 |
+
b_scale_layout: gl.constexpr,
|
| 168 |
+
):
|
| 169 |
+
# Compile-time constants
|
| 170 |
+
FP4_ELEMS_PER_BYTE: gl.constexpr = 2
|
| 171 |
+
SCALE_GROUP_ELEMS: gl.constexpr = 32
|
| 172 |
+
|
| 173 |
+
BLOCK_K_BYTES: gl.constexpr = BLOCK_SIZE_K // FP4_ELEMS_PER_BYTE
|
| 174 |
+
K_GROUPS: gl.constexpr = BLOCK_SIZE_K // SCALE_GROUP_ELEMS
|
| 175 |
+
# Scale preshuffle: PRESHUFFLE_FACTOR rows packed per stripe, SCALE_KWIDTH
|
| 176 |
+
# scale-groups contiguous per row (must match the host shuffle_scales_gfx1250).
|
| 177 |
+
PRESHUFFLE_FACTOR: gl.constexpr = 16
|
| 178 |
+
SCALE_KWIDTH: gl.constexpr = 4
|
| 179 |
+
|
| 180 |
+
gl.static_assert(K_GROUPS * 32 == BLOCK_SIZE_K)
|
| 181 |
+
|
| 182 |
+
gl.static_assert(BLOCK_SIZE_K % 32 == 0)
|
| 183 |
+
gl.static_assert(K_GROUPS % SCALE_KWIDTH == 0) # K_GROUPS divisible by SCALE_KWIDTH
|
| 184 |
+
gl.static_assert(BLOCK_SIZE_M % PRESHUFFLE_FACTOR == 0)
|
| 185 |
+
gl.static_assert(BLOCK_SIZE_N % PRESHUFFLE_FACTOR == 0)
|
| 186 |
+
|
| 187 |
+
pid = gl.program_id(axis=0)
|
| 188 |
+
tiles_n = gl.cdiv(N, BLOCK_SIZE_N)
|
| 189 |
+
|
| 190 |
+
tile_linear = pid
|
| 191 |
+
tile_m = tile_linear // tiles_n
|
| 192 |
+
tile_n = tile_linear - tile_m * tiles_n
|
| 193 |
+
|
| 194 |
+
K_bytes = K_elems // FP4_ELEMS_PER_BYTE
|
| 195 |
+
k_tiles = gl.cdiv(K_bytes, BLOCK_K_BYTES)
|
| 196 |
+
|
| 197 |
+
# =====================================================================
|
| 198 |
+
# TDM descriptors (HBM tensor layout for async loads)
|
| 199 |
+
# =====================================================================
|
| 200 |
+
a_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 201 |
+
base=a_fp4_ptr + tile_m * BLOCK_SIZE_M * stride_a_m,
|
| 202 |
+
shape=(M - tile_m * BLOCK_SIZE_M, K_bytes),
|
| 203 |
+
strides=(stride_a_m, stride_a_kbytes),
|
| 204 |
+
block_shape=(BLOCK_SIZE_M, BLOCK_K_BYTES),
|
| 205 |
+
layout=shared_A,
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
b_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 209 |
+
base=b_preshuf_ptr + tile_n * (BLOCK_SIZE_N // 16) * stride_b_n16,
|
| 210 |
+
shape=(gl.cdiv(N, 16) - tile_n * (BLOCK_SIZE_N // 16), K_bytes * 16),
|
| 211 |
+
strides=(stride_b_n16, stride_b_kshuf),
|
| 212 |
+
block_shape=(BLOCK_SIZE_N // 16, BLOCK_K_BYTES * 16),
|
| 213 |
+
layout=shared_B,
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
k_scale_cols = K_elems // SCALE_GROUP_ELEMS
|
| 217 |
+
|
| 218 |
+
as_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 219 |
+
base=a_scale_ptr + tile_m * (BLOCK_SIZE_M // PRESHUFFLE_FACTOR) * stride_as_m,
|
| 220 |
+
shape=(
|
| 221 |
+
gl.cdiv(M, PRESHUFFLE_FACTOR)
|
| 222 |
+
- tile_m * (BLOCK_SIZE_M // PRESHUFFLE_FACTOR),
|
| 223 |
+
k_scale_cols * PRESHUFFLE_FACTOR,
|
| 224 |
+
),
|
| 225 |
+
strides=(stride_as_m, stride_as_k),
|
| 226 |
+
block_shape=(BLOCK_SIZE_M // PRESHUFFLE_FACTOR, K_GROUPS * PRESHUFFLE_FACTOR),
|
| 227 |
+
layout=shared_S,
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
bs_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 231 |
+
base=b_scale_ptr + tile_n * (BLOCK_SIZE_N // PRESHUFFLE_FACTOR) * stride_bs_n,
|
| 232 |
+
shape=(
|
| 233 |
+
gl.cdiv(N, PRESHUFFLE_FACTOR)
|
| 234 |
+
- tile_n * (BLOCK_SIZE_N // PRESHUFFLE_FACTOR),
|
| 235 |
+
k_scale_cols * PRESHUFFLE_FACTOR,
|
| 236 |
+
),
|
| 237 |
+
strides=(stride_bs_n, stride_bs_k),
|
| 238 |
+
block_shape=(BLOCK_SIZE_N // PRESHUFFLE_FACTOR, K_GROUPS * PRESHUFFLE_FACTOR),
|
| 239 |
+
layout=shared_S,
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
# =====================================================================
|
| 243 |
+
# Allocate shared memory
|
| 244 |
+
# =====================================================================
|
| 245 |
+
smem_A = gl.allocate_shared_memory(
|
| 246 |
+
a_fp4_ptr.type.element_ty,
|
| 247 |
+
[NUM_BUFFERS, BLOCK_SIZE_M, BLOCK_K_BYTES],
|
| 248 |
+
layout=shared_A,
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
smem_B = gl.allocate_shared_memory(
|
| 252 |
+
b_preshuf_ptr.type.element_ty,
|
| 253 |
+
[NUM_BUFFERS, BLOCK_SIZE_N // 16, BLOCK_K_BYTES * 16],
|
| 254 |
+
layout=shared_B,
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
smem_AS = gl.allocate_shared_memory(
|
| 258 |
+
a_scale_ptr.type.element_ty,
|
| 259 |
+
[NUM_BUFFERS, BLOCK_SIZE_M // PRESHUFFLE_FACTOR, K_GROUPS * PRESHUFFLE_FACTOR],
|
| 260 |
+
layout=shared_S,
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
smem_BS = gl.allocate_shared_memory(
|
| 264 |
+
b_scale_ptr.type.element_ty,
|
| 265 |
+
[NUM_BUFFERS, BLOCK_SIZE_N // PRESHUFFLE_FACTOR, K_GROUPS * PRESHUFFLE_FACTOR],
|
| 266 |
+
layout=shared_S,
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
# Pipelining start
|
| 270 |
+
load_idx = 0
|
| 271 |
+
compute_idx = 0
|
| 272 |
+
acc = gl.zeros(
|
| 273 |
+
(BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=gl.float32, layout=wmma_acc_layout
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
# --- 1. Prologue: fill NUM_BUFFERS-1 LDS slots via TDM ---
|
| 277 |
+
# Load-then-advance: each iter consumes the descriptor's current K
|
| 278 |
+
# position, then steps it forward for the next load (prologue or main).
|
| 279 |
+
for _ in gl.static_range(NUM_BUFFERS):
|
| 280 |
+
slot = load_idx % NUM_BUFFERS
|
| 281 |
+
# slot index math (arith.muli) ahead of the copies so the four tdm async_loads emit back-to-back and the compiler can merge them.
|
| 282 |
+
a_slot = smem_A.index(slot)
|
| 283 |
+
b_slot = smem_B.index(slot)
|
| 284 |
+
as_slot = smem_AS.index(slot)
|
| 285 |
+
bs_slot = smem_BS.index(slot)
|
| 286 |
+
off_a = load_idx * BLOCK_K_BYTES
|
| 287 |
+
off_b = load_idx * BLOCK_K_BYTES * 16
|
| 288 |
+
off_s = load_idx * K_GROUPS * PRESHUFFLE_FACTOR
|
| 289 |
+
gl.amd.gfx1250.tdm.async_load(a_desc, [0, off_a], a_slot)
|
| 290 |
+
gl.amd.gfx1250.tdm.async_load(b_desc, [0, off_b], b_slot)
|
| 291 |
+
gl.amd.gfx1250.tdm.async_load(as_desc, [0, off_s], as_slot)
|
| 292 |
+
gl.amd.gfx1250.tdm.async_load(bs_desc, [0, off_s], bs_slot)
|
| 293 |
+
load_idx += 1
|
| 294 |
+
|
| 295 |
+
# --- 2. Pre-load tile 0 from LDS into registers ---
|
| 296 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * 4)
|
| 297 |
+
|
| 298 |
+
slot_c = compute_idx % NUM_BUFFERS
|
| 299 |
+
cur_A = smem_A.index(slot_c).load(layout=dot_a_layout)
|
| 300 |
+
cur_B = depreshuffle_b_raw_to_kn(
|
| 301 |
+
smem_B.index(slot_c), BLOCK_N=BLOCK_SIZE_N, BLOCK_K_BYTES=BLOCK_K_BYTES
|
| 302 |
+
).load(layout=dot_b_layout)
|
| 303 |
+
cur_AS = depreshuffle_scales(smem_AS.index(slot_c), BLOCK_SIZE_M, K_GROUPS).load(
|
| 304 |
+
layout=a_scale_layout
|
| 305 |
+
)
|
| 306 |
+
cur_BS = depreshuffle_scales(smem_BS.index(slot_c), BLOCK_SIZE_N, K_GROUPS).load(
|
| 307 |
+
layout=b_scale_layout
|
| 308 |
+
)
|
| 309 |
+
|
| 310 |
+
# --- 3. Main loop: WMMA(cur) → TDM(future) → wait → pre-load(next) ---
|
| 311 |
+
main_iters = k_tiles - (NUM_BUFFERS)
|
| 312 |
+
for _ in range(main_iters):
|
| 313 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 314 |
+
cur_A, cur_AS, "e2m1", cur_B, cur_BS, "e2m1", acc
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
# TDM load next tile (descriptors are already positioned by
|
| 318 |
+
# the previous iter's / prologue's trailing update_tensor_descriptor)
|
| 319 |
+
slot = load_idx % NUM_BUFFERS
|
| 320 |
+
|
| 321 |
+
a_slot = smem_A.index(slot)
|
| 322 |
+
b_slot = smem_B.index(slot)
|
| 323 |
+
as_slot = smem_AS.index(slot)
|
| 324 |
+
bs_slot = smem_BS.index(slot)
|
| 325 |
+
off_a = load_idx * BLOCK_K_BYTES
|
| 326 |
+
off_b = load_idx * BLOCK_K_BYTES * 16
|
| 327 |
+
off_s = load_idx * K_GROUPS * PRESHUFFLE_FACTOR
|
| 328 |
+
gl.amd.gfx1250.tdm.async_load(a_desc, [0, off_a], a_slot)
|
| 329 |
+
gl.amd.gfx1250.tdm.async_load(b_desc, [0, off_b], b_slot)
|
| 330 |
+
gl.amd.gfx1250.tdm.async_load(as_desc, [0, off_s], as_slot)
|
| 331 |
+
gl.amd.gfx1250.tdm.async_load(bs_desc, [0, off_s], bs_slot)
|
| 332 |
+
|
| 333 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * 4)
|
| 334 |
+
load_idx += 1
|
| 335 |
+
|
| 336 |
+
# Pre-load next tile from LDS into registers
|
| 337 |
+
next_slot = (compute_idx + 1) % NUM_BUFFERS
|
| 338 |
+
cur_A = smem_A.index(next_slot).load(layout=dot_a_layout)
|
| 339 |
+
cur_B = depreshuffle_b_raw_to_kn(
|
| 340 |
+
smem_B.index(next_slot),
|
| 341 |
+
BLOCK_N=BLOCK_SIZE_N,
|
| 342 |
+
BLOCK_K_BYTES=BLOCK_K_BYTES,
|
| 343 |
+
).load(layout=dot_b_layout)
|
| 344 |
+
cur_AS = depreshuffle_scales(
|
| 345 |
+
smem_AS.index(next_slot), BLOCK_SIZE_M, K_GROUPS
|
| 346 |
+
).load(layout=a_scale_layout)
|
| 347 |
+
cur_BS = depreshuffle_scales(
|
| 348 |
+
smem_BS.index(next_slot), BLOCK_SIZE_N, K_GROUPS
|
| 349 |
+
).load(layout=b_scale_layout)
|
| 350 |
+
compute_idx += 1
|
| 351 |
+
|
| 352 |
+
# --- 4. Epilogue: drain remaining tiles (no new TDM loads) ---
|
| 353 |
+
for i in gl.static_range(NUM_BUFFERS - 1):
|
| 354 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 355 |
+
cur_A, cur_AS, "e2m1", cur_B, cur_BS, "e2m1", acc
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 2 - i) * 4)
|
| 359 |
+
|
| 360 |
+
next_slot = (compute_idx + 1) % NUM_BUFFERS
|
| 361 |
+
cur_A = smem_A.index(next_slot).load(layout=dot_a_layout)
|
| 362 |
+
cur_B = depreshuffle_b_raw_to_kn(
|
| 363 |
+
smem_B.index(next_slot),
|
| 364 |
+
BLOCK_N=BLOCK_SIZE_N,
|
| 365 |
+
BLOCK_K_BYTES=BLOCK_K_BYTES,
|
| 366 |
+
).load(layout=dot_b_layout)
|
| 367 |
+
cur_AS = depreshuffle_scales(
|
| 368 |
+
smem_AS.index(next_slot), BLOCK_SIZE_M, K_GROUPS
|
| 369 |
+
).load(layout=a_scale_layout)
|
| 370 |
+
cur_BS = depreshuffle_scales(
|
| 371 |
+
smem_BS.index(next_slot), BLOCK_SIZE_N, K_GROUPS
|
| 372 |
+
).load(layout=b_scale_layout)
|
| 373 |
+
compute_idx += 1
|
| 374 |
+
|
| 375 |
+
# --- 5. Final WMMA ---
|
| 376 |
+
acc = gl.amd.gfx1250.wmma_scaled(cur_A, cur_AS, "e2m1", cur_B, cur_BS, "e2m1", acc)
|
| 377 |
+
|
| 378 |
+
# =====================================================================
|
| 379 |
+
# Store output via TDM: accumulator → shared memory → global memory.
|
| 380 |
+
# =====================================================================
|
| 381 |
+
|
| 382 |
+
c_buffer = gl.allocate_shared_memory(
|
| 383 |
+
c_ptr.type.element_ty,
|
| 384 |
+
shape=[BLOCK_SIZE_M, BLOCK_SIZE_N],
|
| 385 |
+
layout=shared_C,
|
| 386 |
+
)
|
| 387 |
+
c_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 388 |
+
base=c_ptr,
|
| 389 |
+
shape=(M, N),
|
| 390 |
+
strides=(stride_c_m, stride_c_n),
|
| 391 |
+
block_shape=(BLOCK_SIZE_M, BLOCK_SIZE_N),
|
| 392 |
+
layout=shared_C,
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
c_buffer.store(acc.to(c_ptr.type.element_ty))
|
| 396 |
+
|
| 397 |
+
gl.amd.gfx1250.tdm.async_store(
|
| 398 |
+
c_desc, [tile_m * BLOCK_SIZE_M, tile_n * BLOCK_SIZE_N], c_buffer
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
build/torch-rocm/_gluon_kernels/gfx1250/moe/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_gluon_kernels/gfx1250/moe/moe_op_gemm_a8w4.py
ADDED
|
@@ -0,0 +1,1170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
from triton.experimental import gluon
|
| 4 |
+
import triton.experimental.gluon.language as gl
|
| 5 |
+
from ....utils._triton.pid_preprocessing import remap_xcd, pid_grid
|
| 6 |
+
from ...._triton_kernels.moe.quant_moe import _compute_static_fp8_quant
|
| 7 |
+
from ...._triton_kernels.moe.activations import _swiglu
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def matmul_launch_metadata(grid, kernel, args):
|
| 11 |
+
ret = dict()
|
| 12 |
+
M, N, K = None, args["N"], args["K"]
|
| 13 |
+
Y, X, W = args["Y"], args["X"], args["W"]
|
| 14 |
+
hist = args["ExptHist"]
|
| 15 |
+
if hist is not None:
|
| 16 |
+
n_rows = int(hist.float().mean())
|
| 17 |
+
n_tokens = float(hist.sum())
|
| 18 |
+
n_w_bytes = (W.numel() * W.element_size() // hist.numel()) * (hist > 0).sum()
|
| 19 |
+
else:
|
| 20 |
+
n_tokens = None
|
| 21 |
+
n_w_bytes = W.numel() * W.element_size()
|
| 22 |
+
|
| 23 |
+
def repr(s, x):
|
| 24 |
+
return f"{s}={x}" if x is not None else f"E_{len(hist)}({s})={n_rows}"
|
| 25 |
+
|
| 26 |
+
nbits = X.dtype.itemsize * 8
|
| 27 |
+
ret["name"] = f"{kernel.name} [{repr('M', M)}, {repr('N', N)}, {repr('K', K)}]"
|
| 28 |
+
gindx = args.get("GatherIndx", None)
|
| 29 |
+
if gindx is not None:
|
| 30 |
+
gindx = gindx.to(torch.int32)
|
| 31 |
+
ret["name"] += "_layer1"
|
| 32 |
+
else:
|
| 33 |
+
ret["name"] += "_layer2"
|
| 34 |
+
if args["B"] is not None:
|
| 35 |
+
ret["name"] += "_bias"
|
| 36 |
+
if args["APPLY_SWIGLU"]:
|
| 37 |
+
ret["name"] += "_swiglu"
|
| 38 |
+
if args["Quant_static_scale"] is not None:
|
| 39 |
+
ret["name"] += "_quant"
|
| 40 |
+
|
| 41 |
+
fM = n_tokens
|
| 42 |
+
fK = K if K is not None else n_tokens
|
| 43 |
+
ret[f"flops{nbits}"] = 2.0 * fM * N * fK
|
| 44 |
+
|
| 45 |
+
n_x_bytes = X.numel() * X.element_size()
|
| 46 |
+
n_y_bytes = Y.numel() * Y.element_size()
|
| 47 |
+
if hist is not None:
|
| 48 |
+
assert n_tokens is not None
|
| 49 |
+
n_expts_act = args["N_EXPTS_ACT"]
|
| 50 |
+
|
| 51 |
+
if gindx is not None:
|
| 52 |
+
# recreate inverse GatherIndx.
|
| 53 |
+
dst = torch.full_like(gindx, -1)
|
| 54 |
+
idx = torch.arange(len(gindx), device=gindx.device, dtype=torch.int32)
|
| 55 |
+
mask = gindx != -1
|
| 56 |
+
dst[gindx[mask]] = idx[mask]
|
| 57 |
+
n_read_rows = (dst.view((-1, n_expts_act)) != -1).any(dim=1).sum()
|
| 58 |
+
else:
|
| 59 |
+
n_read_rows = n_tokens
|
| 60 |
+
n_x_bytes = n_read_rows * X.shape[-1] * X.element_size()
|
| 61 |
+
n_y_bytes = n_tokens * Y.shape[-1] * Y.element_size()
|
| 62 |
+
ret["bytes"] = int(n_x_bytes + n_y_bytes + n_w_bytes)
|
| 63 |
+
|
| 64 |
+
return ret
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
@gluon.jit
|
| 68 |
+
def unswizzle_mx_scale_gfx1250(
|
| 69 |
+
scale_buffer_slice, BLOCK_N, MX_SCALE_BLOCK_K, PRESHUFFLE_FACTOR, SCALE_KWIDTH
|
| 70 |
+
):
|
| 71 |
+
scale_buffer_slice = (
|
| 72 |
+
scale_buffer_slice.reshape(
|
| 73 |
+
(
|
| 74 |
+
BLOCK_N // PRESHUFFLE_FACTOR,
|
| 75 |
+
MX_SCALE_BLOCK_K // SCALE_KWIDTH,
|
| 76 |
+
PRESHUFFLE_FACTOR,
|
| 77 |
+
SCALE_KWIDTH,
|
| 78 |
+
)
|
| 79 |
+
)
|
| 80 |
+
.permute((0, 2, 1, 3))
|
| 81 |
+
.reshape((BLOCK_N, MX_SCALE_BLOCK_K))
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
return scale_buffer_slice
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@gluon.jit(launch_metadata=matmul_launch_metadata, do_not_specialize=["num_tokens"])
|
| 88 |
+
def _moe_gemm_a8w4_decode(
|
| 89 |
+
Y,
|
| 90 |
+
stride_y_m,
|
| 91 |
+
stride_y_n,
|
| 92 |
+
X,
|
| 93 |
+
stride_x_m,
|
| 94 |
+
stride_x_k,
|
| 95 |
+
XMxScale,
|
| 96 |
+
stride_x_mx_m,
|
| 97 |
+
stride_x_mx_k,
|
| 98 |
+
W,
|
| 99 |
+
stride_w_e,
|
| 100 |
+
stride_w_n,
|
| 101 |
+
stride_w_k,
|
| 102 |
+
WMxScale,
|
| 103 |
+
stride_w_mx_e,
|
| 104 |
+
stride_w_mx_n,
|
| 105 |
+
stride_w_mx_k,
|
| 106 |
+
X_static_scale,
|
| 107 |
+
Quant_static_scale,
|
| 108 |
+
B,
|
| 109 |
+
stride_b_e, # Bias
|
| 110 |
+
Gammas,
|
| 111 |
+
num_tokens,
|
| 112 |
+
N,
|
| 113 |
+
K, # shapes
|
| 114 |
+
# expt data
|
| 115 |
+
GatherIndx,
|
| 116 |
+
ExptHist,
|
| 117 |
+
ExptOffs,
|
| 118 |
+
ExptOffsSum,
|
| 119 |
+
ExptData,
|
| 120 |
+
# true grid size
|
| 121 |
+
grid_m,
|
| 122 |
+
grid_n,
|
| 123 |
+
# fused activation function
|
| 124 |
+
APPLY_SWIGLU: gl.constexpr,
|
| 125 |
+
alpha,
|
| 126 |
+
limit,
|
| 127 |
+
ACTIVATION_REDUCTION_N: gl.constexpr,
|
| 128 |
+
SWIGLU_ADD_RESIDUAL: gl.constexpr,
|
| 129 |
+
# MoE config
|
| 130 |
+
N_EXPTS_ACT: gl.constexpr,
|
| 131 |
+
# optimization config
|
| 132 |
+
BLOCK_M: gl.constexpr,
|
| 133 |
+
BLOCK_N: gl.constexpr,
|
| 134 |
+
BLOCK_K: gl.constexpr,
|
| 135 |
+
XCD_SWIZZLE: gl.constexpr,
|
| 136 |
+
NUM_BUFFERS: gl.constexpr,
|
| 137 |
+
# One of ["GFX1250", None]
|
| 138 |
+
SWIZZLE_MX_SCALE: gl.constexpr,
|
| 139 |
+
MASK_K_LIMIT: gl.constexpr,
|
| 140 |
+
W_CACHE_MODIFIER: gl.constexpr,
|
| 141 |
+
num_warps: gl.constexpr,
|
| 142 |
+
UPCAST_INDICES: gl.constexpr = False,
|
| 143 |
+
):
|
| 144 |
+
|
| 145 |
+
is_x_microscaled: gl.constexpr = XMxScale is not None
|
| 146 |
+
MX_PACK_DIVISOR: gl.constexpr = 32
|
| 147 |
+
NUM_TDM_OPS: gl.constexpr = 4 if is_x_microscaled else 3
|
| 148 |
+
w_type: gl.constexpr = W.dtype.element_ty
|
| 149 |
+
gl.static_assert(w_type == gl.uint8, "mx_weight_ptr must be uint8 or fp8")
|
| 150 |
+
gl.static_assert(
|
| 151 |
+
WMxScale.dtype.element_ty == gl.uint8, "mx_scale_ptr must be uint8"
|
| 152 |
+
)
|
| 153 |
+
gl.static_assert(
|
| 154 |
+
BLOCK_K % MX_PACK_DIVISOR == 0, "BLOCK_K must be a multiple of MX_PACK_DIVISOR"
|
| 155 |
+
)
|
| 156 |
+
x_type: gl.constexpr = X.dtype.element_ty
|
| 157 |
+
if is_x_microscaled:
|
| 158 |
+
gl.static_assert(x_type == gl.float8e4nv, "mx_act_ptr must be float8e4nv")
|
| 159 |
+
gl.static_assert(
|
| 160 |
+
XMxScale.dtype.element_ty == gl.uint8, "mx_scale_ptr must be uint8"
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
OUT_BLOCK_N: tl.constexpr = BLOCK_N // ACTIVATION_REDUCTION_N
|
| 164 |
+
yN = N // ACTIVATION_REDUCTION_N
|
| 165 |
+
|
| 166 |
+
pid = gl.program_id(0)
|
| 167 |
+
|
| 168 |
+
index_type: tl.constexpr = gl.int64 if UPCAST_INDICES else gl.int32
|
| 169 |
+
|
| 170 |
+
if XCD_SWIZZLE != 1:
|
| 171 |
+
padding_m = grid_m - gl.load(ExptOffsSum)
|
| 172 |
+
unpadded_m = grid_m - padding_m
|
| 173 |
+
total_actual_tiles = unpadded_m * grid_n
|
| 174 |
+
if padding_m > 0 and pid >= total_actual_tiles:
|
| 175 |
+
return
|
| 176 |
+
pid = remap_xcd(pid, total_actual_tiles, XCD_SWIZZLE)
|
| 177 |
+
else:
|
| 178 |
+
unpadded_m = grid_m
|
| 179 |
+
pid_m, pid_n = pid_grid(pid, unpadded_m, grid_n, 1)
|
| 180 |
+
# unpack expert data
|
| 181 |
+
expt_data = gl.load(ExptData + pid_m)
|
| 182 |
+
if XCD_SWIZZLE == 1 and expt_data == -1:
|
| 183 |
+
return
|
| 184 |
+
expt_id = expt_data & 0x0000FFFF
|
| 185 |
+
block_id = expt_data >> 16
|
| 186 |
+
M = gl.load(ExptHist + expt_id)
|
| 187 |
+
start_m = gl.load(ExptOffs + expt_id)
|
| 188 |
+
expt_id, block_id = expt_id.to(index_type), block_id.to(index_type)
|
| 189 |
+
start_m = start_m.to(index_type)
|
| 190 |
+
pid_n = pid_n.to(index_type)
|
| 191 |
+
|
| 192 |
+
# A pointers
|
| 193 |
+
off_x_m = BLOCK_M * block_id
|
| 194 |
+
if GatherIndx is None:
|
| 195 |
+
X += start_m * stride_x_m
|
| 196 |
+
else:
|
| 197 |
+
if GatherIndx.dtype.element_ty == gl.uint16:
|
| 198 |
+
IDX_LAYOUT: gl.constexpr = gl.SliceLayout(
|
| 199 |
+
0, gl.BlockedLayout([1, 16], [32, 1], [1, num_warps], [0, 1])
|
| 200 |
+
)
|
| 201 |
+
oob_idx = (num_tokens).to(gl.uint16)
|
| 202 |
+
else:
|
| 203 |
+
gl.static_assert(
|
| 204 |
+
GatherIndx.dtype.element_ty == gl.int32,
|
| 205 |
+
"Gather index datatype should be uint16 or int32",
|
| 206 |
+
)
|
| 207 |
+
IDX_LAYOUT: gl.constexpr = gl.SliceLayout(
|
| 208 |
+
0, gl.BlockedLayout([1, 8], [32, 1], [1, num_warps], [0, 1])
|
| 209 |
+
)
|
| 210 |
+
oob_idx = num_tokens
|
| 211 |
+
offs_x_m = BLOCK_M * block_id + gl.arange(0, BLOCK_M, layout=IDX_LAYOUT)
|
| 212 |
+
mask_idx = offs_x_m < M
|
| 213 |
+
offs_x_m = offs_x_m % M
|
| 214 |
+
GatherIndx += start_m
|
| 215 |
+
offs_x_m = gl.load(GatherIndx + offs_x_m) // N_EXPTS_ACT
|
| 216 |
+
offs_x_m = gl.where(mask_idx, offs_x_m, oob_idx)
|
| 217 |
+
|
| 218 |
+
W_K_DIVISOR: gl.constexpr = 2
|
| 219 |
+
PACKED_BLOCK_K_W: gl.constexpr = BLOCK_K // W_K_DIVISOR
|
| 220 |
+
PACKED_BLOCK_N_W: gl.constexpr = BLOCK_N
|
| 221 |
+
MX_SCALE_BLOCK_K: gl.constexpr = BLOCK_K // MX_PACK_DIVISOR
|
| 222 |
+
|
| 223 |
+
WMxScale += expt_id * stride_w_mx_e
|
| 224 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 225 |
+
gl.static_assert(stride_w_mx_k is not None)
|
| 226 |
+
gl.static_assert(stride_w_mx_n is not None)
|
| 227 |
+
PRESHUFFLE_FACTOR: gl.constexpr = 32
|
| 228 |
+
PACKED_MX_BLOCK: gl.constexpr = MX_SCALE_BLOCK_K * PRESHUFFLE_FACTOR
|
| 229 |
+
SCALE_BLOCK_N: gl.constexpr = BLOCK_N // PRESHUFFLE_FACTOR
|
| 230 |
+
SCALE_KWIDTH: gl.constexpr = 8
|
| 231 |
+
else:
|
| 232 |
+
PRESHUFFLE_FACTOR: gl.constexpr = 1
|
| 233 |
+
PACKED_MX_BLOCK: gl.constexpr = MX_SCALE_BLOCK_K
|
| 234 |
+
SCALE_BLOCK_N: gl.constexpr = BLOCK_N
|
| 235 |
+
|
| 236 |
+
# B pointers
|
| 237 |
+
off_w_n_scale = pid_n * SCALE_BLOCK_N
|
| 238 |
+
off_w_n = pid_n * PACKED_BLOCK_N_W
|
| 239 |
+
W += expt_id * stride_w_e
|
| 240 |
+
|
| 241 |
+
SHARED_LAYOUT_X: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 242 |
+
[[BLOCK_K, 16]], [BLOCK_M, BLOCK_K], [1, 0]
|
| 243 |
+
)
|
| 244 |
+
if BLOCK_K <= 256:
|
| 245 |
+
SHARED_LAYOUT_W: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 246 |
+
[[256, 16]], [BLOCK_N, PACKED_BLOCK_K_W], [1, 0]
|
| 247 |
+
)
|
| 248 |
+
else:
|
| 249 |
+
SHARED_LAYOUT_W: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 250 |
+
[[PACKED_BLOCK_K_W, 16]], [BLOCK_N, PACKED_BLOCK_K_W], [1, 0]
|
| 251 |
+
)
|
| 252 |
+
SHARED_LAYOUT_W_SCALES: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 253 |
+
[[256, 16]], [SCALE_BLOCK_N, PACKED_MX_BLOCK], [1, 0]
|
| 254 |
+
)
|
| 255 |
+
if is_x_microscaled:
|
| 256 |
+
SHARED_LAYOUT_X_SCALES: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 257 |
+
[[256, 16]], [BLOCK_M, MX_SCALE_BLOCK_K], [1, 0]
|
| 258 |
+
)
|
| 259 |
+
if Quant_static_scale is not None:
|
| 260 |
+
SHARED_LAYOUT_Y: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 261 |
+
[[OUT_BLOCK_N, 16]], [BLOCK_M, OUT_BLOCK_N], [1, 0]
|
| 262 |
+
)
|
| 263 |
+
else:
|
| 264 |
+
SHARED_LAYOUT_Y: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 265 |
+
[[OUT_BLOCK_N, 8]], [BLOCK_M, OUT_BLOCK_N], [1, 0]
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
if GatherIndx is None:
|
| 269 |
+
x_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 270 |
+
base=X,
|
| 271 |
+
shape=(M, K),
|
| 272 |
+
strides=(stride_x_m, stride_x_k),
|
| 273 |
+
block_shape=(BLOCK_M, BLOCK_K),
|
| 274 |
+
layout=SHARED_LAYOUT_X,
|
| 275 |
+
)
|
| 276 |
+
else:
|
| 277 |
+
x_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 278 |
+
base=X,
|
| 279 |
+
shape=(num_tokens, K),
|
| 280 |
+
strides=(stride_x_m, stride_x_k),
|
| 281 |
+
block_shape=(BLOCK_M, BLOCK_K),
|
| 282 |
+
layout=SHARED_LAYOUT_X,
|
| 283 |
+
)
|
| 284 |
+
w_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 285 |
+
base=W,
|
| 286 |
+
shape=(N, K // W_K_DIVISOR),
|
| 287 |
+
strides=(
|
| 288 |
+
stride_w_n,
|
| 289 |
+
stride_w_k,
|
| 290 |
+
),
|
| 291 |
+
block_shape=(
|
| 292 |
+
BLOCK_N,
|
| 293 |
+
PACKED_BLOCK_K_W,
|
| 294 |
+
),
|
| 295 |
+
layout=SHARED_LAYOUT_W,
|
| 296 |
+
)
|
| 297 |
+
w_scales_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 298 |
+
base=WMxScale,
|
| 299 |
+
shape=(N // PRESHUFFLE_FACTOR, tl.cdiv(K, MX_PACK_DIVISOR) * PRESHUFFLE_FACTOR),
|
| 300 |
+
strides=(stride_w_mx_n, stride_w_mx_k),
|
| 301 |
+
block_shape=(SCALE_BLOCK_N, PACKED_MX_BLOCK),
|
| 302 |
+
layout=SHARED_LAYOUT_W_SCALES,
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
if is_x_microscaled:
|
| 306 |
+
if GatherIndx is None:
|
| 307 |
+
XMxScale += start_m * stride_x_mx_m
|
| 308 |
+
x_scales_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 309 |
+
base=XMxScale,
|
| 310 |
+
shape=(M, tl.cdiv(K, MX_PACK_DIVISOR)),
|
| 311 |
+
strides=(stride_x_mx_m, stride_x_mx_k),
|
| 312 |
+
block_shape=(BLOCK_M, MX_SCALE_BLOCK_K),
|
| 313 |
+
layout=SHARED_LAYOUT_X_SCALES,
|
| 314 |
+
)
|
| 315 |
+
else:
|
| 316 |
+
x_scales_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 317 |
+
base=XMxScale,
|
| 318 |
+
shape=(num_tokens, tl.cdiv(K, MX_PACK_DIVISOR)),
|
| 319 |
+
strides=(stride_x_mx_m, stride_x_mx_k),
|
| 320 |
+
block_shape=(BLOCK_M, MX_SCALE_BLOCK_K),
|
| 321 |
+
layout=SHARED_LAYOUT_X_SCALES,
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
WMMA_LAYOUT: gl.constexpr = gl.amd.AMDWMMALayout(
|
| 325 |
+
3,
|
| 326 |
+
transposed=True,
|
| 327 |
+
warp_bases=[[0, 1], [0, 2]],
|
| 328 |
+
reg_bases=[],
|
| 329 |
+
instr_shape=[16, 16, 128],
|
| 330 |
+
)
|
| 331 |
+
WMMA_LAYOUT_PACKED: gl.constexpr = gl.amd.AMDWMMALayout(
|
| 332 |
+
3,
|
| 333 |
+
transposed=True,
|
| 334 |
+
warp_bases=[[0, 1], [0, 2]],
|
| 335 |
+
reg_bases=[],
|
| 336 |
+
instr_shape=[16, 16, 64],
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
DOT_LAYOUT_X: gl.constexpr = gl.DotOperandLayout(0, WMMA_LAYOUT, k_width=16)
|
| 340 |
+
DOT_LAYOUT_W: gl.constexpr = gl.DotOperandLayout(1, WMMA_LAYOUT_PACKED, k_width=16)
|
| 341 |
+
DOT_LAYOUT_W_SCALES: gl.constexpr = gl.amd.gfx1250.get_wmma_scale_layout(
|
| 342 |
+
DOT_LAYOUT_W, [BLOCK_N, MX_SCALE_BLOCK_K]
|
| 343 |
+
)
|
| 344 |
+
if is_x_microscaled:
|
| 345 |
+
DOT_LAYOUT_X_SCALES: gl.constexpr = gl.amd.gfx1250.get_wmma_scale_layout(
|
| 346 |
+
DOT_LAYOUT_X, [BLOCK_M, MX_SCALE_BLOCK_K]
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
x_buffer = gl.allocate_shared_memory(
|
| 350 |
+
x_desc.dtype, shape=[NUM_BUFFERS] + x_desc.block_shape, layout=x_desc.layout
|
| 351 |
+
)
|
| 352 |
+
w_buffer = gl.allocate_shared_memory(
|
| 353 |
+
w_desc.dtype, shape=[NUM_BUFFERS] + w_desc.block_shape, layout=w_desc.layout
|
| 354 |
+
)
|
| 355 |
+
w_scales_buffer = gl.allocate_shared_memory(
|
| 356 |
+
w_scales_desc.dtype,
|
| 357 |
+
shape=[NUM_BUFFERS] + w_scales_desc.block_shape,
|
| 358 |
+
layout=w_scales_desc.layout,
|
| 359 |
+
)
|
| 360 |
+
if is_x_microscaled:
|
| 361 |
+
x_scales_buffer = gl.allocate_shared_memory(
|
| 362 |
+
x_scales_desc.dtype,
|
| 363 |
+
shape=[NUM_BUFFERS] + x_scales_desc.block_shape,
|
| 364 |
+
layout=x_scales_desc.layout,
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
read_idx = 0
|
| 368 |
+
write_idx = 0
|
| 369 |
+
for _ in gl.static_range(NUM_BUFFERS - 1):
|
| 370 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 371 |
+
w_desc,
|
| 372 |
+
[off_w_n, write_idx * PACKED_BLOCK_K_W],
|
| 373 |
+
w_buffer.index(write_idx % NUM_BUFFERS),
|
| 374 |
+
)
|
| 375 |
+
if GatherIndx is None:
|
| 376 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 377 |
+
x_desc,
|
| 378 |
+
[off_x_m, write_idx * BLOCK_K],
|
| 379 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 380 |
+
)
|
| 381 |
+
else:
|
| 382 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 383 |
+
x_desc,
|
| 384 |
+
offs_x_m,
|
| 385 |
+
write_idx * BLOCK_K,
|
| 386 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 387 |
+
)
|
| 388 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 389 |
+
w_scales_desc,
|
| 390 |
+
[off_w_n_scale, write_idx * PACKED_MX_BLOCK],
|
| 391 |
+
w_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 392 |
+
)
|
| 393 |
+
if is_x_microscaled:
|
| 394 |
+
if GatherIndx is None:
|
| 395 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 396 |
+
x_scales_desc,
|
| 397 |
+
[off_x_m, write_idx * MX_SCALE_BLOCK_K],
|
| 398 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 399 |
+
)
|
| 400 |
+
else:
|
| 401 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 402 |
+
x_scales_desc,
|
| 403 |
+
offs_x_m,
|
| 404 |
+
write_idx * MX_SCALE_BLOCK_K,
|
| 405 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 406 |
+
)
|
| 407 |
+
write_idx += 1
|
| 408 |
+
|
| 409 |
+
num_k_iter = tl.cdiv(K, BLOCK_K)
|
| 410 |
+
acc = gl.zeros((BLOCK_M, BLOCK_N), dtype=gl.float32, layout=WMMA_LAYOUT)
|
| 411 |
+
for k in range(num_k_iter - (NUM_BUFFERS - 1)):
|
| 412 |
+
|
| 413 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 414 |
+
w_desc,
|
| 415 |
+
[off_w_n, write_idx * PACKED_BLOCK_K_W],
|
| 416 |
+
w_buffer.index(write_idx % NUM_BUFFERS),
|
| 417 |
+
)
|
| 418 |
+
if GatherIndx is None:
|
| 419 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 420 |
+
x_desc,
|
| 421 |
+
[off_x_m, write_idx * BLOCK_K],
|
| 422 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 423 |
+
)
|
| 424 |
+
else:
|
| 425 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 426 |
+
x_desc,
|
| 427 |
+
offs_x_m,
|
| 428 |
+
write_idx * BLOCK_K,
|
| 429 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 430 |
+
)
|
| 431 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 432 |
+
w_scales_desc,
|
| 433 |
+
[off_w_n_scale, write_idx * PACKED_MX_BLOCK],
|
| 434 |
+
w_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 435 |
+
)
|
| 436 |
+
if is_x_microscaled:
|
| 437 |
+
if GatherIndx is None:
|
| 438 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 439 |
+
x_scales_desc,
|
| 440 |
+
[off_x_m, write_idx * MX_SCALE_BLOCK_K],
|
| 441 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 442 |
+
)
|
| 443 |
+
else:
|
| 444 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 445 |
+
x_scales_desc,
|
| 446 |
+
offs_x_m,
|
| 447 |
+
write_idx * MX_SCALE_BLOCK_K,
|
| 448 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 449 |
+
)
|
| 450 |
+
write_idx += 1
|
| 451 |
+
|
| 452 |
+
gl.amd.gfx1250.tdm.async_wait(NUM_BUFFERS * NUM_TDM_OPS - 1)
|
| 453 |
+
cur_w = (
|
| 454 |
+
w_buffer.index(read_idx % NUM_BUFFERS)
|
| 455 |
+
.permute((1, 0))
|
| 456 |
+
.load(layout=DOT_LAYOUT_W)
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * NUM_TDM_OPS)
|
| 460 |
+
cur_x = x_buffer.index(read_idx % NUM_BUFFERS).load(layout=DOT_LAYOUT_X)
|
| 461 |
+
w_scales_buffer_slice = w_scales_buffer.index(read_idx % NUM_BUFFERS)
|
| 462 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 463 |
+
w_scales_buffer_slice = unswizzle_mx_scale_gfx1250(
|
| 464 |
+
w_scales_buffer_slice,
|
| 465 |
+
BLOCK_N,
|
| 466 |
+
MX_SCALE_BLOCK_K,
|
| 467 |
+
PRESHUFFLE_FACTOR,
|
| 468 |
+
SCALE_KWIDTH,
|
| 469 |
+
)
|
| 470 |
+
cur_w_scales = w_scales_buffer_slice.load(layout=DOT_LAYOUT_W_SCALES)
|
| 471 |
+
if is_x_microscaled:
|
| 472 |
+
cur_x_scales = x_scales_buffer.index(read_idx % NUM_BUFFERS).load(
|
| 473 |
+
layout=DOT_LAYOUT_X_SCALES
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
read_idx += 1
|
| 477 |
+
|
| 478 |
+
if is_x_microscaled:
|
| 479 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 480 |
+
cur_x, cur_x_scales, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 481 |
+
)
|
| 482 |
+
else:
|
| 483 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 484 |
+
cur_x, 0, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
# bias
|
| 488 |
+
if B is not None:
|
| 489 |
+
BPtrs = B + expt_id * stride_b_e
|
| 490 |
+
SHARED_LAYOUT_BIAS: gl.constexpr = gl.SwizzledSharedLayout(1, 1, 1, [1, 0])
|
| 491 |
+
bias_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 492 |
+
base=BPtrs,
|
| 493 |
+
shape=(1, N),
|
| 494 |
+
strides=(N, 1),
|
| 495 |
+
block_shape=(1, BLOCK_N),
|
| 496 |
+
layout=SHARED_LAYOUT_BIAS,
|
| 497 |
+
)
|
| 498 |
+
bias_buffer = gl.allocate_shared_memory(
|
| 499 |
+
bias_desc.dtype, shape=[1, BLOCK_N], layout=bias_desc.layout
|
| 500 |
+
)
|
| 501 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 502 |
+
bias_desc,
|
| 503 |
+
[0, pid_n * BLOCK_N],
|
| 504 |
+
bias_buffer,
|
| 505 |
+
)
|
| 506 |
+
TDM_BIAS_WAIT: gl.constexpr = 1
|
| 507 |
+
else:
|
| 508 |
+
TDM_BIAS_WAIT: gl.constexpr = 0
|
| 509 |
+
|
| 510 |
+
# Epilogue: drain remaining pipeline stages (no new TDM loads).
|
| 511 |
+
# The first NUM_BUFFERS-1 iterations still use the pre-load / WMMA pattern.
|
| 512 |
+
for k_ep in gl.static_range(NUM_BUFFERS - 1):
|
| 513 |
+
|
| 514 |
+
gl.amd.gfx1250.tdm.async_wait(
|
| 515 |
+
(NUM_BUFFERS - 1 - k_ep) * NUM_TDM_OPS - 1 + TDM_BIAS_WAIT
|
| 516 |
+
)
|
| 517 |
+
|
| 518 |
+
cur_w = (
|
| 519 |
+
w_buffer.index(read_idx % NUM_BUFFERS)
|
| 520 |
+
.permute((1, 0))
|
| 521 |
+
.load(layout=DOT_LAYOUT_W)
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
gl.amd.gfx1250.tdm.async_wait(
|
| 525 |
+
(NUM_BUFFERS - 2 - k_ep) * NUM_TDM_OPS + TDM_BIAS_WAIT
|
| 526 |
+
)
|
| 527 |
+
|
| 528 |
+
cur_x = x_buffer.index(read_idx % NUM_BUFFERS).load(layout=DOT_LAYOUT_X)
|
| 529 |
+
w_scales_buffer_slice = w_scales_buffer.index(read_idx % NUM_BUFFERS)
|
| 530 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 531 |
+
w_scales_buffer_slice = unswizzle_mx_scale_gfx1250(
|
| 532 |
+
w_scales_buffer_slice,
|
| 533 |
+
BLOCK_N,
|
| 534 |
+
MX_SCALE_BLOCK_K,
|
| 535 |
+
PRESHUFFLE_FACTOR,
|
| 536 |
+
SCALE_KWIDTH,
|
| 537 |
+
)
|
| 538 |
+
cur_w_scales = w_scales_buffer_slice.load(layout=DOT_LAYOUT_W_SCALES)
|
| 539 |
+
if is_x_microscaled:
|
| 540 |
+
cur_x_scales = x_scales_buffer.index(read_idx % NUM_BUFFERS).load(
|
| 541 |
+
layout=DOT_LAYOUT_X_SCALES
|
| 542 |
+
)
|
| 543 |
+
|
| 544 |
+
read_idx += 1
|
| 545 |
+
|
| 546 |
+
if is_x_microscaled:
|
| 547 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 548 |
+
cur_x, cur_x_scales, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 549 |
+
)
|
| 550 |
+
else:
|
| 551 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 552 |
+
cur_x, 0, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
# scalar fp8 scale
|
| 556 |
+
if X_static_scale is not None:
|
| 557 |
+
acc = acc * gl.load(X_static_scale)
|
| 558 |
+
|
| 559 |
+
if B is not None:
|
| 560 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 561 |
+
bias = bias_buffer.reshape((BLOCK_N,)).load(
|
| 562 |
+
layout=gl.SliceLayout(0, WMMA_LAYOUT)
|
| 563 |
+
)
|
| 564 |
+
acc = acc + bias[None, :]
|
| 565 |
+
|
| 566 |
+
if APPLY_SWIGLU:
|
| 567 |
+
out = _swiglu(acc, alpha, limit, ADD_RESIDUAL=SWIGLU_ADD_RESIDUAL)
|
| 568 |
+
tl.static_assert(
|
| 569 |
+
out.shape[1] == OUT_BLOCK_N,
|
| 570 |
+
f"Activation fn out.shape[1] ({out.shape[1]}) doesn't match computed OUT_BLOCK_N ({OUT_BLOCK_N})",
|
| 571 |
+
)
|
| 572 |
+
else:
|
| 573 |
+
tl.static_assert(
|
| 574 |
+
ACTIVATION_REDUCTION_N == 1,
|
| 575 |
+
"Activation reduction must be 1 if no activation fn is provided",
|
| 576 |
+
)
|
| 577 |
+
out = acc
|
| 578 |
+
|
| 579 |
+
if Gammas is not None:
|
| 580 |
+
offs_m = BLOCK_M * block_id + gl.arange(0, BLOCK_M)
|
| 581 |
+
mask_m = offs_m < M
|
| 582 |
+
gammas = gl.amd.gfx1250.buffer_load(
|
| 583 |
+
Gammas + start_m, offs_m, mask=mask_m, other=0.0
|
| 584 |
+
)
|
| 585 |
+
out *= gammas[:, None]
|
| 586 |
+
|
| 587 |
+
# quant
|
| 588 |
+
if Quant_static_scale is not None:
|
| 589 |
+
out = _compute_static_fp8_quant(out, gl.load(Quant_static_scale))
|
| 590 |
+
else:
|
| 591 |
+
out = out.to(tl.bfloat16)
|
| 592 |
+
|
| 593 |
+
# TDM Store: accumulator → shared memory → global memory
|
| 594 |
+
Y += start_m * stride_y_m
|
| 595 |
+
y_buffer = gl.allocate_shared_memory(
|
| 596 |
+
Y.type.element_ty,
|
| 597 |
+
shape=[BLOCK_M, OUT_BLOCK_N],
|
| 598 |
+
layout=SHARED_LAYOUT_Y,
|
| 599 |
+
)
|
| 600 |
+
y_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 601 |
+
base=Y,
|
| 602 |
+
shape=(M, yN),
|
| 603 |
+
strides=(stride_y_m, stride_y_n),
|
| 604 |
+
block_shape=(BLOCK_M, OUT_BLOCK_N),
|
| 605 |
+
layout=SHARED_LAYOUT_Y,
|
| 606 |
+
)
|
| 607 |
+
y_buffer.store(out)
|
| 608 |
+
gl.amd.gfx1250.tdm.async_store(
|
| 609 |
+
y_desc, [block_id * BLOCK_M, pid_n * OUT_BLOCK_N], y_buffer
|
| 610 |
+
)
|
| 611 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
@gluon.jit(launch_metadata=matmul_launch_metadata)
|
| 615 |
+
def _moe_gemm_a8w4_prefill(
|
| 616 |
+
Y,
|
| 617 |
+
stride_y_m,
|
| 618 |
+
stride_y_n,
|
| 619 |
+
X,
|
| 620 |
+
stride_x_m,
|
| 621 |
+
stride_x_k,
|
| 622 |
+
XMxScale,
|
| 623 |
+
stride_x_mx_m,
|
| 624 |
+
stride_x_mx_k,
|
| 625 |
+
W,
|
| 626 |
+
stride_w_e,
|
| 627 |
+
stride_w_n,
|
| 628 |
+
stride_w_k,
|
| 629 |
+
WMxScale,
|
| 630 |
+
stride_w_mx_e,
|
| 631 |
+
stride_w_mx_n,
|
| 632 |
+
stride_w_mx_k,
|
| 633 |
+
X_static_scale,
|
| 634 |
+
Quant_static_scale,
|
| 635 |
+
B,
|
| 636 |
+
stride_b_e, # Bias
|
| 637 |
+
Gammas,
|
| 638 |
+
num_tokens,
|
| 639 |
+
N,
|
| 640 |
+
K, # shapes
|
| 641 |
+
# expt data
|
| 642 |
+
GatherIndx,
|
| 643 |
+
ExptHist,
|
| 644 |
+
ExptOffs,
|
| 645 |
+
ExptOffsSum,
|
| 646 |
+
ExptData,
|
| 647 |
+
# true grid size
|
| 648 |
+
grid_m,
|
| 649 |
+
grid_n,
|
| 650 |
+
# fused activation function
|
| 651 |
+
APPLY_SWIGLU: gl.constexpr,
|
| 652 |
+
alpha,
|
| 653 |
+
limit,
|
| 654 |
+
ACTIVATION_REDUCTION_N: gl.constexpr,
|
| 655 |
+
SWIGLU_ADD_RESIDUAL: gl.constexpr,
|
| 656 |
+
# MoE config
|
| 657 |
+
N_EXPTS_ACT: gl.constexpr,
|
| 658 |
+
# optimization config
|
| 659 |
+
BLOCK_M: gl.constexpr,
|
| 660 |
+
BLOCK_N: gl.constexpr,
|
| 661 |
+
BLOCK_K: gl.constexpr,
|
| 662 |
+
XCD_SWIZZLE: gl.constexpr,
|
| 663 |
+
NUM_BUFFERS: gl.constexpr,
|
| 664 |
+
# One of ["GFX1250", None]
|
| 665 |
+
SWIZZLE_MX_SCALE: gl.constexpr,
|
| 666 |
+
MASK_K_LIMIT: gl.constexpr,
|
| 667 |
+
W_CACHE_MODIFIER: gl.constexpr,
|
| 668 |
+
num_warps: gl.constexpr,
|
| 669 |
+
UPCAST_INDICES: gl.constexpr = False,
|
| 670 |
+
):
|
| 671 |
+
|
| 672 |
+
is_x_microscaled: gl.constexpr = XMxScale is not None
|
| 673 |
+
MX_PACK_DIVISOR: gl.constexpr = 32
|
| 674 |
+
NUM_TDM_OPS: gl.constexpr = 4 if is_x_microscaled else 3
|
| 675 |
+
w_type: gl.constexpr = W.dtype.element_ty
|
| 676 |
+
gl.static_assert(w_type == gl.uint8, "mx_weight_ptr must be uint8 or fp8")
|
| 677 |
+
gl.static_assert(
|
| 678 |
+
WMxScale.dtype.element_ty == gl.uint8, "mx_scale_ptr must be uint8"
|
| 679 |
+
)
|
| 680 |
+
gl.static_assert(
|
| 681 |
+
BLOCK_K % MX_PACK_DIVISOR == 0, "BLOCK_K must be a multiple of MX_PACK_DIVISOR"
|
| 682 |
+
)
|
| 683 |
+
x_type: gl.constexpr = X.dtype.element_ty
|
| 684 |
+
if is_x_microscaled:
|
| 685 |
+
gl.static_assert(x_type == gl.float8e4nv, "mx_act_ptr must be float8e4nv")
|
| 686 |
+
gl.static_assert(
|
| 687 |
+
XMxScale.dtype.element_ty == gl.uint8, "mx_scale_ptr must be uint8"
|
| 688 |
+
)
|
| 689 |
+
|
| 690 |
+
OUT_BLOCK_N: tl.constexpr = BLOCK_N // ACTIVATION_REDUCTION_N
|
| 691 |
+
yN = N // ACTIVATION_REDUCTION_N
|
| 692 |
+
|
| 693 |
+
pid = gl.program_id(0)
|
| 694 |
+
|
| 695 |
+
index_type: tl.constexpr = gl.int64 if UPCAST_INDICES else gl.int32
|
| 696 |
+
|
| 697 |
+
if XCD_SWIZZLE != 1:
|
| 698 |
+
padding_m = grid_m - gl.load(ExptOffsSum)
|
| 699 |
+
unpadded_m = grid_m - padding_m
|
| 700 |
+
total_actual_tiles = unpadded_m * grid_n
|
| 701 |
+
if padding_m > 0 and pid >= total_actual_tiles:
|
| 702 |
+
return
|
| 703 |
+
pid = remap_xcd(pid, total_actual_tiles, XCD_SWIZZLE)
|
| 704 |
+
else:
|
| 705 |
+
unpadded_m = grid_m
|
| 706 |
+
pid_m, pid_n = pid_grid(pid, unpadded_m, grid_n, 1)
|
| 707 |
+
# unpack expert data
|
| 708 |
+
expt_data = gl.load(ExptData + pid_m)
|
| 709 |
+
if XCD_SWIZZLE == 1 and expt_data == -1:
|
| 710 |
+
return
|
| 711 |
+
expt_id = expt_data & 0x0000FFFF
|
| 712 |
+
block_id = expt_data >> 16
|
| 713 |
+
M = gl.load(ExptHist + expt_id)
|
| 714 |
+
start_m = gl.load(ExptOffs + expt_id)
|
| 715 |
+
expt_id, block_id = expt_id.to(index_type), block_id.to(index_type)
|
| 716 |
+
start_m = start_m.to(index_type)
|
| 717 |
+
pid_n = pid_n.to(index_type)
|
| 718 |
+
|
| 719 |
+
# A pointers
|
| 720 |
+
off_x_m = BLOCK_M * block_id
|
| 721 |
+
if GatherIndx is None:
|
| 722 |
+
X += start_m * stride_x_m
|
| 723 |
+
else:
|
| 724 |
+
if GatherIndx.dtype.element_ty == gl.uint16:
|
| 725 |
+
IDX_LAYOUT: gl.constexpr = gl.SliceLayout(
|
| 726 |
+
0, gl.BlockedLayout([1, 16], [32, 1], [1, num_warps], [0, 1])
|
| 727 |
+
)
|
| 728 |
+
else:
|
| 729 |
+
gl.static_assert(
|
| 730 |
+
GatherIndx.dtype.element_ty == gl.int32,
|
| 731 |
+
"Gather index datatype should be uint16 or int32",
|
| 732 |
+
)
|
| 733 |
+
IDX_LAYOUT: gl.constexpr = gl.SliceLayout(
|
| 734 |
+
0, gl.BlockedLayout([1, 8], [32, 1], [1, num_warps], [0, 1])
|
| 735 |
+
)
|
| 736 |
+
offs_x_m = BLOCK_M * block_id + gl.arange(0, BLOCK_M, layout=IDX_LAYOUT)
|
| 737 |
+
offs_x_m = offs_x_m % M
|
| 738 |
+
GatherIndx += start_m
|
| 739 |
+
offs_x_m = gl.load(GatherIndx + offs_x_m) // N_EXPTS_ACT
|
| 740 |
+
|
| 741 |
+
W_K_DIVISOR: gl.constexpr = 2
|
| 742 |
+
PACKED_BLOCK_K_W: gl.constexpr = BLOCK_K // W_K_DIVISOR
|
| 743 |
+
PACKED_BLOCK_N_W: gl.constexpr = BLOCK_N
|
| 744 |
+
MX_SCALE_BLOCK_K: gl.constexpr = BLOCK_K // MX_PACK_DIVISOR
|
| 745 |
+
|
| 746 |
+
WMxScale += expt_id * stride_w_mx_e
|
| 747 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 748 |
+
gl.static_assert(stride_w_mx_k is not None)
|
| 749 |
+
gl.static_assert(stride_w_mx_n is not None)
|
| 750 |
+
PRESHUFFLE_FACTOR: gl.constexpr = 32
|
| 751 |
+
PACKED_MX_BLOCK: gl.constexpr = MX_SCALE_BLOCK_K * PRESHUFFLE_FACTOR
|
| 752 |
+
SCALE_BLOCK_N: gl.constexpr = BLOCK_N // PRESHUFFLE_FACTOR
|
| 753 |
+
SCALE_KWIDTH: gl.constexpr = 8
|
| 754 |
+
else:
|
| 755 |
+
PRESHUFFLE_FACTOR: gl.constexpr = 1
|
| 756 |
+
PACKED_MX_BLOCK: gl.constexpr = MX_SCALE_BLOCK_K
|
| 757 |
+
SCALE_BLOCK_N: gl.constexpr = BLOCK_N
|
| 758 |
+
|
| 759 |
+
# B pointers
|
| 760 |
+
off_w_n_scale = pid_n * SCALE_BLOCK_N
|
| 761 |
+
off_w_n = pid_n * PACKED_BLOCK_N_W
|
| 762 |
+
W += expt_id * stride_w_e
|
| 763 |
+
|
| 764 |
+
SHARED_LAYOUT_X: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 765 |
+
[[BLOCK_K, 16]], [BLOCK_M, BLOCK_K], [1, 0]
|
| 766 |
+
)
|
| 767 |
+
if BLOCK_K <= 256:
|
| 768 |
+
SHARED_LAYOUT_W: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 769 |
+
[[256, 16]], [BLOCK_N, PACKED_BLOCK_K_W], [1, 0]
|
| 770 |
+
)
|
| 771 |
+
else:
|
| 772 |
+
SHARED_LAYOUT_W: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 773 |
+
[[PACKED_BLOCK_K_W, 16]], [BLOCK_N, PACKED_BLOCK_K_W], [1, 0]
|
| 774 |
+
)
|
| 775 |
+
SHARED_LAYOUT_W_SCALES: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 776 |
+
[[256, 16]], [SCALE_BLOCK_N, PACKED_MX_BLOCK], [1, 0]
|
| 777 |
+
)
|
| 778 |
+
if is_x_microscaled:
|
| 779 |
+
SHARED_LAYOUT_X_SCALES: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 780 |
+
[[256, 16]], [BLOCK_M, MX_SCALE_BLOCK_K], [1, 0]
|
| 781 |
+
)
|
| 782 |
+
if Quant_static_scale is not None:
|
| 783 |
+
SHARED_LAYOUT_Y: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 784 |
+
[[OUT_BLOCK_N, 16]], [BLOCK_M, OUT_BLOCK_N], [1, 0]
|
| 785 |
+
)
|
| 786 |
+
else:
|
| 787 |
+
SHARED_LAYOUT_Y: gl.constexpr = gl.PaddedSharedLayout.with_identity_for(
|
| 788 |
+
[[OUT_BLOCK_N, 8]], [BLOCK_M, OUT_BLOCK_N], [1, 0]
|
| 789 |
+
)
|
| 790 |
+
|
| 791 |
+
if GatherIndx is None:
|
| 792 |
+
x_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 793 |
+
base=X,
|
| 794 |
+
shape=(M, K),
|
| 795 |
+
strides=(stride_x_m, stride_x_k),
|
| 796 |
+
block_shape=(BLOCK_M, BLOCK_K),
|
| 797 |
+
layout=SHARED_LAYOUT_X,
|
| 798 |
+
)
|
| 799 |
+
else:
|
| 800 |
+
x_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 801 |
+
base=X,
|
| 802 |
+
shape=(num_tokens, K),
|
| 803 |
+
strides=(stride_x_m, stride_x_k),
|
| 804 |
+
block_shape=(BLOCK_M, BLOCK_K),
|
| 805 |
+
layout=SHARED_LAYOUT_X,
|
| 806 |
+
)
|
| 807 |
+
w_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 808 |
+
base=W,
|
| 809 |
+
shape=(N, K // W_K_DIVISOR),
|
| 810 |
+
strides=(
|
| 811 |
+
stride_w_n,
|
| 812 |
+
stride_w_k,
|
| 813 |
+
),
|
| 814 |
+
block_shape=(
|
| 815 |
+
BLOCK_N,
|
| 816 |
+
PACKED_BLOCK_K_W,
|
| 817 |
+
),
|
| 818 |
+
layout=SHARED_LAYOUT_W,
|
| 819 |
+
)
|
| 820 |
+
w_scales_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 821 |
+
base=WMxScale,
|
| 822 |
+
shape=(N // PRESHUFFLE_FACTOR, tl.cdiv(K, MX_PACK_DIVISOR) * PRESHUFFLE_FACTOR),
|
| 823 |
+
strides=(stride_w_mx_n, stride_w_mx_k),
|
| 824 |
+
block_shape=(SCALE_BLOCK_N, PACKED_MX_BLOCK),
|
| 825 |
+
layout=SHARED_LAYOUT_W_SCALES,
|
| 826 |
+
)
|
| 827 |
+
|
| 828 |
+
if is_x_microscaled:
|
| 829 |
+
if GatherIndx is None:
|
| 830 |
+
XMxScale += start_m * stride_x_mx_m
|
| 831 |
+
x_scales_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 832 |
+
base=XMxScale,
|
| 833 |
+
shape=(M, tl.cdiv(K, MX_PACK_DIVISOR)),
|
| 834 |
+
strides=(stride_x_mx_m, stride_x_mx_k),
|
| 835 |
+
block_shape=(BLOCK_M, MX_SCALE_BLOCK_K),
|
| 836 |
+
layout=SHARED_LAYOUT_X_SCALES,
|
| 837 |
+
)
|
| 838 |
+
else:
|
| 839 |
+
x_scales_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 840 |
+
base=XMxScale,
|
| 841 |
+
shape=(num_tokens, tl.cdiv(K, MX_PACK_DIVISOR)),
|
| 842 |
+
strides=(stride_x_mx_m, stride_x_mx_k),
|
| 843 |
+
block_shape=(BLOCK_M, MX_SCALE_BLOCK_K),
|
| 844 |
+
layout=SHARED_LAYOUT_X_SCALES,
|
| 845 |
+
)
|
| 846 |
+
|
| 847 |
+
WMMA_LAYOUT: gl.constexpr = gl.amd.AMDWMMALayout(
|
| 848 |
+
3,
|
| 849 |
+
transposed=True,
|
| 850 |
+
warp_bases=[[0, 1], [1, 0]],
|
| 851 |
+
reg_bases=[],
|
| 852 |
+
instr_shape=[16, 16, 128],
|
| 853 |
+
)
|
| 854 |
+
WMMA_LAYOUT_PACKED: gl.constexpr = gl.amd.AMDWMMALayout(
|
| 855 |
+
3,
|
| 856 |
+
transposed=True,
|
| 857 |
+
warp_bases=[[0, 1], [1, 0]],
|
| 858 |
+
reg_bases=[],
|
| 859 |
+
instr_shape=[16, 16, 64],
|
| 860 |
+
)
|
| 861 |
+
DOT_LAYOUT_X: gl.constexpr = gl.DotOperandLayout(0, WMMA_LAYOUT, k_width=16)
|
| 862 |
+
DOT_LAYOUT_W: gl.constexpr = gl.DotOperandLayout(1, WMMA_LAYOUT_PACKED, k_width=16)
|
| 863 |
+
DOT_LAYOUT_W_SCALES: gl.constexpr = gl.amd.gfx1250.get_wmma_scale_layout(
|
| 864 |
+
DOT_LAYOUT_W, [BLOCK_N, MX_SCALE_BLOCK_K]
|
| 865 |
+
)
|
| 866 |
+
if is_x_microscaled:
|
| 867 |
+
DOT_LAYOUT_X_SCALES: gl.constexpr = gl.amd.gfx1250.get_wmma_scale_layout(
|
| 868 |
+
DOT_LAYOUT_X, [BLOCK_M, MX_SCALE_BLOCK_K]
|
| 869 |
+
)
|
| 870 |
+
|
| 871 |
+
x_buffer = gl.allocate_shared_memory(
|
| 872 |
+
x_desc.dtype, shape=[NUM_BUFFERS] + x_desc.block_shape, layout=x_desc.layout
|
| 873 |
+
)
|
| 874 |
+
w_buffer = gl.allocate_shared_memory(
|
| 875 |
+
w_desc.dtype, shape=[NUM_BUFFERS] + w_desc.block_shape, layout=w_desc.layout
|
| 876 |
+
)
|
| 877 |
+
w_scales_buffer = gl.allocate_shared_memory(
|
| 878 |
+
w_scales_desc.dtype,
|
| 879 |
+
shape=[NUM_BUFFERS] + w_scales_desc.block_shape,
|
| 880 |
+
layout=w_scales_desc.layout,
|
| 881 |
+
)
|
| 882 |
+
if is_x_microscaled:
|
| 883 |
+
x_scales_buffer = gl.allocate_shared_memory(
|
| 884 |
+
x_scales_desc.dtype,
|
| 885 |
+
shape=[NUM_BUFFERS] + x_scales_desc.block_shape,
|
| 886 |
+
layout=x_scales_desc.layout,
|
| 887 |
+
)
|
| 888 |
+
|
| 889 |
+
read_idx = 0
|
| 890 |
+
write_idx = 0
|
| 891 |
+
for _ in gl.static_range(NUM_BUFFERS):
|
| 892 |
+
if GatherIndx is None:
|
| 893 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 894 |
+
x_desc,
|
| 895 |
+
[off_x_m, write_idx * BLOCK_K],
|
| 896 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 897 |
+
)
|
| 898 |
+
else:
|
| 899 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 900 |
+
x_desc,
|
| 901 |
+
offs_x_m,
|
| 902 |
+
write_idx * BLOCK_K,
|
| 903 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 904 |
+
)
|
| 905 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 906 |
+
w_desc,
|
| 907 |
+
[off_w_n, write_idx * PACKED_BLOCK_K_W],
|
| 908 |
+
w_buffer.index(write_idx % NUM_BUFFERS),
|
| 909 |
+
)
|
| 910 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 911 |
+
w_scales_desc,
|
| 912 |
+
[off_w_n_scale, write_idx * PACKED_MX_BLOCK],
|
| 913 |
+
w_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 914 |
+
)
|
| 915 |
+
if is_x_microscaled:
|
| 916 |
+
if GatherIndx is None:
|
| 917 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 918 |
+
x_scales_desc,
|
| 919 |
+
[off_x_m, write_idx * MX_SCALE_BLOCK_K],
|
| 920 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 921 |
+
)
|
| 922 |
+
else:
|
| 923 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 924 |
+
x_scales_desc,
|
| 925 |
+
offs_x_m,
|
| 926 |
+
write_idx * MX_SCALE_BLOCK_K,
|
| 927 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 928 |
+
)
|
| 929 |
+
write_idx += 1
|
| 930 |
+
|
| 931 |
+
num_k_iter = tl.cdiv(K, BLOCK_K)
|
| 932 |
+
|
| 933 |
+
# After TDM prologue there are NUM_BUFFERS*3 ops in-flight; waiting for
|
| 934 |
+
# (NUM_BUFFERS-1)*3 lets exactly one tile (tile 0) complete.
|
| 935 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * NUM_TDM_OPS)
|
| 936 |
+
|
| 937 |
+
# Register pre-load prologue: wait for tile 0 then read it into cur_x/cur_w/cur_w_scales.
|
| 938 |
+
cur_x = x_buffer.index(read_idx % NUM_BUFFERS).load(layout=DOT_LAYOUT_X)
|
| 939 |
+
cur_w = (
|
| 940 |
+
w_buffer.index(read_idx % NUM_BUFFERS).permute((1, 0)).load(layout=DOT_LAYOUT_W)
|
| 941 |
+
)
|
| 942 |
+
w_scales_buffer_slice = w_scales_buffer.index(read_idx % NUM_BUFFERS)
|
| 943 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 944 |
+
w_scales_buffer_slice = unswizzle_mx_scale_gfx1250(
|
| 945 |
+
w_scales_buffer_slice,
|
| 946 |
+
BLOCK_N,
|
| 947 |
+
MX_SCALE_BLOCK_K,
|
| 948 |
+
PRESHUFFLE_FACTOR,
|
| 949 |
+
SCALE_KWIDTH,
|
| 950 |
+
)
|
| 951 |
+
cur_w_scales = w_scales_buffer_slice.load(layout=DOT_LAYOUT_W_SCALES)
|
| 952 |
+
if is_x_microscaled:
|
| 953 |
+
cur_x_scales = x_scales_buffer.index(read_idx % NUM_BUFFERS).load(
|
| 954 |
+
layout=DOT_LAYOUT_X_SCALES
|
| 955 |
+
)
|
| 956 |
+
read_idx += 1
|
| 957 |
+
|
| 958 |
+
acc = gl.zeros((BLOCK_M, BLOCK_N), dtype=gl.float32, layout=WMMA_LAYOUT)
|
| 959 |
+
for k in range(num_k_iter - NUM_BUFFERS):
|
| 960 |
+
if is_x_microscaled:
|
| 961 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 962 |
+
cur_x, cur_x_scales, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 963 |
+
)
|
| 964 |
+
else:
|
| 965 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 966 |
+
cur_x, 0, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 967 |
+
)
|
| 968 |
+
|
| 969 |
+
if GatherIndx is None:
|
| 970 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 971 |
+
x_desc,
|
| 972 |
+
[off_x_m, write_idx * BLOCK_K],
|
| 973 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 974 |
+
)
|
| 975 |
+
else:
|
| 976 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 977 |
+
x_desc,
|
| 978 |
+
offs_x_m,
|
| 979 |
+
write_idx * BLOCK_K,
|
| 980 |
+
x_buffer.index(write_idx % NUM_BUFFERS),
|
| 981 |
+
)
|
| 982 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 983 |
+
w_desc,
|
| 984 |
+
[off_w_n, write_idx * PACKED_BLOCK_K_W],
|
| 985 |
+
w_buffer.index(write_idx % NUM_BUFFERS),
|
| 986 |
+
)
|
| 987 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 988 |
+
w_scales_desc,
|
| 989 |
+
[off_w_n_scale, write_idx * PACKED_MX_BLOCK],
|
| 990 |
+
w_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 991 |
+
)
|
| 992 |
+
if is_x_microscaled:
|
| 993 |
+
if GatherIndx is None:
|
| 994 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 995 |
+
x_scales_desc,
|
| 996 |
+
[off_x_m, write_idx * MX_SCALE_BLOCK_K],
|
| 997 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 998 |
+
)
|
| 999 |
+
else:
|
| 1000 |
+
gl.amd.gfx1250.tdm.async_gather(
|
| 1001 |
+
x_scales_desc,
|
| 1002 |
+
offs_x_m,
|
| 1003 |
+
write_idx * MX_SCALE_BLOCK_K,
|
| 1004 |
+
x_scales_buffer.index(write_idx % NUM_BUFFERS),
|
| 1005 |
+
)
|
| 1006 |
+
write_idx += 1
|
| 1007 |
+
|
| 1008 |
+
gl.amd.gfx1250.tdm.async_wait((NUM_BUFFERS - 1) * NUM_TDM_OPS)
|
| 1009 |
+
|
| 1010 |
+
next_x = x_buffer.index(read_idx % NUM_BUFFERS).load(layout=DOT_LAYOUT_X)
|
| 1011 |
+
next_w = (
|
| 1012 |
+
w_buffer.index(read_idx % NUM_BUFFERS)
|
| 1013 |
+
.permute((1, 0))
|
| 1014 |
+
.load(layout=DOT_LAYOUT_W)
|
| 1015 |
+
)
|
| 1016 |
+
w_scales_buffer_slice = w_scales_buffer.index(read_idx % NUM_BUFFERS)
|
| 1017 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 1018 |
+
w_scales_buffer_slice = unswizzle_mx_scale_gfx1250(
|
| 1019 |
+
w_scales_buffer_slice,
|
| 1020 |
+
BLOCK_N,
|
| 1021 |
+
MX_SCALE_BLOCK_K,
|
| 1022 |
+
PRESHUFFLE_FACTOR,
|
| 1023 |
+
SCALE_KWIDTH,
|
| 1024 |
+
)
|
| 1025 |
+
next_w_scales = w_scales_buffer_slice.load(layout=DOT_LAYOUT_W_SCALES)
|
| 1026 |
+
if is_x_microscaled:
|
| 1027 |
+
next_x_scales = x_scales_buffer.index(read_idx % NUM_BUFFERS).load(
|
| 1028 |
+
layout=DOT_LAYOUT_X_SCALES
|
| 1029 |
+
)
|
| 1030 |
+
|
| 1031 |
+
cur_x = next_x
|
| 1032 |
+
cur_w = next_w
|
| 1033 |
+
cur_w_scales = next_w_scales
|
| 1034 |
+
if is_x_microscaled:
|
| 1035 |
+
cur_x_scales = next_x_scales
|
| 1036 |
+
read_idx += 1
|
| 1037 |
+
|
| 1038 |
+
# bias
|
| 1039 |
+
if B is not None:
|
| 1040 |
+
BPtrs = B + expt_id * stride_b_e
|
| 1041 |
+
SHARED_LAYOUT_BIAS: gl.constexpr = gl.SwizzledSharedLayout(1, 1, 1, [1, 0])
|
| 1042 |
+
bias_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 1043 |
+
base=BPtrs,
|
| 1044 |
+
shape=(1, N),
|
| 1045 |
+
strides=(N, 1),
|
| 1046 |
+
block_shape=(1, BLOCK_N),
|
| 1047 |
+
layout=SHARED_LAYOUT_BIAS,
|
| 1048 |
+
)
|
| 1049 |
+
bias_buffer = gl.allocate_shared_memory(
|
| 1050 |
+
bias_desc.dtype, shape=[1, BLOCK_N], layout=bias_desc.layout
|
| 1051 |
+
)
|
| 1052 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 1053 |
+
bias_desc,
|
| 1054 |
+
[0, pid_n * BLOCK_N],
|
| 1055 |
+
bias_buffer,
|
| 1056 |
+
)
|
| 1057 |
+
TDM_BIAS_WAIT: gl.constexpr = 1
|
| 1058 |
+
else:
|
| 1059 |
+
TDM_BIAS_WAIT: gl.constexpr = 0
|
| 1060 |
+
|
| 1061 |
+
# Epilogue: drain remaining pipeline stages (no new TDM loads).
|
| 1062 |
+
# The first NUM_BUFFERS-1 iterations still use the pre-load / WMMA pattern.
|
| 1063 |
+
for k_ep in gl.static_range(NUM_BUFFERS - 1):
|
| 1064 |
+
if is_x_microscaled:
|
| 1065 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 1066 |
+
cur_x, cur_x_scales, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 1067 |
+
)
|
| 1068 |
+
else:
|
| 1069 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 1070 |
+
cur_x, 0, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 1071 |
+
)
|
| 1072 |
+
|
| 1073 |
+
gl.amd.gfx1250.tdm.async_wait(
|
| 1074 |
+
(NUM_BUFFERS - 2 - k_ep) * NUM_TDM_OPS + TDM_BIAS_WAIT
|
| 1075 |
+
)
|
| 1076 |
+
|
| 1077 |
+
next_x = x_buffer.index(read_idx % NUM_BUFFERS).load(layout=DOT_LAYOUT_X)
|
| 1078 |
+
next_w = (
|
| 1079 |
+
w_buffer.index(read_idx % NUM_BUFFERS)
|
| 1080 |
+
.permute((1, 0))
|
| 1081 |
+
.load(layout=DOT_LAYOUT_W)
|
| 1082 |
+
)
|
| 1083 |
+
w_scales_buffer_slice = w_scales_buffer.index(read_idx % NUM_BUFFERS)
|
| 1084 |
+
if SWIZZLE_MX_SCALE == "GFX1250_SCALE":
|
| 1085 |
+
w_scales_buffer_slice = unswizzle_mx_scale_gfx1250(
|
| 1086 |
+
w_scales_buffer_slice,
|
| 1087 |
+
BLOCK_N,
|
| 1088 |
+
MX_SCALE_BLOCK_K,
|
| 1089 |
+
PRESHUFFLE_FACTOR,
|
| 1090 |
+
SCALE_KWIDTH,
|
| 1091 |
+
)
|
| 1092 |
+
next_w_scales = w_scales_buffer_slice.load(layout=DOT_LAYOUT_W_SCALES)
|
| 1093 |
+
if is_x_microscaled:
|
| 1094 |
+
next_x_scales = x_scales_buffer.index(read_idx % NUM_BUFFERS).load(
|
| 1095 |
+
layout=DOT_LAYOUT_X_SCALES
|
| 1096 |
+
)
|
| 1097 |
+
|
| 1098 |
+
cur_x = next_x
|
| 1099 |
+
cur_w = next_w
|
| 1100 |
+
cur_w_scales = next_w_scales
|
| 1101 |
+
if is_x_microscaled:
|
| 1102 |
+
cur_x_scales = next_x_scales
|
| 1103 |
+
read_idx += 1
|
| 1104 |
+
|
| 1105 |
+
if is_x_microscaled:
|
| 1106 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 1107 |
+
cur_x, cur_x_scales, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 1108 |
+
)
|
| 1109 |
+
else:
|
| 1110 |
+
acc = gl.amd.gfx1250.wmma_scaled(
|
| 1111 |
+
cur_x, 0, "e4m3", cur_w, cur_w_scales, "e2m1", acc
|
| 1112 |
+
)
|
| 1113 |
+
|
| 1114 |
+
# scalar fp8 scale
|
| 1115 |
+
if X_static_scale is not None:
|
| 1116 |
+
acc = acc * gl.load(X_static_scale)
|
| 1117 |
+
|
| 1118 |
+
if B is not None:
|
| 1119 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 1120 |
+
bias = bias_buffer.reshape((BLOCK_N,)).load(
|
| 1121 |
+
layout=gl.SliceLayout(0, WMMA_LAYOUT)
|
| 1122 |
+
)
|
| 1123 |
+
acc = acc + bias[None, :]
|
| 1124 |
+
|
| 1125 |
+
if APPLY_SWIGLU:
|
| 1126 |
+
out = _swiglu(acc, alpha, limit, ADD_RESIDUAL=SWIGLU_ADD_RESIDUAL)
|
| 1127 |
+
tl.static_assert(
|
| 1128 |
+
out.shape[1] == OUT_BLOCK_N,
|
| 1129 |
+
f"Activation fn out.shape[1] ({out.shape[1]}) doesn't match computed OUT_BLOCK_N ({OUT_BLOCK_N})",
|
| 1130 |
+
)
|
| 1131 |
+
else:
|
| 1132 |
+
tl.static_assert(
|
| 1133 |
+
ACTIVATION_REDUCTION_N == 1,
|
| 1134 |
+
"Activation reduction must be 1 if no activation fn is provided",
|
| 1135 |
+
)
|
| 1136 |
+
out = acc
|
| 1137 |
+
|
| 1138 |
+
if Gammas is not None:
|
| 1139 |
+
offs_m = BLOCK_M * block_id + gl.arange(0, BLOCK_M)
|
| 1140 |
+
mask_m = offs_m < M
|
| 1141 |
+
gammas = gl.amd.gfx1250.buffer_load(
|
| 1142 |
+
Gammas + start_m, offs_m, mask=mask_m, other=0.0
|
| 1143 |
+
)
|
| 1144 |
+
out *= gammas[:, None]
|
| 1145 |
+
|
| 1146 |
+
# quant
|
| 1147 |
+
if Quant_static_scale is not None:
|
| 1148 |
+
out = _compute_static_fp8_quant(out, gl.load(Quant_static_scale))
|
| 1149 |
+
else:
|
| 1150 |
+
out = out.to(tl.bfloat16)
|
| 1151 |
+
|
| 1152 |
+
# TDM Store: accumulator → shared memory → global memory
|
| 1153 |
+
Y += start_m * stride_y_m
|
| 1154 |
+
y_buffer = gl.allocate_shared_memory(
|
| 1155 |
+
Y.type.element_ty,
|
| 1156 |
+
shape=[BLOCK_M, OUT_BLOCK_N],
|
| 1157 |
+
layout=SHARED_LAYOUT_Y,
|
| 1158 |
+
)
|
| 1159 |
+
y_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 1160 |
+
base=Y,
|
| 1161 |
+
shape=(M, yN),
|
| 1162 |
+
strides=(stride_y_m, stride_y_n),
|
| 1163 |
+
block_shape=(BLOCK_M, OUT_BLOCK_N),
|
| 1164 |
+
layout=SHARED_LAYOUT_Y,
|
| 1165 |
+
)
|
| 1166 |
+
y_buffer.store(out)
|
| 1167 |
+
gl.amd.gfx1250.tdm.async_store(
|
| 1168 |
+
y_desc, [block_id * BLOCK_M, pid_n * OUT_BLOCK_N], y_buffer
|
| 1169 |
+
)
|
| 1170 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
build/torch-rocm/_gluon_kernels/gfx1250/norm/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_gluon_kernels/gfx1250/norm/fused_rmsnorm_add.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
# RMSNorm + residual add.
|
| 5 |
+
from triton.experimental import gluon
|
| 6 |
+
from triton.experimental.gluon import language as gl
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@gluon.jit
|
| 10 |
+
def _rmsnorm_op(row, weights, n_cols, epsilon):
|
| 11 |
+
row_norm = row * row
|
| 12 |
+
row_norm = gl.sum(row_norm, axis=-1, keep_dims=True)
|
| 13 |
+
norm_factor = gl.rsqrt((row_norm / n_cols) + epsilon)
|
| 14 |
+
return row * norm_factor * weights
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@gluon.jit
|
| 18 |
+
def _gluon_fused_rms_kernel(
|
| 19 |
+
x1_ptr,
|
| 20 |
+
w1_ptr,
|
| 21 |
+
res1_ptr,
|
| 22 |
+
out1_ptr,
|
| 23 |
+
out_res1_ptr,
|
| 24 |
+
eps1,
|
| 25 |
+
M,
|
| 26 |
+
N,
|
| 27 |
+
x1_stride_m,
|
| 28 |
+
res1_stride_m,
|
| 29 |
+
out1_stride_m,
|
| 30 |
+
out_res1_stride_m,
|
| 31 |
+
BLOCK_SIZE_M: gl.constexpr,
|
| 32 |
+
BLOCK_SIZE_N: gl.constexpr,
|
| 33 |
+
FIRST_INPUT_RES: gl.constexpr,
|
| 34 |
+
):
|
| 35 |
+
start_pid = gl.program_id(0)
|
| 36 |
+
|
| 37 |
+
gLayout2D: gl.constexpr = gl.BlockedLayout([1, 8], [1, 32], [1, 4], [1, 0])
|
| 38 |
+
gLayoutN: gl.constexpr = gl.SliceLayout(0, gLayout2D)
|
| 39 |
+
sharedLayout2D: gl.constexpr = gl.SwizzledSharedLayout(1, 1, 1, order=[1, 0])
|
| 40 |
+
sharedLayoutN: gl.constexpr = gl.SwizzledSharedLayout(1, 1, 1, order=[0])
|
| 41 |
+
|
| 42 |
+
# descriptors + smem for first input and its weight
|
| 43 |
+
x1_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 44 |
+
x1_ptr, [M, N], [x1_stride_m, 1], [BLOCK_SIZE_M, BLOCK_SIZE_N], sharedLayout2D
|
| 45 |
+
)
|
| 46 |
+
w1_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 47 |
+
w1_ptr, [N], [1], [BLOCK_SIZE_N], sharedLayoutN
|
| 48 |
+
)
|
| 49 |
+
smemX1 = gl.allocate_shared_memory(
|
| 50 |
+
x1_ptr.dtype.element_ty, [BLOCK_SIZE_M, BLOCK_SIZE_N], sharedLayout2D
|
| 51 |
+
)
|
| 52 |
+
smemW1 = gl.allocate_shared_memory(
|
| 53 |
+
w1_ptr.dtype.element_ty, [BLOCK_SIZE_N], sharedLayoutN
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
# x1 load issued first for early latency hiding
|
| 57 |
+
gl.amd.gfx1250.tdm.async_load(x1_desc, [start_pid * BLOCK_SIZE_M, 0], smemX1)
|
| 58 |
+
|
| 59 |
+
# optional residual input
|
| 60 |
+
if FIRST_INPUT_RES:
|
| 61 |
+
res1_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 62 |
+
res1_ptr,
|
| 63 |
+
[M, N],
|
| 64 |
+
[res1_stride_m, 1],
|
| 65 |
+
[BLOCK_SIZE_M, BLOCK_SIZE_N],
|
| 66 |
+
sharedLayout2D,
|
| 67 |
+
)
|
| 68 |
+
smemRes1 = gl.allocate_shared_memory(
|
| 69 |
+
res1_ptr.dtype.element_ty, [BLOCK_SIZE_M, BLOCK_SIZE_N], sharedLayout2D
|
| 70 |
+
)
|
| 71 |
+
gl.amd.gfx1250.tdm.async_load(
|
| 72 |
+
res1_desc, [start_pid * BLOCK_SIZE_M, 0], smemRes1
|
| 73 |
+
)
|
| 74 |
+
out_res1_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 75 |
+
out_res1_ptr,
|
| 76 |
+
[M, N],
|
| 77 |
+
[out_res1_stride_m, 1],
|
| 78 |
+
[BLOCK_SIZE_M, BLOCK_SIZE_N],
|
| 79 |
+
sharedLayout2D,
|
| 80 |
+
)
|
| 81 |
+
smemOutRes1 = gl.allocate_shared_memory(
|
| 82 |
+
out_res1_ptr.dtype.element_ty, [BLOCK_SIZE_M, BLOCK_SIZE_N], sharedLayout2D
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
gl.amd.gfx1250.tdm.async_load(w1_desc, [0], smemW1)
|
| 86 |
+
|
| 87 |
+
# output descriptor + smem (static alloc; placement before wait is free)
|
| 88 |
+
out1_desc = gl.amd.gfx1250.tdm.make_tensor_descriptor(
|
| 89 |
+
out1_ptr,
|
| 90 |
+
[M, N],
|
| 91 |
+
[out1_stride_m, 1],
|
| 92 |
+
[BLOCK_SIZE_M, BLOCK_SIZE_N],
|
| 93 |
+
sharedLayout2D,
|
| 94 |
+
)
|
| 95 |
+
smemOut1 = gl.allocate_shared_memory(
|
| 96 |
+
out1_ptr.dtype.element_ty, [BLOCK_SIZE_M, BLOCK_SIZE_N], sharedLayout2D
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
gl.amd.gfx1250.tdm.async_wait(1)
|
| 100 |
+
|
| 101 |
+
x1 = smemX1.load(gLayout2D).to(gl.float32)
|
| 102 |
+
|
| 103 |
+
if FIRST_INPUT_RES:
|
| 104 |
+
res1_loaded = smemRes1.load(gLayout2D).to(gl.float32)
|
| 105 |
+
x1 = x1 + res1_loaded
|
| 106 |
+
smemOutRes1.store(x1.to(out_res1_ptr.dtype.element_ty))
|
| 107 |
+
gl.amd.gfx1250.tdm.async_store(
|
| 108 |
+
out_res1_desc, [start_pid * BLOCK_SIZE_M, 0], smemOutRes1
|
| 109 |
+
)
|
| 110 |
+
gl.amd.gfx1250.tdm.async_wait(1)
|
| 111 |
+
else:
|
| 112 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
| 113 |
+
|
| 114 |
+
w1 = smemW1.load(gLayoutN).to(gl.float32)
|
| 115 |
+
w1 = w1.reshape(1, BLOCK_SIZE_N)
|
| 116 |
+
w1 = gl.convert_layout(w1, gLayout2D)
|
| 117 |
+
norm1 = _rmsnorm_op(x1, w1, N, eps1)
|
| 118 |
+
|
| 119 |
+
smemOut1.store(norm1.to(out1_ptr.dtype.element_ty))
|
| 120 |
+
gl.amd.gfx1250.tdm.async_store(out1_desc, [start_pid * BLOCK_SIZE_M, 0], smemOut1)
|
| 121 |
+
|
| 122 |
+
gl.amd.gfx1250.tdm.async_wait(0)
|
build/torch-rocm/_gluon_kernels/gfx942/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_gluon_kernels/gfx942/moe/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_gluon_kernels/gfx942/moe/moe_op_gemm_int8_smoothquant.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
from triton.experimental import gluon
|
| 3 |
+
from triton.experimental.gluon import language as gl
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@triton.heuristics(
|
| 7 |
+
{
|
| 8 |
+
"UNROLL_TIMES": lambda args: triton.cdiv(args["K"], args["BLOCK_K"]),
|
| 9 |
+
}
|
| 10 |
+
)
|
| 11 |
+
@gluon.jit
|
| 12 |
+
def _gluon_moe_gemm_int8_smoothquant(
|
| 13 |
+
Y,
|
| 14 |
+
stride_y_k,
|
| 15 |
+
stride_y_m,
|
| 16 |
+
stride_y_n,
|
| 17 |
+
X,
|
| 18 |
+
stride_x_m,
|
| 19 |
+
stride_x_k,
|
| 20 |
+
XScale,
|
| 21 |
+
stride_x_scale,
|
| 22 |
+
W,
|
| 23 |
+
stride_w_e,
|
| 24 |
+
stride_w_k,
|
| 25 |
+
stride_w_n,
|
| 26 |
+
WScale,
|
| 27 |
+
stride_w_scale_e,
|
| 28 |
+
stride_w_scale_n,
|
| 29 |
+
B,
|
| 30 |
+
stride_b_e, # Bias
|
| 31 |
+
Gammas,
|
| 32 |
+
N,
|
| 33 |
+
K,
|
| 34 |
+
# expt data
|
| 35 |
+
GatherIndx,
|
| 36 |
+
ExptHist,
|
| 37 |
+
ExptOffs,
|
| 38 |
+
ExptOffsSum,
|
| 39 |
+
ExptData,
|
| 40 |
+
# true grid size
|
| 41 |
+
grid_m,
|
| 42 |
+
grid_n,
|
| 43 |
+
alpha,
|
| 44 |
+
limit,
|
| 45 |
+
ACTIVATION_REDUCTION_N: gl.constexpr,
|
| 46 |
+
APPLY_ACTIVATION: gl.constexpr,
|
| 47 |
+
SWIGLU_ADD_RESIDUAL: gl.constexpr,
|
| 48 |
+
# MoE config
|
| 49 |
+
N_EXPTS_ACT: gl.constexpr,
|
| 50 |
+
# optimization config
|
| 51 |
+
BLOCK_M: gl.constexpr,
|
| 52 |
+
BLOCK_N: gl.constexpr,
|
| 53 |
+
BLOCK_K: gl.constexpr,
|
| 54 |
+
GROUP_M: gl.constexpr,
|
| 55 |
+
EVEN_K: gl.constexpr,
|
| 56 |
+
MASK_K_LIMIT: gl.constexpr,
|
| 57 |
+
# Gluon-specific
|
| 58 |
+
UNROLL_TIMES: gl.constexpr,
|
| 59 |
+
num_warps: gl.constexpr,
|
| 60 |
+
):
|
| 61 |
+
"""
|
| 62 |
+
Gluon-optimized Int8 MoE GEMM with SmoothQuant for small K dimensions.
|
| 63 |
+
|
| 64 |
+
Key optimizations over the standard _moe_gemm_int8_smoothquant:
|
| 65 |
+
- Manual LICM: A matrix, x_scale, and gammas pre-loaded outside N loop
|
| 66 |
+
- K-dimension unrolling via gl.static_range eliminates loop overhead
|
| 67 |
+
- Explicit BlockedLayout + MFMA instructions for optimal register usage
|
| 68 |
+
- SUB_BLOCK_SIZE_N inner loop processes large BLOCK_N in 64-wide chunks
|
| 69 |
+
"""
|
| 70 |
+
SUB_BLOCK_SIZE_N: gl.constexpr = 64
|
| 71 |
+
|
| 72 |
+
# -- Layouts --
|
| 73 |
+
# INT8 on CDNA3 uses v_mfma_i32_32x32x16_i8 instruction
|
| 74 |
+
blocked_a: gl.constexpr = gl.BlockedLayout(
|
| 75 |
+
size_per_thread=[1, 16],
|
| 76 |
+
threads_per_warp=[32, 2],
|
| 77 |
+
warps_per_cta=[num_warps, 1],
|
| 78 |
+
order=[1, 0],
|
| 79 |
+
)
|
| 80 |
+
blocked_b: gl.constexpr = gl.BlockedLayout(
|
| 81 |
+
size_per_thread=[16, 1],
|
| 82 |
+
threads_per_warp=[4, 16],
|
| 83 |
+
warps_per_cta=[1, num_warps],
|
| 84 |
+
order=[0, 1],
|
| 85 |
+
)
|
| 86 |
+
mfma_layout: gl.constexpr = gl.amd.AMDMFMALayout(
|
| 87 |
+
version=3,
|
| 88 |
+
instr_shape=[32, 32, 16],
|
| 89 |
+
transposed=True,
|
| 90 |
+
warps_per_cta=[num_warps, 1],
|
| 91 |
+
)
|
| 92 |
+
mfma_a_layout: gl.constexpr = gl.DotOperandLayout(
|
| 93 |
+
operand_index=0, parent=mfma_layout, k_width=16
|
| 94 |
+
)
|
| 95 |
+
mfma_b_layout: gl.constexpr = gl.DotOperandLayout(
|
| 96 |
+
operand_index=1, parent=mfma_layout, k_width=16
|
| 97 |
+
)
|
| 98 |
+
blocked_d: gl.constexpr = gl.BlockedLayout(
|
| 99 |
+
size_per_thread=[1, 8],
|
| 100 |
+
threads_per_warp=[16, 4],
|
| 101 |
+
warps_per_cta=[num_warps, 1],
|
| 102 |
+
order=[1, 0],
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
# -- PID mapping (new path style) --
|
| 106 |
+
pid = gl.program_id(axis=0)
|
| 107 |
+
|
| 108 |
+
pid_m = pid // grid_n
|
| 109 |
+
pid_n = pid % grid_n
|
| 110 |
+
|
| 111 |
+
# Unpack expert data
|
| 112 |
+
expt_data = gl.load(ExptData + pid_m)
|
| 113 |
+
if expt_data == -1:
|
| 114 |
+
return
|
| 115 |
+
expt_id = expt_data & 0x0000FFFF
|
| 116 |
+
block_id = expt_data >> 16
|
| 117 |
+
M = gl.load(ExptHist + expt_id)
|
| 118 |
+
start_m = gl.load(ExptOffs + expt_id)
|
| 119 |
+
|
| 120 |
+
# -- A row offsets --
|
| 121 |
+
offs_x_m_raw = block_id * BLOCK_M + gl.arange(
|
| 122 |
+
0, BLOCK_M, layout=gl.SliceLayout(1, blocked_a)
|
| 123 |
+
)
|
| 124 |
+
offs_x_m = offs_x_m_raw % M
|
| 125 |
+
mask_m = offs_x_m_raw < M
|
| 126 |
+
|
| 127 |
+
if GatherIndx is not None:
|
| 128 |
+
# Indirect indexing via gather
|
| 129 |
+
offs_x_m = (
|
| 130 |
+
gl.amd.cdna3.buffer_load(
|
| 131 |
+
GatherIndx + start_m, offs_x_m, mask=mask_m, other=0
|
| 132 |
+
)
|
| 133 |
+
// N_EXPTS_ACT
|
| 134 |
+
)
|
| 135 |
+
else:
|
| 136 |
+
X += start_m * stride_x_m
|
| 137 |
+
XScale += start_m * stride_x_scale
|
| 138 |
+
|
| 139 |
+
offs_ak = gl.arange(0, BLOCK_K, layout=gl.SliceLayout(0, blocked_a))
|
| 140 |
+
offs_bk = gl.arange(0, BLOCK_K, layout=gl.SliceLayout(1, blocked_b))
|
| 141 |
+
|
| 142 |
+
# =====================================================
|
| 143 |
+
# LICM: Pre-load all A matrix K-blocks outside N loop
|
| 144 |
+
# =====================================================
|
| 145 |
+
a_converted = ()
|
| 146 |
+
for k in gl.static_range(UNROLL_TIMES):
|
| 147 |
+
if EVEN_K:
|
| 148 |
+
a = gl.amd.cdna3.buffer_load(
|
| 149 |
+
X + k * BLOCK_K * stride_x_k,
|
| 150 |
+
offs_x_m[:, None] * stride_x_m + offs_ak[None, :] * stride_x_k,
|
| 151 |
+
mask=mask_m[:, None],
|
| 152 |
+
other=0.0,
|
| 153 |
+
)
|
| 154 |
+
else:
|
| 155 |
+
a = gl.amd.cdna3.buffer_load(
|
| 156 |
+
X + k * BLOCK_K * stride_x_k,
|
| 157 |
+
offs_x_m[:, None] * stride_x_m + offs_ak[None, :] * stride_x_k,
|
| 158 |
+
mask=mask_m[:, None] & (offs_ak[None, :] < K - k * BLOCK_K),
|
| 159 |
+
other=0.0,
|
| 160 |
+
)
|
| 161 |
+
a_converted = a_converted + (gl.convert_layout(a, mfma_a_layout),)
|
| 162 |
+
|
| 163 |
+
# =====================================================
|
| 164 |
+
# LICM: Pre-load per-token x_scale outside N loop
|
| 165 |
+
# =====================================================
|
| 166 |
+
x_scale = gl.amd.cdna3.buffer_load(
|
| 167 |
+
XScale, offs_x_m * stride_x_scale, mask=mask_m, other=1.0
|
| 168 |
+
)
|
| 169 |
+
x_scale_converted = gl.convert_layout(x_scale, gl.SliceLayout(1, mfma_layout))
|
| 170 |
+
|
| 171 |
+
# =====================================================
|
| 172 |
+
# LICM: Pre-load gammas outside N loop
|
| 173 |
+
# =====================================================
|
| 174 |
+
if Gammas is not None:
|
| 175 |
+
offs_gamma = block_id * BLOCK_M + gl.arange(
|
| 176 |
+
0, BLOCK_M, layout=gl.SliceLayout(1, mfma_layout)
|
| 177 |
+
)
|
| 178 |
+
gamma_mask = offs_gamma < M
|
| 179 |
+
gamma_vals = gl.amd.cdna3.buffer_load(
|
| 180 |
+
Gammas + start_m,
|
| 181 |
+
offs_gamma,
|
| 182 |
+
mask=gamma_mask,
|
| 183 |
+
other=0.0,
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
# =====================================================
|
| 187 |
+
# N-dimension loop (SUB_BLOCK_SIZE_N chunks)
|
| 188 |
+
# =====================================================
|
| 189 |
+
W_base = W + expt_id * stride_w_e
|
| 190 |
+
|
| 191 |
+
for n_start in range(0, BLOCK_N, SUB_BLOCK_SIZE_N):
|
| 192 |
+
offs_bn = (
|
| 193 |
+
pid_n * BLOCK_N
|
| 194 |
+
+ n_start
|
| 195 |
+
+ gl.arange(0, SUB_BLOCK_SIZE_N, layout=gl.SliceLayout(0, blocked_b))
|
| 196 |
+
) % N
|
| 197 |
+
|
| 198 |
+
# Accumulator in int32 for int8 x int8
|
| 199 |
+
accumulator = gl.zeros(
|
| 200 |
+
(BLOCK_M, SUB_BLOCK_SIZE_N), dtype=gl.int32, layout=mfma_layout
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
# -- Load B and compute MFMA (unrolled K via static_range) --
|
| 204 |
+
for k in gl.static_range(UNROLL_TIMES):
|
| 205 |
+
if EVEN_K:
|
| 206 |
+
b = gl.amd.cdna3.buffer_load(
|
| 207 |
+
W_base + k * BLOCK_K * stride_w_k,
|
| 208 |
+
offs_bk[:, None] * stride_w_k + offs_bn[None, :] * stride_w_n,
|
| 209 |
+
)
|
| 210 |
+
else:
|
| 211 |
+
b = gl.amd.cdna3.buffer_load(
|
| 212 |
+
W_base + k * BLOCK_K * stride_w_k,
|
| 213 |
+
offs_bk[:, None] * stride_w_k + offs_bn[None, :] * stride_w_n,
|
| 214 |
+
mask=offs_bk[:, None] < K - k * BLOCK_K,
|
| 215 |
+
other=0.0,
|
| 216 |
+
)
|
| 217 |
+
b_converted = gl.convert_layout(b, mfma_b_layout)
|
| 218 |
+
accumulator = gl.amd.cdna3.mfma(a_converted[k], b_converted, accumulator)
|
| 219 |
+
|
| 220 |
+
# -- Apply SmoothQuant scales: acc_fp32 = acc_int32 * x_scale * w_scale --
|
| 221 |
+
# Load per-channel weight scale for this N sub-block
|
| 222 |
+
offs_wscale_n = (
|
| 223 |
+
pid_n * BLOCK_N
|
| 224 |
+
+ n_start
|
| 225 |
+
+ gl.arange(0, SUB_BLOCK_SIZE_N, layout=gl.SliceLayout(0, mfma_layout))
|
| 226 |
+
)
|
| 227 |
+
w_scale = gl.amd.cdna3.buffer_load(
|
| 228 |
+
WScale + expt_id * stride_w_scale_e,
|
| 229 |
+
offs_wscale_n * stride_w_scale_n,
|
| 230 |
+
mask=offs_wscale_n < N,
|
| 231 |
+
other=1.0,
|
| 232 |
+
)
|
| 233 |
+
w_scale_converted = gl.convert_layout(w_scale, gl.SliceLayout(0, mfma_layout))
|
| 234 |
+
acc_fp32 = (
|
| 235 |
+
accumulator.to(gl.float32)
|
| 236 |
+
* x_scale_converted[:, None]
|
| 237 |
+
* w_scale_converted[None, :]
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
# -- Bias --
|
| 241 |
+
if B is not None:
|
| 242 |
+
bias = gl.amd.cdna3.buffer_load(
|
| 243 |
+
B + expt_id * stride_b_e,
|
| 244 |
+
offs_wscale_n,
|
| 245 |
+
mask=offs_wscale_n < N,
|
| 246 |
+
other=0.0,
|
| 247 |
+
)
|
| 248 |
+
bias_converted = gl.convert_layout(bias, gl.SliceLayout(0, mfma_layout))
|
| 249 |
+
acc_fp32 = acc_fp32 + bias_converted[None, :]
|
| 250 |
+
|
| 251 |
+
# -- Apply gammas (pre-loaded outside N loop) --
|
| 252 |
+
if Gammas is not None:
|
| 253 |
+
acc_fp32 = acc_fp32 * gamma_vals[:, None]
|
| 254 |
+
|
| 255 |
+
# -- Write back --
|
| 256 |
+
offs_cn = (
|
| 257 |
+
pid_n * BLOCK_N
|
| 258 |
+
+ n_start
|
| 259 |
+
+ gl.arange(0, SUB_BLOCK_SIZE_N, layout=gl.SliceLayout(0, blocked_d))
|
| 260 |
+
)
|
| 261 |
+
offs_ym = block_id * BLOCK_M + gl.arange(
|
| 262 |
+
0, BLOCK_M, layout=gl.SliceLayout(1, blocked_d)
|
| 263 |
+
)
|
| 264 |
+
ym_mask = offs_ym < M
|
| 265 |
+
cn_mask = offs_cn < N
|
| 266 |
+
|
| 267 |
+
out = gl.convert_layout(acc_fp32, blocked_d).to(Y.dtype.element_ty)
|
| 268 |
+
gl.amd.cdna3.buffer_store(
|
| 269 |
+
out,
|
| 270 |
+
Y + start_m * stride_y_m,
|
| 271 |
+
offs_ym[:, None] * stride_y_m + offs_cn[None, :] * stride_y_n,
|
| 272 |
+
mask=ym_mask[:, None] & cn_mask[None, :],
|
| 273 |
+
)
|
build/torch-rocm/_ops.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
def get_backend() -> str:
|
| 4 |
+
"""Detect the backend by inspecting torch."""
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
if hasattr(torch, "neuron"):
|
| 8 |
+
# Needs to be sorted before specific Torch builds, since Neuron
|
| 9 |
+
# extension can be loaded into e.g. CUDA Torch builds.
|
| 10 |
+
return "neuron"
|
| 11 |
+
elif torch.version.cuda is not None:
|
| 12 |
+
return "cuda"
|
| 13 |
+
elif torch.version.hip is not None:
|
| 14 |
+
return "rocm"
|
| 15 |
+
elif torch.backends.mps.is_available():
|
| 16 |
+
return "metal"
|
| 17 |
+
elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
|
| 18 |
+
return "xpu"
|
| 19 |
+
else:
|
| 20 |
+
return "cpu"
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _find_ops_name() -> str:
|
| 24 |
+
kernel_name = "aiter_kernels"
|
| 25 |
+
unique_id = "a7b6068"
|
| 26 |
+
backend = get_backend()
|
| 27 |
+
return f"_{kernel_name}_{backend}_{unique_id}"
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
_OPS_NAME = _find_ops_name()
|
| 31 |
+
|
| 32 |
+
ops = getattr(torch.ops, _OPS_NAME)
|
| 33 |
+
|
| 34 |
+
def add_op_namespace_prefix(op_name: str) -> str:
|
| 35 |
+
"""
|
| 36 |
+
Prefix op by namespace.
|
| 37 |
+
"""
|
| 38 |
+
return f"{_OPS_NAME}::{op_name}"
|
build/torch-rocm/_triton_kernels/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_triton_kernels/activation.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .quant.quant import _mxfp4_quant_op
|
| 2 |
+
from .quant.fused_fp8_quant import _fp8_quant_op
|
| 3 |
+
import triton
|
| 4 |
+
import triton.language as tl
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@triton.jit
|
| 8 |
+
def _silu_exp2(x):
|
| 9 |
+
return x / (1.0 + tl.exp2(-(x * 1.44269504089)))
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@triton.jit
|
| 13 |
+
def _silu(x):
|
| 14 |
+
return _silu_exp2(x)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@triton.jit
|
| 18 |
+
def fused_silu_mul_kernel(
|
| 19 |
+
inp_ptr,
|
| 20 |
+
out_ptr,
|
| 21 |
+
n_rows,
|
| 22 |
+
n_cols,
|
| 23 |
+
row_stride_in,
|
| 24 |
+
col_stride_in,
|
| 25 |
+
row_stride_out,
|
| 26 |
+
col_stride_out,
|
| 27 |
+
BLOCK_M: tl.constexpr,
|
| 28 |
+
BLOCK_N: tl.constexpr,
|
| 29 |
+
):
|
| 30 |
+
"""
|
| 31 |
+
SiLU on the first half of the last dimension, multiply by the second half.
|
| 32 |
+
Each row has 2 * n_cols input elements; writes n_cols outputs.
|
| 33 |
+
2D grid: axis 0 tiles rows (BLOCK_M), axis 1 tiles columns (BLOCK_N).
|
| 34 |
+
"""
|
| 35 |
+
m_pid = tl.program_id(0)
|
| 36 |
+
n_pid = tl.program_id(1)
|
| 37 |
+
m_offs = tl.arange(0, BLOCK_M)
|
| 38 |
+
n_offs = tl.arange(0, BLOCK_N)
|
| 39 |
+
row_idx = m_pid * BLOCK_M + m_offs
|
| 40 |
+
col_idx = n_pid * BLOCK_N + n_offs
|
| 41 |
+
|
| 42 |
+
row_in = row_idx * row_stride_in
|
| 43 |
+
row_out = row_idx * row_stride_out
|
| 44 |
+
|
| 45 |
+
first_half_ptrs = inp_ptr + row_in[:, None] + col_idx[None, :] * col_stride_in
|
| 46 |
+
second_half_ptrs = (
|
| 47 |
+
inp_ptr + row_in[:, None] + (n_cols + col_idx)[None, :] * col_stride_in
|
| 48 |
+
)
|
| 49 |
+
out_ptrs = out_ptr + row_out[:, None] + col_idx[None, :] * col_stride_out
|
| 50 |
+
|
| 51 |
+
mask = (row_idx < n_rows)[:, None] & (col_idx < n_cols)[None, :]
|
| 52 |
+
a = tl.load(first_half_ptrs, mask=mask, other=0.0).to(tl.float32)
|
| 53 |
+
silu_a = _silu_exp2(a).to(inp_ptr.dtype.element_ty)
|
| 54 |
+
b = tl.load(second_half_ptrs, mask=mask, other=0.0)
|
| 55 |
+
o = (silu_a * b).to(out_ptr.dtype.element_ty)
|
| 56 |
+
tl.store(out_ptrs, o, mask=mask)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
@triton.jit
|
| 60 |
+
def _tanh(x):
|
| 61 |
+
return 2 * tl.sigmoid(2 * x) - 1
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
@triton.jit
|
| 65 |
+
def _gelu(x):
|
| 66 |
+
M_SQRT1_2 = 0.70710678118654752440
|
| 67 |
+
ALPHA = M_SQRT1_2
|
| 68 |
+
return 0.5 * x * (1.0 + tl.erf(x * ALPHA))
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@triton.jit
|
| 72 |
+
def _gelu_tanh(x):
|
| 73 |
+
M_SQRT2 = 1.41421356237309504880
|
| 74 |
+
M_2_SQRTPI = 1.12837916709551257390
|
| 75 |
+
BETA = M_SQRT2 * M_2_SQRTPI * 0.5
|
| 76 |
+
KAPPA = 0.044715
|
| 77 |
+
x_cube = x * x * x
|
| 78 |
+
inner = BETA * (x + KAPPA * x_cube)
|
| 79 |
+
return 0.5 * x * (1.0 + _tanh(inner))
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@triton.jit
|
| 83 |
+
def _relu(x):
|
| 84 |
+
return tl.maximum(0.0, x)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _get_activation_from_str(activation: str):
|
| 88 |
+
mapping = {
|
| 89 |
+
"gelu": _gelu,
|
| 90 |
+
"gelu_tanh": _gelu_tanh,
|
| 91 |
+
"silu": _silu,
|
| 92 |
+
"silu_exp2": _silu_exp2,
|
| 93 |
+
"relu": _relu,
|
| 94 |
+
}
|
| 95 |
+
return mapping[activation]
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@triton.jit
|
| 99 |
+
def _apply_activation_from_str(x, activation: tl.constexpr):
|
| 100 |
+
if activation == "gelu":
|
| 101 |
+
return _gelu(x)
|
| 102 |
+
elif activation == "gelu_tanh":
|
| 103 |
+
return _gelu_tanh(x)
|
| 104 |
+
elif activation == "silu":
|
| 105 |
+
return _silu(x)
|
| 106 |
+
elif activation == "silu_exp2":
|
| 107 |
+
return _silu_exp2(x)
|
| 108 |
+
elif activation == "relu":
|
| 109 |
+
return _relu(x)
|
| 110 |
+
else:
|
| 111 |
+
return x # No activation if it is not recognized
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
@triton.heuristics(
|
| 115 |
+
{
|
| 116 |
+
"EVEN_M_N": lambda args: args["M"] % args["BLOCK_SIZE_M"] == 0
|
| 117 |
+
and args["N"] % (args["BLOCK_SIZE_N"] * args["NUM_ITER"]) == 0,
|
| 118 |
+
}
|
| 119 |
+
)
|
| 120 |
+
@triton.jit
|
| 121 |
+
def _act_mul_and_dynamic_mxfp4_quant_kernel(
|
| 122 |
+
x_ptr,
|
| 123 |
+
x_fp4_ptr,
|
| 124 |
+
bs_ptr,
|
| 125 |
+
stride_x_m_in,
|
| 126 |
+
stride_x_n_in,
|
| 127 |
+
stride_x_fp4_m_in,
|
| 128 |
+
stride_x_fp4_n_in,
|
| 129 |
+
stride_bs_m_in,
|
| 130 |
+
stride_bs_n_in,
|
| 131 |
+
M,
|
| 132 |
+
N,
|
| 133 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 134 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 135 |
+
NUM_ITER: tl.constexpr,
|
| 136 |
+
NUM_STAGES: tl.constexpr,
|
| 137 |
+
MXFP4_QUANT_BLOCK_SIZE: tl.constexpr,
|
| 138 |
+
EVEN_M_N: tl.constexpr,
|
| 139 |
+
SCALING_MODE: tl.constexpr,
|
| 140 |
+
ACTIVATION: tl.constexpr,
|
| 141 |
+
scaleN: tl.constexpr,
|
| 142 |
+
scaleM_pad: tl.constexpr,
|
| 143 |
+
scaleN_pad: tl.constexpr,
|
| 144 |
+
SHUFFLE: tl.constexpr,
|
| 145 |
+
):
|
| 146 |
+
pid_m = tl.program_id(0)
|
| 147 |
+
start_n = tl.program_id(1) * NUM_ITER
|
| 148 |
+
# cast strides to int64, in case M*N > max int32
|
| 149 |
+
stride_x_m = tl.cast(stride_x_m_in, tl.int64)
|
| 150 |
+
stride_x_n = tl.cast(stride_x_n_in, tl.int64)
|
| 151 |
+
stride_x_fp4_m = tl.cast(stride_x_fp4_m_in, tl.int64)
|
| 152 |
+
stride_x_fp4_n = tl.cast(stride_x_fp4_n_in, tl.int64)
|
| 153 |
+
stride_bs_m = tl.cast(stride_bs_m_in, tl.int64)
|
| 154 |
+
stride_bs_n = tl.cast(stride_bs_n_in, tl.int64)
|
| 155 |
+
|
| 156 |
+
NUM_QUANT_BLOCKS: tl.constexpr = BLOCK_SIZE_N // MXFP4_QUANT_BLOCK_SIZE
|
| 157 |
+
|
| 158 |
+
for pid_n in tl.range(start_n, min(start_n + NUM_ITER, N), num_stages=NUM_STAGES):
|
| 159 |
+
x_offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
|
| 160 |
+
x_offs_n = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
|
| 161 |
+
x_offs = x_offs_m[:, None] * stride_x_m + x_offs_n[None, :] * stride_x_n
|
| 162 |
+
|
| 163 |
+
if EVEN_M_N:
|
| 164 |
+
a = tl.load(x_ptr + x_offs, cache_modifier=".cg").to(tl.float32)
|
| 165 |
+
b = tl.load(x_ptr + x_offs + stride_x_n * N, cache_modifier=".cg").to(
|
| 166 |
+
tl.float32
|
| 167 |
+
)
|
| 168 |
+
else:
|
| 169 |
+
x_mask = (x_offs_m < M)[:, None] & (x_offs_n < N)[None, :]
|
| 170 |
+
a = tl.load(x_ptr + x_offs, mask=x_mask, cache_modifier=".cg").to(
|
| 171 |
+
tl.float32
|
| 172 |
+
)
|
| 173 |
+
# a and b can share the same mask
|
| 174 |
+
b = tl.load(
|
| 175 |
+
x_ptr + x_offs + stride_x_n * N, mask=x_mask, cache_modifier=".cg"
|
| 176 |
+
).to(tl.float32)
|
| 177 |
+
|
| 178 |
+
x = _apply_activation_from_str(a, ACTIVATION) * b
|
| 179 |
+
|
| 180 |
+
out_tensor, bs_e8m0 = _mxfp4_quant_op(
|
| 181 |
+
x, BLOCK_SIZE_N, BLOCK_SIZE_M, MXFP4_QUANT_BLOCK_SIZE
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
out_offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
|
| 185 |
+
out_offs_n = pid_n * BLOCK_SIZE_N // 2 + tl.arange(0, BLOCK_SIZE_N // 2)
|
| 186 |
+
out_offs = (
|
| 187 |
+
out_offs_m[:, None] * stride_x_fp4_m + out_offs_n[None, :] * stride_x_fp4_n
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
if EVEN_M_N:
|
| 191 |
+
tl.store(x_fp4_ptr + out_offs, out_tensor)
|
| 192 |
+
else:
|
| 193 |
+
out_mask = (out_offs_m < M)[:, None] & (out_offs_n < (N // 2))[None, :]
|
| 194 |
+
tl.store(x_fp4_ptr + out_offs, out_tensor, mask=out_mask)
|
| 195 |
+
|
| 196 |
+
bs_offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
|
| 197 |
+
bs_offs_n = pid_n * NUM_QUANT_BLOCKS + tl.arange(0, NUM_QUANT_BLOCKS)
|
| 198 |
+
if SHUFFLE:
|
| 199 |
+
bs_offs_0 = bs_offs_m[:, None] // 32
|
| 200 |
+
bs_offs_1 = bs_offs_m[:, None] % 32
|
| 201 |
+
bs_offs_2 = bs_offs_1 % 16
|
| 202 |
+
bs_offs_1 = bs_offs_1 // 16
|
| 203 |
+
bs_offs_3 = bs_offs_n[None, :] // 8
|
| 204 |
+
bs_offs_4 = bs_offs_n[None, :] % 8
|
| 205 |
+
bs_offs_5 = bs_offs_4 % 4
|
| 206 |
+
bs_offs_4 = bs_offs_4 // 4
|
| 207 |
+
bs_offs = (
|
| 208 |
+
bs_offs_1
|
| 209 |
+
+ bs_offs_4 * 2
|
| 210 |
+
+ bs_offs_2 * 2 * 2
|
| 211 |
+
+ bs_offs_5 * 2 * 2 * 16
|
| 212 |
+
+ bs_offs_3 * 2 * 2 * 16 * 4
|
| 213 |
+
+ bs_offs_0 * 2 * 16 * scaleN
|
| 214 |
+
)
|
| 215 |
+
bs_mask1 = (bs_offs_m < M)[:, None] & (bs_offs_n < scaleN)[None, :]
|
| 216 |
+
bs_mask = (bs_offs_m < scaleM_pad)[:, None] & (bs_offs_n < scaleN_pad)[
|
| 217 |
+
None, :
|
| 218 |
+
]
|
| 219 |
+
bs_e8m0 = tl.where(bs_mask1, bs_e8m0, 127)
|
| 220 |
+
else:
|
| 221 |
+
bs_offs = (
|
| 222 |
+
bs_offs_m[:, None] * stride_bs_m + bs_offs_n[None, :] * stride_bs_n
|
| 223 |
+
)
|
| 224 |
+
bs_mask = (bs_offs_m < M)[:, None] & (bs_offs_n < scaleN)[None, :]
|
| 225 |
+
if EVEN_M_N:
|
| 226 |
+
tl.store(bs_ptr + bs_offs, bs_e8m0)
|
| 227 |
+
else:
|
| 228 |
+
|
| 229 |
+
tl.store(
|
| 230 |
+
bs_ptr + bs_offs,
|
| 231 |
+
bs_e8m0,
|
| 232 |
+
mask=bs_mask,
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
@triton.heuristics(
|
| 237 |
+
{
|
| 238 |
+
"EVEN_N": lambda args: args["N"] % args["BLOCK_SIZE_N"] == 0,
|
| 239 |
+
}
|
| 240 |
+
)
|
| 241 |
+
@triton.jit
|
| 242 |
+
def _act_mul_and_dynamic_fp8_group_quant_kernel(
|
| 243 |
+
x_ptr,
|
| 244 |
+
x_fp8_ptr,
|
| 245 |
+
x_bs_ptr,
|
| 246 |
+
stride_x_m_in,
|
| 247 |
+
stride_x_n_in,
|
| 248 |
+
stride_x_fp8_m_in,
|
| 249 |
+
stride_x_fp8_n_in,
|
| 250 |
+
stride_bs_m_in,
|
| 251 |
+
stride_bs_n_in,
|
| 252 |
+
N,
|
| 253 |
+
ACTIVATION: tl.constexpr,
|
| 254 |
+
scaleN: tl.constexpr,
|
| 255 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 256 |
+
QUANT_BLOCK_SIZE: tl.constexpr,
|
| 257 |
+
DTYPE_MAX: tl.constexpr,
|
| 258 |
+
DTYPE_MIN: tl.constexpr,
|
| 259 |
+
EVEN_N: tl.constexpr,
|
| 260 |
+
):
|
| 261 |
+
pid_m = tl.program_id(0)
|
| 262 |
+
pid_n = tl.program_id(1)
|
| 263 |
+
# cast strides to int64, in case M*N > max int32
|
| 264 |
+
stride_x_m = tl.cast(stride_x_m_in, tl.int64)
|
| 265 |
+
stride_x_n = tl.cast(stride_x_n_in, tl.int64)
|
| 266 |
+
stride_x_fp8_m = tl.cast(stride_x_fp8_m_in, tl.int64)
|
| 267 |
+
stride_x_fp8_n = tl.cast(stride_x_fp8_n_in, tl.int64)
|
| 268 |
+
stride_bs_m = tl.cast(stride_bs_m_in, tl.int64)
|
| 269 |
+
stride_bs_n = tl.cast(stride_bs_n_in, tl.int64)
|
| 270 |
+
NUM_QUANT_BLOCKS: tl.constexpr = BLOCK_SIZE_N // QUANT_BLOCK_SIZE
|
| 271 |
+
|
| 272 |
+
x_offs_n = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
|
| 273 |
+
x_offs = pid_m * stride_x_m + x_offs_n * stride_x_n
|
| 274 |
+
|
| 275 |
+
if EVEN_N:
|
| 276 |
+
a = tl.load(x_ptr + x_offs, cache_modifier=".cg").to(tl.float32)
|
| 277 |
+
b = tl.load(x_ptr + x_offs + stride_x_n * N, cache_modifier=".cg").to(
|
| 278 |
+
tl.float32
|
| 279 |
+
)
|
| 280 |
+
else:
|
| 281 |
+
x_mask = x_offs_n < N
|
| 282 |
+
a = tl.load(x_ptr + x_offs, mask=x_mask, cache_modifier=".cg").to(tl.float32)
|
| 283 |
+
# a and b can share the same mask
|
| 284 |
+
b = tl.load(
|
| 285 |
+
x_ptr + x_offs + stride_x_n * N, mask=x_mask, cache_modifier=".cg"
|
| 286 |
+
).to(tl.float32)
|
| 287 |
+
|
| 288 |
+
x = _apply_activation_from_str(a, ACTIVATION) * b
|
| 289 |
+
|
| 290 |
+
x_fp8, x_bs = _fp8_quant_op(
|
| 291 |
+
x, 1, BLOCK_SIZE_N, QUANT_BLOCK_SIZE, DTYPE_MAX, DTYPE_MIN
|
| 292 |
+
)
|
| 293 |
+
x_fp8 = tl.ravel(x_fp8)
|
| 294 |
+
x_bs = tl.ravel(x_bs)
|
| 295 |
+
|
| 296 |
+
out_offs_n = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
|
| 297 |
+
out_offs = pid_m * stride_x_fp8_m + out_offs_n * stride_x_fp8_n
|
| 298 |
+
|
| 299 |
+
if EVEN_N:
|
| 300 |
+
tl.store(x_fp8_ptr + out_offs, x_fp8.to(x_fp8_ptr.dtype.element_ty))
|
| 301 |
+
else:
|
| 302 |
+
out_mask = out_offs_n < N
|
| 303 |
+
tl.store(
|
| 304 |
+
x_fp8_ptr + out_offs, x_fp8.to(x_fp8_ptr.dtype.element_ty), mask=out_mask
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
bs_offs_n = pid_n * NUM_QUANT_BLOCKS + tl.arange(0, NUM_QUANT_BLOCKS)
|
| 308 |
+
bs_offs = pid_m * stride_bs_m + bs_offs_n * stride_bs_n
|
| 309 |
+
if EVEN_N:
|
| 310 |
+
tl.store(x_bs_ptr + bs_offs, x_bs.to(x_bs_ptr.dtype.element_ty))
|
| 311 |
+
else:
|
| 312 |
+
bs_mask = bs_offs_n < scaleN
|
| 313 |
+
tl.store(
|
| 314 |
+
x_bs_ptr + bs_offs,
|
| 315 |
+
x_bs.to(x_bs_ptr.dtype.element_ty),
|
| 316 |
+
mask=bs_mask,
|
| 317 |
+
)
|
build/torch-rocm/_triton_kernels/causal_conv1d.py
ADDED
|
@@ -0,0 +1,631 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
PAD_SLOT_ID = -1
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@triton.jit()
|
| 8 |
+
def _causal_conv1d_fwd_kernel( # continuous batching
|
| 9 |
+
# Pointers to matrices
|
| 10 |
+
x_ptr, # (dim, cu_seqlen) holding `batch` of actual sequences + padded sequences
|
| 11 |
+
w_ptr, # (dim, width)
|
| 12 |
+
bias_ptr,
|
| 13 |
+
initial_states_ptr, # conv_states_ptr
|
| 14 |
+
cache_indices_ptr, # conv_state_indices_ptr
|
| 15 |
+
has_initial_states_ptr,
|
| 16 |
+
query_start_loc_ptr,
|
| 17 |
+
o_ptr, # (dim, seqlen) - actually pointing to x_ptr
|
| 18 |
+
# Matrix dimensions
|
| 19 |
+
dim: tl.constexpr,
|
| 20 |
+
seqlen: tl.int32, # cu_seqlen
|
| 21 |
+
num_cache_lines: tl.constexpr, # added to support vLLM larger cache lines
|
| 22 |
+
# Strides
|
| 23 |
+
stride_x_seq: tl.constexpr, # stride to get to next sequence,
|
| 24 |
+
stride_x_dim: tl.constexpr, # stride to get to next feature-value,
|
| 25 |
+
stride_x_token: tl.constexpr, # stride to get to next token (same feature-index, same sequence-index)
|
| 26 |
+
stride_w_dim: tl.constexpr, # stride to get to next dim-axis value
|
| 27 |
+
stride_w_width: tl.constexpr, # stride to get to next width-axis value
|
| 28 |
+
stride_istate_seq: tl.constexpr,
|
| 29 |
+
stride_istate_dim: tl.constexpr,
|
| 30 |
+
stride_istate_token: tl.constexpr,
|
| 31 |
+
stride_o_seq: tl.constexpr,
|
| 32 |
+
stride_o_dim: tl.constexpr,
|
| 33 |
+
stride_o_token: tl.constexpr,
|
| 34 |
+
# others
|
| 35 |
+
pad_slot_id: tl.constexpr,
|
| 36 |
+
# Meta-parameters
|
| 37 |
+
HAS_BIAS: tl.constexpr,
|
| 38 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 39 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 40 |
+
HAS_INITIAL_STATES: tl.constexpr,
|
| 41 |
+
HAS_CACHE: tl.constexpr,
|
| 42 |
+
IS_CONTINUOUS_BATCHING: tl.constexpr,
|
| 43 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 44 |
+
NP2_STATELEN: tl.constexpr,
|
| 45 |
+
BLOCK_M: tl.constexpr,
|
| 46 |
+
BLOCK_N: tl.constexpr,
|
| 47 |
+
):
|
| 48 |
+
conv_states_ptr = initial_states_ptr
|
| 49 |
+
conv_state_indices_ptr = cache_indices_ptr
|
| 50 |
+
stride_conv_state_seq = stride_istate_seq
|
| 51 |
+
stride_conv_state_dim = stride_istate_dim
|
| 52 |
+
stride_conv_state_tok = stride_istate_token
|
| 53 |
+
state_len = (
|
| 54 |
+
KERNEL_WIDTH - 1
|
| 55 |
+
) # can be passed via argument if it's not the same as this value
|
| 56 |
+
|
| 57 |
+
# one program handles one chunk in a single sequence
|
| 58 |
+
# rather than mixing sequences - to make updating initial_states across sequences efficiently
|
| 59 |
+
|
| 60 |
+
# single-sequence id
|
| 61 |
+
idx_seq = tl.program_id(0)
|
| 62 |
+
chunk_offset = tl.program_id(1)
|
| 63 |
+
|
| 64 |
+
# BLOCK_N elements along the feature-dimension (channel)
|
| 65 |
+
idx_feats = tl.program_id(2) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 66 |
+
|
| 67 |
+
if idx_seq == pad_slot_id:
|
| 68 |
+
return
|
| 69 |
+
|
| 70 |
+
sequence_start_index = tl.load(query_start_loc_ptr + idx_seq)
|
| 71 |
+
sequence_end_index = tl.load(query_start_loc_ptr + idx_seq + 1)
|
| 72 |
+
# find the actual sequence length
|
| 73 |
+
seqlen = sequence_end_index - sequence_start_index
|
| 74 |
+
|
| 75 |
+
token_offset = BLOCK_M * chunk_offset
|
| 76 |
+
segment_len = min(BLOCK_M, seqlen - token_offset)
|
| 77 |
+
|
| 78 |
+
if segment_len <= 0:
|
| 79 |
+
return
|
| 80 |
+
|
| 81 |
+
# base of the sequence
|
| 82 |
+
x_base = (
|
| 83 |
+
x_ptr + sequence_start_index * stride_x_token + idx_feats * stride_x_dim
|
| 84 |
+
) # [BLOCK_N,]
|
| 85 |
+
|
| 86 |
+
if IS_CONTINUOUS_BATCHING:
|
| 87 |
+
# cache_idx
|
| 88 |
+
conv_state_batch_coord = tl.load(conv_state_indices_ptr + idx_seq).to(tl.int64)
|
| 89 |
+
else:
|
| 90 |
+
# cache_idx
|
| 91 |
+
conv_state_batch_coord = idx_seq
|
| 92 |
+
if USE_PAD_SLOT: # noqa
|
| 93 |
+
if conv_state_batch_coord == pad_slot_id:
|
| 94 |
+
# not processing as this is not the actual sequence
|
| 95 |
+
return
|
| 96 |
+
conv_states_base = (
|
| 97 |
+
conv_states_ptr
|
| 98 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 99 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 100 |
+
) # [BLOCK_N,]
|
| 101 |
+
|
| 102 |
+
w_base = w_ptr + (idx_feats * stride_w_dim) # [BLOCK_N,]
|
| 103 |
+
|
| 104 |
+
# Does 2 things:
|
| 105 |
+
# 1. READ prior-block init-state data - [done by every Triton programs]
|
| 106 |
+
# 2. update conv_state with new data [only by the Triton program handles chunk_offset=0]
|
| 107 |
+
if chunk_offset == 0:
|
| 108 |
+
# read from conv_states
|
| 109 |
+
load_init_state = False
|
| 110 |
+
if HAS_INITIAL_STATES: # the new HAS_INITIAL_STATES
|
| 111 |
+
load_init_state = tl.load(has_initial_states_ptr + idx_seq).to(tl.int1)
|
| 112 |
+
if load_init_state:
|
| 113 |
+
# load from conv_states
|
| 114 |
+
prior_tokens = conv_states_base + (state_len - 1) * stride_conv_state_tok
|
| 115 |
+
mask_w = idx_feats < dim
|
| 116 |
+
if KERNEL_WIDTH == 2:
|
| 117 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 118 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 119 |
+
if KERNEL_WIDTH == 3:
|
| 120 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 121 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 122 |
+
conv_states_ptrs = prior_tokens - 1 * stride_conv_state_tok # [BLOCK_N]
|
| 123 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 124 |
+
if KERNEL_WIDTH == 4:
|
| 125 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 126 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 127 |
+
conv_states_ptrs = prior_tokens - 1 * stride_conv_state_tok # [BLOCK_N]
|
| 128 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 129 |
+
conv_states_ptrs = prior_tokens - 2 * stride_conv_state_tok # [BLOCK_N]
|
| 130 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 131 |
+
if KERNEL_WIDTH == 5:
|
| 132 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 133 |
+
col3 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 134 |
+
conv_states_ptrs = prior_tokens - 1 * stride_conv_state_tok # [BLOCK_N]
|
| 135 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 136 |
+
conv_states_ptrs = prior_tokens - 2 * stride_conv_state_tok # [BLOCK_N]
|
| 137 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 138 |
+
conv_states_ptrs = prior_tokens - 3 * stride_conv_state_tok # [BLOCK_N]
|
| 139 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 140 |
+
else:
|
| 141 |
+
# prior-tokens are zeros
|
| 142 |
+
if KERNEL_WIDTH >= 2: # STRATEGY1
|
| 143 |
+
# first chunk and does not have prior-token, so just set to 0
|
| 144 |
+
col0 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 145 |
+
if KERNEL_WIDTH >= 3: # STRATEGY1
|
| 146 |
+
col1 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 147 |
+
if KERNEL_WIDTH >= 4: # STRATEGY1
|
| 148 |
+
col2 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 149 |
+
if KERNEL_WIDTH >= 5: # STRATEGY1
|
| 150 |
+
col3 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 151 |
+
|
| 152 |
+
# STEP 2:
|
| 153 |
+
# here prepare data for updating conv_state
|
| 154 |
+
if (
|
| 155 |
+
state_len <= seqlen
|
| 156 |
+
): # SMALL_CACHE=True (only move part of 'x' into conv_state cache)
|
| 157 |
+
# just read from 'x'
|
| 158 |
+
# copy 'x' data to conv_state
|
| 159 |
+
# load only 'x' data (and set 0 before 'x' if seqlen < state_len)
|
| 160 |
+
idx_tokens_last = (seqlen - state_len) + tl.arange(
|
| 161 |
+
0, NP2_STATELEN
|
| 162 |
+
) # [BLOCK_M]
|
| 163 |
+
x_ptrs = (
|
| 164 |
+
x_ptr
|
| 165 |
+
+ ((sequence_start_index + idx_tokens_last) * stride_x_token)[:, None]
|
| 166 |
+
+ (idx_feats * stride_x_dim)[None, :]
|
| 167 |
+
) # [BLOCK_M,BLOCK_N,]
|
| 168 |
+
mask_x = (
|
| 169 |
+
(idx_tokens_last >= 0)[:, None]
|
| 170 |
+
& (idx_tokens_last < seqlen)[:, None]
|
| 171 |
+
& (idx_feats < dim)[None, :]
|
| 172 |
+
) # token-index # token-index # feature-index
|
| 173 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 174 |
+
new_conv_state = tl.load(x_ptrs, mask_x, 0.0)
|
| 175 |
+
idx_tokens_conv = tl.arange(0, NP2_STATELEN) # [BLOCK_M]
|
| 176 |
+
conv_states_ptrs_target = (
|
| 177 |
+
conv_states_base[None, :]
|
| 178 |
+
+ (idx_tokens_conv * stride_conv_state_tok)[:, None]
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
mask = (idx_tokens_conv < state_len)[:, None] & (idx_feats < dim)[None, :]
|
| 182 |
+
tl.debug_barrier() # NOTE: use this due to bug in Triton compiler
|
| 183 |
+
tl.store(conv_states_ptrs_target, new_conv_state, mask)
|
| 184 |
+
|
| 185 |
+
else:
|
| 186 |
+
if load_init_state:
|
| 187 |
+
# update conv_state by shifting left, i.e. take last few cols from conv_state + cols from 'x'
|
| 188 |
+
idx_tokens_conv = tl.arange(0, NP2_STATELEN) # [BLOCK_M]
|
| 189 |
+
|
| 190 |
+
conv_states_ptrs_source = (
|
| 191 |
+
conv_states_ptr
|
| 192 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 193 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 194 |
+
+ ((idx_tokens_conv + seqlen) * stride_conv_state_tok)[:, None]
|
| 195 |
+
) # [BLOCK_M, BLOCK_N]
|
| 196 |
+
mask = (
|
| 197 |
+
(conv_state_batch_coord < num_cache_lines)
|
| 198 |
+
& ((idx_tokens_conv + seqlen) < state_len)[:, None]
|
| 199 |
+
& (idx_feats < dim)[None, :]
|
| 200 |
+
)
|
| 201 |
+
conv_state = tl.load(conv_states_ptrs_source, mask, other=0.0)
|
| 202 |
+
|
| 203 |
+
VAL = state_len - seqlen
|
| 204 |
+
|
| 205 |
+
x_ptrs = (
|
| 206 |
+
x_base[None, :]
|
| 207 |
+
+ ((idx_tokens_conv - VAL) * stride_x_token)[:, None]
|
| 208 |
+
) # [BLOCK_M, BLOCK_N]
|
| 209 |
+
|
| 210 |
+
mask_x = (
|
| 211 |
+
(idx_tokens_conv - VAL >= 0)[:, None]
|
| 212 |
+
& (idx_tokens_conv - VAL < seqlen)[:, None]
|
| 213 |
+
& (idx_feats < dim)[None, :]
|
| 214 |
+
) # token-index # token-index # feature-index
|
| 215 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 216 |
+
|
| 217 |
+
tl.debug_barrier() # need this due to the bug in tl.where not enforcing this when data is the result of another tl.load
|
| 218 |
+
new_conv_state = tl.where(
|
| 219 |
+
mask, conv_state, loaded_x
|
| 220 |
+
) # BUG in 'tl.where' which requires a barrier before this
|
| 221 |
+
conv_states_ptrs_target = (
|
| 222 |
+
conv_states_base
|
| 223 |
+
+ (idx_tokens_conv * stride_conv_state_tok)[:, None]
|
| 224 |
+
) # [BLOCK_M, BLOCK_N]
|
| 225 |
+
mask = (idx_tokens_conv < state_len)[:, None] & (idx_feats < dim)[
|
| 226 |
+
None, :
|
| 227 |
+
]
|
| 228 |
+
tl.store(conv_states_ptrs_target, new_conv_state, mask)
|
| 229 |
+
else: # load_init_state == False
|
| 230 |
+
# update conv_state by shifting left, BUT
|
| 231 |
+
# set cols prior to 'x' as zeros + cols from 'x'
|
| 232 |
+
idx_tokens_conv = tl.arange(0, NP2_STATELEN) # [BLOCK_M]
|
| 233 |
+
|
| 234 |
+
VAL = state_len - seqlen
|
| 235 |
+
|
| 236 |
+
x_ptrs = (
|
| 237 |
+
x_base[None, :]
|
| 238 |
+
+ ((idx_tokens_conv - VAL) * stride_x_token)[:, None]
|
| 239 |
+
) # [BLOCK_M, BLOCK_N]
|
| 240 |
+
|
| 241 |
+
mask_x = (
|
| 242 |
+
(idx_tokens_conv - VAL >= 0)[:, None]
|
| 243 |
+
& (idx_tokens_conv - VAL < seqlen)[:, None]
|
| 244 |
+
& (idx_feats < dim)[None, :]
|
| 245 |
+
) # token-index # token-index # feature-index
|
| 246 |
+
new_conv_state = tl.load(x_ptrs, mask_x, 0.0)
|
| 247 |
+
|
| 248 |
+
conv_states_ptrs_target = (
|
| 249 |
+
conv_states_base
|
| 250 |
+
+ (idx_tokens_conv * stride_conv_state_tok)[:, None]
|
| 251 |
+
) # [BLOCK_M, BLOCK_N]
|
| 252 |
+
mask = (idx_tokens_conv < state_len)[:, None] & (idx_feats < dim)[
|
| 253 |
+
None, :
|
| 254 |
+
]
|
| 255 |
+
tl.store(conv_states_ptrs_target, new_conv_state, mask)
|
| 256 |
+
|
| 257 |
+
else: # chunk_offset > 0
|
| 258 |
+
# read prior-token data from `x`
|
| 259 |
+
load_init_state = True
|
| 260 |
+
prior_tokens = x_base + (token_offset - 1) * stride_x_token
|
| 261 |
+
mask_w = idx_feats < dim
|
| 262 |
+
if KERNEL_WIDTH == 2:
|
| 263 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 264 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 265 |
+
if KERNEL_WIDTH == 3:
|
| 266 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 267 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 268 |
+
conv_states_ptrs = prior_tokens - 1 * stride_x_token # [BLOCK_N]
|
| 269 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 270 |
+
if KERNEL_WIDTH == 4:
|
| 271 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 272 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 273 |
+
conv_states_ptrs = prior_tokens - 1 * stride_x_token # [BLOCK_N]
|
| 274 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 275 |
+
conv_states_ptrs = prior_tokens - 2 * stride_x_token # [BLOCK_N]
|
| 276 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 277 |
+
if KERNEL_WIDTH == 5:
|
| 278 |
+
# ruff: noqa: F841
|
| 279 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 280 |
+
col3 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 281 |
+
conv_states_ptrs = prior_tokens - 1 * stride_x_token # [BLOCK_N]
|
| 282 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 283 |
+
conv_states_ptrs = prior_tokens - 2 * stride_x_token # [BLOCK_N]
|
| 284 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 285 |
+
conv_states_ptrs = prior_tokens - 3 * stride_x_token # [BLOCK_N]
|
| 286 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0, cache_modifier=".ca")
|
| 287 |
+
|
| 288 |
+
if HAS_BIAS:
|
| 289 |
+
bias = bias_ptr + idx_feats
|
| 290 |
+
mask_bias = idx_feats < dim
|
| 291 |
+
acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(
|
| 292 |
+
tl.float32
|
| 293 |
+
) # [BLOCK_N]
|
| 294 |
+
else:
|
| 295 |
+
acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 296 |
+
|
| 297 |
+
x_base_1d = x_base + token_offset * stride_x_token # starting of chunk
|
| 298 |
+
|
| 299 |
+
# PRE-LOAD WEIGHTS
|
| 300 |
+
mask_w = idx_feats < dim
|
| 301 |
+
if KERNEL_WIDTH >= 2:
|
| 302 |
+
w_ptrs = w_base + (0 * stride_w_width) # [BLOCK_N] tensor
|
| 303 |
+
w_col0 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 304 |
+
w_ptrs = w_base + (1 * stride_w_width) # [BLOCK_N] tensor
|
| 305 |
+
w_col1 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 306 |
+
if KERNEL_WIDTH >= 3:
|
| 307 |
+
w_ptrs = w_base + (2 * stride_w_width) # [BLOCK_N] tensor
|
| 308 |
+
w_col2 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 309 |
+
if KERNEL_WIDTH >= 4:
|
| 310 |
+
w_ptrs = w_base + (3 * stride_w_width) # [BLOCK_N] tensor
|
| 311 |
+
w_col3 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 312 |
+
mask_x_1d = idx_feats < dim
|
| 313 |
+
for idx_token in range(segment_len):
|
| 314 |
+
acc = acc_preload
|
| 315 |
+
|
| 316 |
+
matrix_w = w_col0
|
| 317 |
+
matrix_x = col0
|
| 318 |
+
for j in tl.static_range(KERNEL_WIDTH):
|
| 319 |
+
|
| 320 |
+
if KERNEL_WIDTH == 2:
|
| 321 |
+
if j == 1: # KERNEL_WIDTH-1:
|
| 322 |
+
matrix_w = w_col1
|
| 323 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token # [BLOCK_N]
|
| 324 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 325 |
+
elif KERNEL_WIDTH == 3:
|
| 326 |
+
if j == 1:
|
| 327 |
+
matrix_w = w_col1
|
| 328 |
+
matrix_x = col1
|
| 329 |
+
elif j == 2:
|
| 330 |
+
matrix_w = w_col2
|
| 331 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token # [BLOCK_N]
|
| 332 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 333 |
+
elif KERNEL_WIDTH == 4:
|
| 334 |
+
if j == 1:
|
| 335 |
+
matrix_w = w_col1
|
| 336 |
+
matrix_x = col1
|
| 337 |
+
elif j == 2:
|
| 338 |
+
matrix_w = w_col2
|
| 339 |
+
matrix_x = col2
|
| 340 |
+
elif j == 3:
|
| 341 |
+
matrix_w = w_col3
|
| 342 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token # [BLOCK_N]
|
| 343 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 344 |
+
|
| 345 |
+
acc += matrix_x * matrix_w # [BLOCK_N]
|
| 346 |
+
|
| 347 |
+
if KERNEL_WIDTH == 2:
|
| 348 |
+
col0 = matrix_x
|
| 349 |
+
elif KERNEL_WIDTH == 3:
|
| 350 |
+
col0 = col1
|
| 351 |
+
col1 = matrix_x
|
| 352 |
+
elif KERNEL_WIDTH == 4:
|
| 353 |
+
col0 = col1
|
| 354 |
+
col1 = col2
|
| 355 |
+
col2 = matrix_x
|
| 356 |
+
|
| 357 |
+
if SILU_ACTIVATION:
|
| 358 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 359 |
+
mask_1d = (idx_token < segment_len) & (
|
| 360 |
+
idx_feats < dim
|
| 361 |
+
) # token-index # feature-index
|
| 362 |
+
o_ptrs = (
|
| 363 |
+
o_ptr
|
| 364 |
+
+ (sequence_start_index + token_offset + idx_token) * stride_o_token
|
| 365 |
+
+ (idx_feats * stride_o_dim)
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
tl.store(o_ptrs, acc, mask=mask_1d)
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
@triton.jit()
|
| 372 |
+
def _causal_conv1d_update_kernel(
|
| 373 |
+
# Pointers to matrices
|
| 374 |
+
x_ptr, # (batch, dim, seqlen)
|
| 375 |
+
w_ptr, # (dim, width)
|
| 376 |
+
bias_ptr,
|
| 377 |
+
conv_state_ptr,
|
| 378 |
+
cache_seqlens_ptr, # circular buffer
|
| 379 |
+
conv_state_indices_ptr,
|
| 380 |
+
num_accepted_tokens_ptr,
|
| 381 |
+
intermediate_conv_window_ptr,
|
| 382 |
+
o_ptr, # (batch, dim, seqlen)
|
| 383 |
+
# Matrix dimensions
|
| 384 |
+
batch: int,
|
| 385 |
+
dim: tl.constexpr,
|
| 386 |
+
seqlen: tl.constexpr,
|
| 387 |
+
state_len: tl.constexpr,
|
| 388 |
+
num_cache_lines: tl.constexpr, # added to support vLLM larger cache lines
|
| 389 |
+
# Strides
|
| 390 |
+
stride_x_seq: tl.constexpr,
|
| 391 |
+
stride_x_dim: tl.constexpr,
|
| 392 |
+
stride_x_token: tl.constexpr,
|
| 393 |
+
stride_w_dim: tl.constexpr,
|
| 394 |
+
stride_w_width: tl.constexpr,
|
| 395 |
+
stride_conv_state_seq: tl.constexpr,
|
| 396 |
+
stride_conv_state_dim: tl.constexpr,
|
| 397 |
+
stride_conv_state_tok: tl.constexpr,
|
| 398 |
+
stride_state_indices: tl.constexpr,
|
| 399 |
+
stride_inter_seq: tl.constexpr,
|
| 400 |
+
stride_inter_step: tl.constexpr,
|
| 401 |
+
stride_inter_dim: tl.constexpr,
|
| 402 |
+
stride_inter_win: tl.constexpr,
|
| 403 |
+
stride_o_seq: tl.constexpr,
|
| 404 |
+
stride_o_dim: tl.constexpr,
|
| 405 |
+
stride_o_token: tl.constexpr,
|
| 406 |
+
# others
|
| 407 |
+
pad_slot_id: tl.constexpr,
|
| 408 |
+
# Meta-parameters
|
| 409 |
+
HAS_BIAS: tl.constexpr,
|
| 410 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 411 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 412 |
+
IS_CONTINUOUS_BATCHING: tl.constexpr,
|
| 413 |
+
IS_SPEC_DECODING: tl.constexpr,
|
| 414 |
+
NP2_STATELEN: tl.constexpr,
|
| 415 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 416 |
+
BLOCK_N: tl.constexpr,
|
| 417 |
+
SAVE_INTERMEDIATE: tl.constexpr,
|
| 418 |
+
):
|
| 419 |
+
# ruff: noqa: E501
|
| 420 |
+
idx_seq = tl.program_id(0)
|
| 421 |
+
if idx_seq >= batch:
|
| 422 |
+
return
|
| 423 |
+
|
| 424 |
+
# [BLOCK_N,] elements along the feature-dimension (channel)
|
| 425 |
+
idx_feats = tl.program_id(1) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 426 |
+
|
| 427 |
+
if IS_CONTINUOUS_BATCHING:
|
| 428 |
+
# mask = idx_seq < batch
|
| 429 |
+
conv_state_batch_coord = tl.load(
|
| 430 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices
|
| 431 |
+
).to(tl.int64)
|
| 432 |
+
else:
|
| 433 |
+
conv_state_batch_coord = idx_seq
|
| 434 |
+
if USE_PAD_SLOT: # noqa
|
| 435 |
+
if conv_state_batch_coord == pad_slot_id:
|
| 436 |
+
# not processing as this is not the actual sequence
|
| 437 |
+
return
|
| 438 |
+
|
| 439 |
+
if IS_SPEC_DECODING:
|
| 440 |
+
# The rolling of conv state:
|
| 441 |
+
#
|
| 442 |
+
# Before forward, the conv_state is:
|
| 443 |
+
# [history1, history2, ..., historyM].
|
| 444 |
+
#
|
| 445 |
+
# After forward, the conv_state becomes:
|
| 446 |
+
# [history2, ..., historyM, draft1, draft2, ..., draftN].
|
| 447 |
+
#
|
| 448 |
+
# After acceptance, it becomes:
|
| 449 |
+
#
|
| 450 |
+
# - accept 1 tokens: [history2, ..., historyM, draft1]
|
| 451 |
+
# - accept 2 tokens: [history3, ..., historyM, draft1, draft2]
|
| 452 |
+
# - and so on.
|
| 453 |
+
conv_state_token_offset = tl.load(num_accepted_tokens_ptr + idx_seq) - 1
|
| 454 |
+
else:
|
| 455 |
+
conv_state_token_offset = 0
|
| 456 |
+
|
| 457 |
+
# STEP 1: READ init_state data
|
| 458 |
+
conv_states_base = (
|
| 459 |
+
conv_state_ptr
|
| 460 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 461 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 462 |
+
)
|
| 463 |
+
mask_w = idx_feats < dim
|
| 464 |
+
|
| 465 |
+
prior_tokens = conv_states_base + conv_state_token_offset * stride_conv_state_tok
|
| 466 |
+
if KERNEL_WIDTH >= 2:
|
| 467 |
+
conv_states_ptrs = prior_tokens # [BLOCK_N]
|
| 468 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 469 |
+
if KERNEL_WIDTH >= 3:
|
| 470 |
+
conv_states_ptrs = prior_tokens + 1 * stride_conv_state_tok # [BLOCK_N]
|
| 471 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 472 |
+
if KERNEL_WIDTH >= 4:
|
| 473 |
+
conv_states_ptrs = prior_tokens + 2 * stride_conv_state_tok # [BLOCK_N]
|
| 474 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 475 |
+
if KERNEL_WIDTH == 5:
|
| 476 |
+
conv_states_ptrs = prior_tokens + 3 * stride_conv_state_tok # [BLOCK_N]
|
| 477 |
+
col3 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 478 |
+
|
| 479 |
+
# STEP 2: assume state_len > seqlen
|
| 480 |
+
idx_tokens = tl.arange(0, NP2_STATELEN) # [BLOCK_M]
|
| 481 |
+
|
| 482 |
+
# The conv_state updates works in a sliding window manner,
|
| 483 |
+
# at each forward pass, the tokens are shift by 1, so we
|
| 484 |
+
# load since idx_tokens + 1.
|
| 485 |
+
conv_state_ptrs_source = (
|
| 486 |
+
conv_state_ptr
|
| 487 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 488 |
+
+ conv_state_token_offset * stride_conv_state_tok
|
| 489 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 490 |
+
+ ((idx_tokens + (1 if IS_SPEC_DECODING else seqlen)) * stride_conv_state_tok)[
|
| 491 |
+
:, None
|
| 492 |
+
]
|
| 493 |
+
) # [BLOCK_M, BLOCK_N]
|
| 494 |
+
mask = (
|
| 495 |
+
(conv_state_batch_coord < num_cache_lines)
|
| 496 |
+
& ((idx_tokens + seqlen) < state_len)[:, None]
|
| 497 |
+
& (idx_feats < dim)[None, :]
|
| 498 |
+
)
|
| 499 |
+
conv_state = tl.load(conv_state_ptrs_source, mask, other=0.0)
|
| 500 |
+
|
| 501 |
+
VAL = state_len - seqlen
|
| 502 |
+
x_base = x_ptr + (idx_seq * stride_x_seq) + (idx_feats * stride_x_dim) # [BLOCK_N]
|
| 503 |
+
|
| 504 |
+
x_ptrs = (
|
| 505 |
+
x_base[None, :] + ((idx_tokens - VAL) * stride_x_token)[:, None]
|
| 506 |
+
) # [BLOCK_M, BLOCK_N]
|
| 507 |
+
|
| 508 |
+
mask_x = (
|
| 509 |
+
(idx_tokens - VAL >= 0)[:, None]
|
| 510 |
+
& (idx_tokens - VAL < seqlen)[:, None]
|
| 511 |
+
& (idx_feats < dim)[None, :]
|
| 512 |
+
) # token-index # token-index # feature-index
|
| 513 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 514 |
+
tl.debug_barrier()
|
| 515 |
+
|
| 516 |
+
new_conv_state = tl.where(mask, conv_state, loaded_x)
|
| 517 |
+
|
| 518 |
+
conv_state_base = (
|
| 519 |
+
conv_state_ptr
|
| 520 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 521 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 522 |
+
) # [BLOCK_N,]
|
| 523 |
+
conv_state_ptrs_target = (
|
| 524 |
+
conv_state_base + (idx_tokens * stride_conv_state_tok)[:, None]
|
| 525 |
+
) # [BLOCK_M, BLOCK_N]
|
| 526 |
+
mask = (idx_tokens < state_len)[:, None] & (idx_feats < dim)[None, :]
|
| 527 |
+
tl.store(conv_state_ptrs_target, new_conv_state, mask)
|
| 528 |
+
|
| 529 |
+
# STEP 3: init accumulator
|
| 530 |
+
if HAS_BIAS:
|
| 531 |
+
bias = bias_ptr + idx_feats
|
| 532 |
+
mask_bias = idx_feats < dim
|
| 533 |
+
acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(
|
| 534 |
+
tl.float32
|
| 535 |
+
) # [BLOCK_N]
|
| 536 |
+
else:
|
| 537 |
+
acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 538 |
+
|
| 539 |
+
# STEP 4:
|
| 540 |
+
# PRE-LOAD WEIGHTS
|
| 541 |
+
# first kernel column, configured for weights to handle BLOCK_N features in range
|
| 542 |
+
w_base = w_ptr + (idx_feats * stride_w_dim) # [BLOCK_N,]
|
| 543 |
+
mask_w = idx_feats < dim
|
| 544 |
+
if KERNEL_WIDTH >= 2:
|
| 545 |
+
w_ptrs = w_base + (0 * stride_w_width) # [BLOCK_N] tensor
|
| 546 |
+
w_col0 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 547 |
+
w_ptrs = w_base + (1 * stride_w_width) # [BLOCK_N] tensor
|
| 548 |
+
w_col1 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 549 |
+
if KERNEL_WIDTH >= 3:
|
| 550 |
+
w_ptrs = w_base + (2 * stride_w_width) # [BLOCK_N] tensor
|
| 551 |
+
w_col2 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 552 |
+
if KERNEL_WIDTH >= 4:
|
| 553 |
+
w_ptrs = w_base + (3 * stride_w_width) # [BLOCK_N] tensor
|
| 554 |
+
w_col3 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 555 |
+
|
| 556 |
+
x_base_1d = x_base # starting of chunk [BLOCK_N]
|
| 557 |
+
mask_x_1d = idx_feats < dim
|
| 558 |
+
|
| 559 |
+
# STEP 5: compute each token
|
| 560 |
+
for idx_token in tl.static_range(seqlen):
|
| 561 |
+
acc = acc_preload
|
| 562 |
+
|
| 563 |
+
matrix_w = w_col0
|
| 564 |
+
matrix_x = col0
|
| 565 |
+
for j in tl.static_range(KERNEL_WIDTH):
|
| 566 |
+
if KERNEL_WIDTH == 2:
|
| 567 |
+
if j == 1: # KERNEL_WIDTH-1:
|
| 568 |
+
matrix_w = w_col1
|
| 569 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token # [BLOCK_N]
|
| 570 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 571 |
+
elif KERNEL_WIDTH == 3:
|
| 572 |
+
if j == 1:
|
| 573 |
+
matrix_w = w_col1
|
| 574 |
+
matrix_x = col1
|
| 575 |
+
elif j == 2:
|
| 576 |
+
matrix_w = w_col2
|
| 577 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token # [BLOCK_N]
|
| 578 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 579 |
+
elif KERNEL_WIDTH == 4:
|
| 580 |
+
if j == 1:
|
| 581 |
+
matrix_w = w_col1
|
| 582 |
+
matrix_x = col1
|
| 583 |
+
elif j == 2:
|
| 584 |
+
matrix_w = w_col2
|
| 585 |
+
matrix_x = col2
|
| 586 |
+
elif j == 3:
|
| 587 |
+
matrix_w = w_col3
|
| 588 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token # [BLOCK_N]
|
| 589 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 590 |
+
|
| 591 |
+
acc += matrix_x * matrix_w # [BLOCK_N]
|
| 592 |
+
|
| 593 |
+
if KERNEL_WIDTH == 2:
|
| 594 |
+
col0 = matrix_x
|
| 595 |
+
elif KERNEL_WIDTH == 3:
|
| 596 |
+
col0 = col1
|
| 597 |
+
col1 = matrix_x
|
| 598 |
+
elif KERNEL_WIDTH == 4:
|
| 599 |
+
col0 = col1
|
| 600 |
+
col1 = col2
|
| 601 |
+
col2 = matrix_x
|
| 602 |
+
|
| 603 |
+
if SILU_ACTIVATION:
|
| 604 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 605 |
+
mask_1d = (idx_token < seqlen) & (
|
| 606 |
+
idx_feats < dim
|
| 607 |
+
) # token-index # feature-index
|
| 608 |
+
o_ptrs = (
|
| 609 |
+
o_ptr
|
| 610 |
+
+ (idx_seq) * stride_o_seq
|
| 611 |
+
+ idx_token * stride_o_token
|
| 612 |
+
+ (idx_feats * stride_o_dim)
|
| 613 |
+
)
|
| 614 |
+
|
| 615 |
+
tl.store(o_ptrs, acc, mask=mask_1d)
|
| 616 |
+
|
| 617 |
+
if SAVE_INTERMEDIATE:
|
| 618 |
+
# Save the window state after consuming this token
|
| 619 |
+
# Layout: [seq(cache line), step, dim, win(K-1)]
|
| 620 |
+
base_ptr = (
|
| 621 |
+
intermediate_conv_window_ptr
|
| 622 |
+
+ conv_state_batch_coord * stride_inter_seq
|
| 623 |
+
+ idx_token * stride_inter_step
|
| 624 |
+
+ idx_feats * stride_inter_dim
|
| 625 |
+
)
|
| 626 |
+
if KERNEL_WIDTH >= 2:
|
| 627 |
+
tl.store(base_ptr + 0 * stride_inter_win, col0, mask=mask_w)
|
| 628 |
+
if KERNEL_WIDTH >= 3:
|
| 629 |
+
tl.store(base_ptr + 1 * stride_inter_win, col1, mask=mask_w)
|
| 630 |
+
if KERNEL_WIDTH >= 4:
|
| 631 |
+
tl.store(base_ptr + 2 * stride_inter_win, col2, mask=mask_w)
|
build/torch-rocm/_triton_kernels/causal_conv1d_update_single_token.py
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
| 3 |
+
# Copyright (c) 2024, Tri Dao.
|
| 4 |
+
# Adapted from https://github.com/Dao-AILab/causal-conv1d/blob/main/causal_conv1d/causal_conv1d_interface.py
|
| 5 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 6 |
+
#
|
| 7 |
+
# Kernels for causal_conv1d **update** single-token paths: ``conv_state`` is updated in place.
|
| 8 |
+
|
| 9 |
+
import triton
|
| 10 |
+
import triton.language as tl
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@triton.jit
|
| 14 |
+
def _ba_source_offsets(
|
| 15 |
+
idx_hv, num_v_heads, num_k_heads, INTERLEAVED_QKVZ: tl.constexpr
|
| 16 |
+
):
|
| 17 |
+
"""Return (b_off, a_off) into the packed ba tensor for one v-head index."""
|
| 18 |
+
if INTERLEAVED_QKVZ:
|
| 19 |
+
G = num_v_heads // num_k_heads
|
| 20 |
+
idx_h = idx_hv // G
|
| 21 |
+
idx_v = idx_hv % G
|
| 22 |
+
b_off = idx_h * (2 * G) + idx_v
|
| 23 |
+
a_off = idx_h * (2 * G) + G + idx_v
|
| 24 |
+
else:
|
| 25 |
+
b_off = idx_hv
|
| 26 |
+
a_off = num_v_heads + idx_hv
|
| 27 |
+
return b_off, a_off
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@triton.jit
|
| 31 |
+
def _z_source_idx(
|
| 32 |
+
idx_z,
|
| 33 |
+
num_k_heads,
|
| 34 |
+
num_v_heads,
|
| 35 |
+
head_k_dim,
|
| 36 |
+
head_v_dim,
|
| 37 |
+
head_qkvz_dim,
|
| 38 |
+
INTERLEAVED_QKVZ: tl.constexpr,
|
| 39 |
+
):
|
| 40 |
+
"""Map flat z index to source column in the packed x tensor."""
|
| 41 |
+
if INTERLEAVED_QKVZ:
|
| 42 |
+
G = num_v_heads // num_k_heads
|
| 43 |
+
gs = G * head_v_dim
|
| 44 |
+
return idx_z // gs * head_qkvz_dim + 2 * head_k_dim + gs + idx_z % gs
|
| 45 |
+
else:
|
| 46 |
+
return 2 * num_k_heads * head_k_dim + num_v_heads * head_v_dim + idx_z
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@triton.jit
|
| 50 |
+
def _feat_source_idx(
|
| 51 |
+
idx_feats,
|
| 52 |
+
num_k_heads,
|
| 53 |
+
head_k_dim,
|
| 54 |
+
head_v_dim,
|
| 55 |
+
head_qkvz_dim,
|
| 56 |
+
num_v_heads,
|
| 57 |
+
INTERLEAVED_QKVZ: tl.constexpr,
|
| 58 |
+
):
|
| 59 |
+
"""Map logical conv-output feature index to source column in packed x."""
|
| 60 |
+
if INTERLEAVED_QKVZ:
|
| 61 |
+
nk = num_k_heads
|
| 62 |
+
hk = head_k_dim
|
| 63 |
+
gs = (num_v_heads // nk) * head_v_dim
|
| 64 |
+
in_q = (idx_feats < nk * hk).to(tl.int64)
|
| 65 |
+
in_k = ((idx_feats >= nk * hk) & (idx_feats < nk * hk * 2)).to(tl.int64)
|
| 66 |
+
in_v = (idx_feats >= nk * hk * 2).to(tl.int64)
|
| 67 |
+
q_idx = idx_feats // hk * head_qkvz_dim + idx_feats % hk
|
| 68 |
+
rel_k = idx_feats - nk * hk
|
| 69 |
+
k_idx = rel_k // hk * head_qkvz_dim + hk + rel_k % hk
|
| 70 |
+
rel_v = idx_feats - nk * hk * 2
|
| 71 |
+
v_idx = rel_v // gs * head_qkvz_dim + 2 * hk + rel_v % gs
|
| 72 |
+
return in_q * q_idx + in_k * k_idx + in_v * v_idx
|
| 73 |
+
else:
|
| 74 |
+
return idx_feats.to(tl.int64)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
@triton.jit()
|
| 78 |
+
def _causal_conv1d_update_single_token_kernel(
|
| 79 |
+
# Pointers to matrices
|
| 80 |
+
x_ptr, # (batch, dim, seqlen)
|
| 81 |
+
w_ptr, # (dim, width)
|
| 82 |
+
bias_ptr,
|
| 83 |
+
conv_state_ptr,
|
| 84 |
+
conv_state_indices_ptr,
|
| 85 |
+
block_idx_last_scheduled_token, # (batch,)
|
| 86 |
+
initial_state_idx, # (batch,)
|
| 87 |
+
o_ptr, # (batch, dim, seqlen)
|
| 88 |
+
# Matrix dimensions
|
| 89 |
+
batch: int,
|
| 90 |
+
dim: tl.constexpr,
|
| 91 |
+
seqlen: tl.constexpr,
|
| 92 |
+
state_len: tl.constexpr,
|
| 93 |
+
num_cache_lines: tl.constexpr, # added to support vLLM larger cache lines
|
| 94 |
+
# Strides
|
| 95 |
+
stride_x_seq: tl.constexpr,
|
| 96 |
+
stride_x_dim: tl.constexpr,
|
| 97 |
+
stride_x_token: tl.constexpr,
|
| 98 |
+
stride_w_dim: tl.constexpr,
|
| 99 |
+
stride_w_width: tl.constexpr,
|
| 100 |
+
stride_conv_state_seq: tl.constexpr,
|
| 101 |
+
stride_conv_state_dim: tl.constexpr,
|
| 102 |
+
stride_conv_state_tok: tl.constexpr,
|
| 103 |
+
stride_state_indices: tl.constexpr,
|
| 104 |
+
stride_o_seq: tl.constexpr,
|
| 105 |
+
stride_o_dim: tl.constexpr,
|
| 106 |
+
stride_o_token: tl.constexpr,
|
| 107 |
+
# others
|
| 108 |
+
pad_slot_id: tl.constexpr,
|
| 109 |
+
# Meta-parameters
|
| 110 |
+
HAS_BIAS: tl.constexpr,
|
| 111 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 112 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 113 |
+
IS_APC_ENABLED: tl.constexpr,
|
| 114 |
+
NP2_STATELEN: tl.constexpr,
|
| 115 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 116 |
+
BLOCK_N: tl.constexpr,
|
| 117 |
+
):
|
| 118 |
+
# ruff: noqa: E501
|
| 119 |
+
idx_seq = tl.program_id(0)
|
| 120 |
+
if idx_seq >= batch:
|
| 121 |
+
return
|
| 122 |
+
|
| 123 |
+
# [BLOCK_N,] elements along the feature-dimension (channel)
|
| 124 |
+
idx_feats = tl.program_id(1) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 125 |
+
|
| 126 |
+
if IS_APC_ENABLED:
|
| 127 |
+
# Get the state from the initial_state_idx
|
| 128 |
+
conv_state_init = tl.load(initial_state_idx + idx_seq)
|
| 129 |
+
current_last_index = tl.load(block_idx_last_scheduled_token + idx_seq)
|
| 130 |
+
else:
|
| 131 |
+
conv_state_init = 0
|
| 132 |
+
current_last_index = 0
|
| 133 |
+
|
| 134 |
+
# cache_idx
|
| 135 |
+
conv_states_input_coord = tl.load(
|
| 136 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices + conv_state_init
|
| 137 |
+
).to(tl.int64)
|
| 138 |
+
|
| 139 |
+
if USE_PAD_SLOT: # noqa
|
| 140 |
+
if conv_states_input_coord == pad_slot_id:
|
| 141 |
+
# not processing as this is not the actual sequence
|
| 142 |
+
return
|
| 143 |
+
|
| 144 |
+
# IS_VARLEN is False
|
| 145 |
+
query_start_index = idx_seq * seqlen
|
| 146 |
+
query_end_index = query_start_index + seqlen
|
| 147 |
+
x_offset = idx_seq * stride_x_seq
|
| 148 |
+
o_offset = idx_seq * stride_o_seq
|
| 149 |
+
|
| 150 |
+
if query_start_index == query_end_index:
|
| 151 |
+
return
|
| 152 |
+
|
| 153 |
+
# IS_SPEC_DECODING is False
|
| 154 |
+
conv_state_token_offset = 0
|
| 155 |
+
|
| 156 |
+
# STEP 1: READ init_state data
|
| 157 |
+
# note: NP2_STATELEN = triton.next_power_of_2(KERNEL_WIDTH - 1)
|
| 158 |
+
idx_cols = tl.arange(0, NP2_STATELEN)
|
| 159 |
+
conv_state_ptrs_cols = (
|
| 160 |
+
conv_state_ptr
|
| 161 |
+
+ (conv_states_input_coord * stride_conv_state_seq)
|
| 162 |
+
+ conv_state_token_offset * stride_conv_state_tok
|
| 163 |
+
+ (idx_feats * stride_conv_state_dim)[:, None]
|
| 164 |
+
+ (idx_cols * stride_conv_state_tok)[None, :]
|
| 165 |
+
) # [BLOCK_N, NP2_STATELEN]
|
| 166 |
+
mask_cols = (
|
| 167 |
+
(conv_states_input_coord < num_cache_lines)
|
| 168 |
+
& (idx_feats < dim)[:, None]
|
| 169 |
+
& (idx_cols < KERNEL_WIDTH - 1)[None, :]
|
| 170 |
+
)
|
| 171 |
+
cols = tl.load(conv_state_ptrs_cols, mask_cols, other=0.0)
|
| 172 |
+
|
| 173 |
+
# STEP 2: assume state_len > seqlen
|
| 174 |
+
idx_tokens = tl.arange(0, NP2_STATELEN) # [BLOCK_M]
|
| 175 |
+
|
| 176 |
+
# With speculative decoding, the conv_state updates works in a sliding
|
| 177 |
+
# window manner, at each forward pass, the tokens are shift by 1, so we
|
| 178 |
+
# load since idx_tokens + 1.
|
| 179 |
+
conv_state_ptrs_source = (
|
| 180 |
+
conv_state_ptr
|
| 181 |
+
+ (conv_states_input_coord * stride_conv_state_seq)
|
| 182 |
+
+ conv_state_token_offset * stride_conv_state_tok
|
| 183 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 184 |
+
+ ((idx_tokens + seqlen) * stride_conv_state_tok)[:, None]
|
| 185 |
+
) # [BLOCK_M, BLOCK_N]
|
| 186 |
+
mask = (
|
| 187 |
+
(conv_states_input_coord < num_cache_lines)
|
| 188 |
+
& ((idx_tokens + seqlen) < state_len)[:, None]
|
| 189 |
+
& (idx_feats < dim)[None, :]
|
| 190 |
+
)
|
| 191 |
+
conv_state = tl.load(conv_state_ptrs_source, mask, other=0.0)
|
| 192 |
+
|
| 193 |
+
VAL = state_len - seqlen
|
| 194 |
+
x_base = x_ptr + x_offset + (idx_feats * stride_x_dim) # [BLOCK_N]
|
| 195 |
+
|
| 196 |
+
x_ptrs = (
|
| 197 |
+
x_base[None, :] + ((idx_tokens - VAL) * stride_x_token)[:, None]
|
| 198 |
+
) # [BLOCK_M, BLOCK_N]
|
| 199 |
+
|
| 200 |
+
mask_x = (
|
| 201 |
+
(idx_tokens - VAL >= 0)[:, None]
|
| 202 |
+
& (idx_tokens - VAL < seqlen)[:, None]
|
| 203 |
+
& (idx_feats < dim)[None, :]
|
| 204 |
+
) # token-index # token-index # feature-index
|
| 205 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 206 |
+
tl.debug_barrier()
|
| 207 |
+
|
| 208 |
+
new_conv_state = tl.where(mask, conv_state, loaded_x)
|
| 209 |
+
|
| 210 |
+
# Get the state from the initial_state_idx
|
| 211 |
+
# cache_idx
|
| 212 |
+
conv_states_offset = tl.load(
|
| 213 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices + current_last_index
|
| 214 |
+
).to(tl.int64)
|
| 215 |
+
conv_state_ptrs_target = (
|
| 216 |
+
conv_state_ptr
|
| 217 |
+
+ (conv_states_offset * stride_conv_state_seq) # Offset from seq
|
| 218 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 219 |
+
)[
|
| 220 |
+
None, :
|
| 221 |
+
] + ( # [BLOCK_N,]
|
| 222 |
+
idx_tokens * stride_conv_state_tok
|
| 223 |
+
)[
|
| 224 |
+
:, None
|
| 225 |
+
]
|
| 226 |
+
mask = (idx_tokens < state_len)[:, None] & (idx_feats < dim)[None, :]
|
| 227 |
+
tl.store(conv_state_ptrs_target, new_conv_state, mask)
|
| 228 |
+
|
| 229 |
+
# STEP 3: init accumulator, not necessary
|
| 230 |
+
# if HAS_BIAS:
|
| 231 |
+
# bias = bias_ptr + idx_feats
|
| 232 |
+
# mask_bias = idx_feats < dim
|
| 233 |
+
# acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(
|
| 234 |
+
# tl.float32
|
| 235 |
+
# ) # [BLOCK_N]
|
| 236 |
+
# else:
|
| 237 |
+
# acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 238 |
+
|
| 239 |
+
# STEP 4:
|
| 240 |
+
# LOAD WEIGHTS and compute
|
| 241 |
+
w_cols_ptrs = (
|
| 242 |
+
w_ptr
|
| 243 |
+
+ (idx_feats * stride_w_dim)[:, None]
|
| 244 |
+
+ (idx_cols * stride_w_width)[None, :]
|
| 245 |
+
)
|
| 246 |
+
mask_w_cols = (idx_feats < dim)[:, None] & (idx_cols < KERNEL_WIDTH - 1)[None, :]
|
| 247 |
+
w_cols = tl.load(w_cols_ptrs, mask_w_cols, other=0.0) # [BLOCK_N, NP2_STATELEN]
|
| 248 |
+
|
| 249 |
+
w_last_ptrs = (
|
| 250 |
+
w_ptr + (idx_feats * stride_w_dim) + (KERNEL_WIDTH - 1) * stride_w_width
|
| 251 |
+
)
|
| 252 |
+
w_last = tl.load(w_last_ptrs, idx_feats < dim, other=0.0) # [BLOCK_N]
|
| 253 |
+
|
| 254 |
+
# For the convolution output: dot(weights, [state_cols | x])
|
| 255 |
+
# cols is [BLOCK_N, NP2_STATELEN] = conv_state history
|
| 256 |
+
# We need x as 1D [BLOCK_N] for the last weight column
|
| 257 |
+
x_1d = tl.load(x_base, mask=(idx_feats < dim), other=0.0) # [BLOCK_N], reload as 1D
|
| 258 |
+
acc = tl.sum((w_cols * cols).to(tl.float32), axis=1) + (w_last * x_1d).to(
|
| 259 |
+
tl.float32
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
if HAS_BIAS:
|
| 263 |
+
bias = bias_ptr + idx_feats
|
| 264 |
+
acc += tl.load(bias, idx_feats < dim, other=0.0).to(tl.float32) # [BLOCK_N]
|
| 265 |
+
|
| 266 |
+
if SILU_ACTIVATION:
|
| 267 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 268 |
+
mask_1d = idx_feats < dim
|
| 269 |
+
o_ptrs = o_ptr + o_offset + (idx_feats * stride_o_dim)
|
| 270 |
+
|
| 271 |
+
tl.store(o_ptrs, acc, mask=mask_1d)
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
@triton.jit()
|
| 275 |
+
def _reshape_causal_conv1d_update_single_token_kernel(
|
| 276 |
+
# Pointers to matrices
|
| 277 |
+
x_ptr, # (num_tokens, dim+z_dim, seqlen) where seqlen=1
|
| 278 |
+
ba_ptr,
|
| 279 |
+
z_ptr, # (num_tokens, num_v_heads, head_v_dim)
|
| 280 |
+
core_attn_out_ptr, # (num_tokens, num_v_heads, head_v_dim)
|
| 281 |
+
b_ptr, # (num_accepted_tokens, num_v_heads)
|
| 282 |
+
a_ptr, # (num_accepted_tokens, num_v_heads)
|
| 283 |
+
w_ptr, # (dim, width)
|
| 284 |
+
bias_ptr,
|
| 285 |
+
conv_state_ptr,
|
| 286 |
+
conv_state_indices_ptr,
|
| 287 |
+
block_idx_last_scheduled_token, # (batch,)
|
| 288 |
+
initial_state_idx, # (batch,)
|
| 289 |
+
o_ptr, # (num_accepted_tokens, dim, seqlen)
|
| 290 |
+
# Matrix dimensions
|
| 291 |
+
batch: int,
|
| 292 |
+
num_tokens: int,
|
| 293 |
+
num_k_heads: tl.constexpr,
|
| 294 |
+
num_v_heads: tl.constexpr,
|
| 295 |
+
head_k_dim: tl.constexpr,
|
| 296 |
+
head_v_dim: tl.constexpr,
|
| 297 |
+
dim: tl.constexpr,
|
| 298 |
+
head_qkvz_dim: tl.constexpr,
|
| 299 |
+
seqlen: tl.constexpr,
|
| 300 |
+
state_len: tl.constexpr,
|
| 301 |
+
num_cache_lines: tl.constexpr, # added to support vLLM larger cache lines
|
| 302 |
+
# Strides
|
| 303 |
+
stride_x_seq: tl.constexpr,
|
| 304 |
+
stride_x_dim: tl.constexpr,
|
| 305 |
+
stride_x_token: tl.constexpr,
|
| 306 |
+
stride_w_dim: tl.constexpr,
|
| 307 |
+
stride_w_width: tl.constexpr,
|
| 308 |
+
stride_conv_state_seq: tl.constexpr,
|
| 309 |
+
stride_conv_state_dim: tl.constexpr,
|
| 310 |
+
stride_conv_state_tok: tl.constexpr,
|
| 311 |
+
stride_state_indices: tl.constexpr,
|
| 312 |
+
stride_o_seq: tl.constexpr,
|
| 313 |
+
stride_o_dim: tl.constexpr,
|
| 314 |
+
stride_o_token: tl.constexpr,
|
| 315 |
+
stride_z_seq: tl.constexpr,
|
| 316 |
+
stride_ba_seq: tl.constexpr,
|
| 317 |
+
stride_ba_token: tl.constexpr,
|
| 318 |
+
stride_b_seq: tl.constexpr,
|
| 319 |
+
# others
|
| 320 |
+
pad_slot_id: tl.constexpr,
|
| 321 |
+
num_program_write_z: tl.constexpr,
|
| 322 |
+
BLOCK_Z: tl.constexpr,
|
| 323 |
+
HV: tl.constexpr,
|
| 324 |
+
# Meta-parameters
|
| 325 |
+
HAS_BIAS: tl.constexpr,
|
| 326 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 327 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 328 |
+
IS_APC_ENABLED: tl.constexpr,
|
| 329 |
+
NP2_STATELEN: tl.constexpr,
|
| 330 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 331 |
+
BLOCK_N: tl.constexpr,
|
| 332 |
+
INTERLEAVED_QKVZ: tl.constexpr,
|
| 333 |
+
):
|
| 334 |
+
# ruff: noqa: E501
|
| 335 |
+
idx_seq = tl.program_id(0)
|
| 336 |
+
if idx_seq >= batch:
|
| 337 |
+
return
|
| 338 |
+
|
| 339 |
+
## write b, a
|
| 340 |
+
if tl.program_id(1) == 0:
|
| 341 |
+
## HV = triton.next_power_of_2(num_v_heads)
|
| 342 |
+
idx_hv = tl.arange(0, HV)
|
| 343 |
+
b_source_offset, a_source_offset = _ba_source_offsets(
|
| 344 |
+
idx_hv, num_v_heads, num_k_heads, INTERLEAVED_QKVZ
|
| 345 |
+
)
|
| 346 |
+
|
| 347 |
+
b_source_ptrs = (
|
| 348 |
+
ba_ptr + idx_seq * stride_ba_seq + b_source_offset * stride_ba_token
|
| 349 |
+
)
|
| 350 |
+
a_source_ptrs = (
|
| 351 |
+
ba_ptr + idx_seq * stride_ba_seq + a_source_offset * stride_ba_token
|
| 352 |
+
)
|
| 353 |
+
mask_ba = idx_hv < num_v_heads
|
| 354 |
+
b = tl.load(b_source_ptrs, mask=mask_ba, other=0.0)
|
| 355 |
+
a = tl.load(a_source_ptrs, mask=mask_ba, other=0.0)
|
| 356 |
+
## b, a should be contiguous so the last stride is 1
|
| 357 |
+
b_ptrs = b_ptr + idx_seq * stride_b_seq + idx_hv
|
| 358 |
+
a_ptrs = a_ptr + idx_seq * stride_b_seq + idx_hv
|
| 359 |
+
tl.store(b_ptrs, b, mask_ba)
|
| 360 |
+
tl.store(a_ptrs, a, mask_ba)
|
| 361 |
+
## write z
|
| 362 |
+
elif tl.program_id(1) < 1 + num_program_write_z:
|
| 363 |
+
idx_z = (tl.program_id(1) - 1) * BLOCK_Z + tl.arange(0, BLOCK_Z)
|
| 364 |
+
idx_z_x = _z_source_idx(
|
| 365 |
+
idx_z,
|
| 366 |
+
num_k_heads,
|
| 367 |
+
num_v_heads,
|
| 368 |
+
head_k_dim,
|
| 369 |
+
head_v_dim,
|
| 370 |
+
head_qkvz_dim,
|
| 371 |
+
INTERLEAVED_QKVZ,
|
| 372 |
+
)
|
| 373 |
+
z_source_ptrs = x_ptr + idx_seq * stride_x_seq + idx_z_x * stride_x_dim
|
| 374 |
+
mask_z = idx_z < num_v_heads * head_v_dim
|
| 375 |
+
z = tl.load(z_source_ptrs, mask=mask_z, other=0.0)
|
| 376 |
+
z_ptrs = z_ptr + idx_seq * stride_z_seq + idx_z
|
| 377 |
+
tl.store(z_ptrs, z, mask=mask_z)
|
| 378 |
+
|
| 379 |
+
## zero-fill core_attn_out
|
| 380 |
+
# first, zero_fill [0, batch) for core_attn_out
|
| 381 |
+
core_attn_out_ptrs = core_attn_out_ptr + idx_seq * stride_z_seq + idx_z
|
| 382 |
+
tl.store(core_attn_out_ptrs, 0.0, mask=mask_z)
|
| 383 |
+
# second, zero_fill [batch, num_tokens) for both z and core_attn_out
|
| 384 |
+
n_repeat = (num_tokens - 1) // batch
|
| 385 |
+
for idx_repeat in tl.range(n_repeat):
|
| 386 |
+
idx_seq_remain = batch * (1 + idx_repeat) + idx_seq
|
| 387 |
+
z_ptrs = z_ptr + idx_seq_remain * stride_z_seq + idx_z
|
| 388 |
+
core_attn_out_ptrs = (
|
| 389 |
+
core_attn_out_ptr + idx_seq_remain * stride_z_seq + idx_z
|
| 390 |
+
)
|
| 391 |
+
mask_remain = (idx_seq_remain < num_tokens) & mask_z
|
| 392 |
+
tl.store(z_ptrs, 0.0, mask=mask_remain)
|
| 393 |
+
tl.store(core_attn_out_ptrs, 0.0, mask=mask_remain)
|
| 394 |
+
## do regular causal conv1d update
|
| 395 |
+
else:
|
| 396 |
+
# [BLOCK_N,] elements along the feature-dimension (channel)
|
| 397 |
+
idx_feats = (tl.program_id(1) - 1 - num_program_write_z) * BLOCK_N + tl.arange(
|
| 398 |
+
0, BLOCK_N
|
| 399 |
+
)
|
| 400 |
+
idx_feats_x = _feat_source_idx(
|
| 401 |
+
idx_feats,
|
| 402 |
+
num_k_heads,
|
| 403 |
+
head_k_dim,
|
| 404 |
+
head_v_dim,
|
| 405 |
+
head_qkvz_dim,
|
| 406 |
+
num_v_heads,
|
| 407 |
+
INTERLEAVED_QKVZ,
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
if IS_APC_ENABLED:
|
| 411 |
+
# Get the state from the initial_state_idx
|
| 412 |
+
conv_state_init = tl.load(initial_state_idx + idx_seq)
|
| 413 |
+
current_last_index = tl.load(block_idx_last_scheduled_token + idx_seq)
|
| 414 |
+
else:
|
| 415 |
+
conv_state_init = 0
|
| 416 |
+
current_last_index = 0
|
| 417 |
+
|
| 418 |
+
# cache_idx
|
| 419 |
+
conv_states_input_coord = tl.load(
|
| 420 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices + conv_state_init
|
| 421 |
+
).to(tl.int64)
|
| 422 |
+
|
| 423 |
+
if USE_PAD_SLOT: # noqa
|
| 424 |
+
if conv_states_input_coord == pad_slot_id:
|
| 425 |
+
# not processing as this is not the actual sequence
|
| 426 |
+
return
|
| 427 |
+
|
| 428 |
+
# IS_VARLEN is False
|
| 429 |
+
query_start_index = idx_seq * seqlen
|
| 430 |
+
query_end_index = query_start_index + seqlen
|
| 431 |
+
x_offset = idx_seq * stride_x_seq
|
| 432 |
+
o_offset = idx_seq * stride_o_seq
|
| 433 |
+
|
| 434 |
+
if query_start_index == query_end_index:
|
| 435 |
+
return
|
| 436 |
+
|
| 437 |
+
# STEP 1: READ init_state data
|
| 438 |
+
# note: NP2_STATELEN = triton.next_power_of_2(KERNEL_WIDTH - 1)
|
| 439 |
+
idx_cols = tl.arange(0, NP2_STATELEN)
|
| 440 |
+
conv_state_ptrs_cols = (
|
| 441 |
+
conv_state_ptr
|
| 442 |
+
+ (conv_states_input_coord * stride_conv_state_seq)
|
| 443 |
+
+ (idx_feats * stride_conv_state_dim)[:, None]
|
| 444 |
+
+ (idx_cols * stride_conv_state_tok)[None, :]
|
| 445 |
+
) # [BLOCK_N, NP2_STATELEN]
|
| 446 |
+
mask_cols = (
|
| 447 |
+
(conv_states_input_coord < num_cache_lines)
|
| 448 |
+
& (idx_feats < dim)[:, None]
|
| 449 |
+
& (idx_cols < KERNEL_WIDTH - 1)[None, :]
|
| 450 |
+
)
|
| 451 |
+
cols = tl.load(conv_state_ptrs_cols, mask_cols, other=0.0)
|
| 452 |
+
|
| 453 |
+
# STEP 2: assume state_len > seqlen
|
| 454 |
+
idx_tokens = tl.arange(0, NP2_STATELEN) # [BLOCK_M]
|
| 455 |
+
|
| 456 |
+
# With speculative decoding, the conv_state updates works in a sliding
|
| 457 |
+
# window manner, at each forward pass, the tokens are shift by 1, so we
|
| 458 |
+
# load since idx_tokens + 1.
|
| 459 |
+
conv_state_ptrs_source = (
|
| 460 |
+
conv_state_ptr
|
| 461 |
+
+ (conv_states_input_coord * stride_conv_state_seq)
|
| 462 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 463 |
+
+ ((idx_tokens + seqlen) * stride_conv_state_tok)[:, None]
|
| 464 |
+
) # [BLOCK_M, BLOCK_N]
|
| 465 |
+
mask = (
|
| 466 |
+
(conv_states_input_coord < num_cache_lines)
|
| 467 |
+
& ((idx_tokens + seqlen) < state_len)[:, None]
|
| 468 |
+
& (idx_feats < dim)[None, :]
|
| 469 |
+
)
|
| 470 |
+
conv_state = tl.load(conv_state_ptrs_source, mask, other=0.0)
|
| 471 |
+
|
| 472 |
+
VAL = state_len - seqlen
|
| 473 |
+
x_base = x_ptr + x_offset + (idx_feats_x * stride_x_dim) # [BLOCK_N]
|
| 474 |
+
|
| 475 |
+
x_ptrs = (
|
| 476 |
+
x_base[None, :] + ((idx_tokens - VAL) * stride_x_token)[:, None]
|
| 477 |
+
) # [BLOCK_M, BLOCK_N]
|
| 478 |
+
|
| 479 |
+
mask_x = (
|
| 480 |
+
(idx_tokens - VAL >= 0)[:, None]
|
| 481 |
+
& (idx_tokens - VAL < seqlen)[:, None]
|
| 482 |
+
& (idx_feats < dim)[None, :]
|
| 483 |
+
) # token-index # token-index # feature-index
|
| 484 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 485 |
+
tl.debug_barrier()
|
| 486 |
+
|
| 487 |
+
new_conv_state = tl.where(mask, conv_state, loaded_x)
|
| 488 |
+
|
| 489 |
+
# Get the state from the initial_state_idx
|
| 490 |
+
# cache_idx
|
| 491 |
+
conv_states_offset = tl.load(
|
| 492 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices + current_last_index
|
| 493 |
+
).to(tl.int64)
|
| 494 |
+
conv_state_ptrs_target = (
|
| 495 |
+
conv_state_ptr
|
| 496 |
+
+ (conv_states_offset * stride_conv_state_seq) # Offset from seq
|
| 497 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 498 |
+
)[
|
| 499 |
+
None, :
|
| 500 |
+
] + ( # [BLOCK_N,]
|
| 501 |
+
idx_tokens * stride_conv_state_tok
|
| 502 |
+
)[
|
| 503 |
+
:, None
|
| 504 |
+
]
|
| 505 |
+
mask = (idx_tokens < state_len)[:, None] & (idx_feats < dim)[None, :]
|
| 506 |
+
tl.store(conv_state_ptrs_target, new_conv_state, mask)
|
| 507 |
+
|
| 508 |
+
# STEP 3: init accumulator, not necessary
|
| 509 |
+
# if HAS_BIAS:
|
| 510 |
+
# bias = bias_ptr + idx_feats
|
| 511 |
+
# mask_bias = idx_feats < dim
|
| 512 |
+
# acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(
|
| 513 |
+
# tl.float32
|
| 514 |
+
# ) # [BLOCK_N]
|
| 515 |
+
# else:
|
| 516 |
+
# acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 517 |
+
|
| 518 |
+
# STEP 4:
|
| 519 |
+
# LOAD WEIGHTS and compute
|
| 520 |
+
w_cols_ptrs = (
|
| 521 |
+
w_ptr
|
| 522 |
+
+ (idx_feats * stride_w_dim)[:, None]
|
| 523 |
+
+ (idx_cols * stride_w_width)[None, :]
|
| 524 |
+
)
|
| 525 |
+
mask_w_cols = (idx_feats < dim)[:, None] & (idx_cols < KERNEL_WIDTH - 1)[
|
| 526 |
+
None, :
|
| 527 |
+
]
|
| 528 |
+
w_cols = tl.load(w_cols_ptrs, mask_w_cols, other=0.0) # [BLOCK_N, NP2_STATELEN]
|
| 529 |
+
|
| 530 |
+
w_last_ptrs = (
|
| 531 |
+
w_ptr + (idx_feats * stride_w_dim) + (KERNEL_WIDTH - 1) * stride_w_width
|
| 532 |
+
)
|
| 533 |
+
w_last = tl.load(w_last_ptrs, idx_feats < dim, other=0.0) # [BLOCK_N]
|
| 534 |
+
|
| 535 |
+
# For the convolution output: dot(weights, [state_cols | x])
|
| 536 |
+
# cols is [BLOCK_N, NP2_STATELEN] = conv_state history
|
| 537 |
+
# We need x as 1D [BLOCK_N] for the last weight column
|
| 538 |
+
x_1d = tl.load(
|
| 539 |
+
x_base, mask=(idx_feats < dim), other=0.0
|
| 540 |
+
) # [BLOCK_N], reload as 1D
|
| 541 |
+
acc = tl.sum((w_cols * cols).to(tl.float32), axis=1) + (w_last * x_1d).to(
|
| 542 |
+
tl.float32
|
| 543 |
+
)
|
| 544 |
+
|
| 545 |
+
if HAS_BIAS:
|
| 546 |
+
bias = bias_ptr + idx_feats
|
| 547 |
+
acc += tl.load(bias, idx_feats < dim, other=0.0).to(tl.float32) # [BLOCK_N]
|
| 548 |
+
|
| 549 |
+
if SILU_ACTIVATION:
|
| 550 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 551 |
+
mask_1d = idx_feats < dim
|
| 552 |
+
o_ptrs = o_ptr + o_offset + (idx_feats * stride_o_dim)
|
| 553 |
+
|
| 554 |
+
tl.store(o_ptrs, acc, mask=mask_1d)
|
build/torch-rocm/_triton_kernels/common/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_triton_kernels/common/splitk_reduce.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
from ...utils._triton.kernel_repr import make_kernel_repr
|
| 5 |
+
|
| 6 |
+
_gemm_splitk_reduce_repr = make_kernel_repr(
|
| 7 |
+
"_gemm_splitk_reduce_kernel",
|
| 8 |
+
[
|
| 9 |
+
"BLOCK_SIZE_M",
|
| 10 |
+
"BLOCK_SIZE_N",
|
| 11 |
+
"ACTUAL_KSPLIT",
|
| 12 |
+
"MAX_KSPLIT",
|
| 13 |
+
"ADD_BIAS",
|
| 14 |
+
"activation",
|
| 15 |
+
"use_activation",
|
| 16 |
+
],
|
| 17 |
+
name_key="KERNEL_NAME",
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@triton.jit(repr=_gemm_splitk_reduce_repr)
|
| 22 |
+
def _gemm_splitk_reduce_kernel(
|
| 23 |
+
c_in_ptr,
|
| 24 |
+
c_out_ptr,
|
| 25 |
+
bias_ptr,
|
| 26 |
+
M,
|
| 27 |
+
N,
|
| 28 |
+
stride_c_in_k,
|
| 29 |
+
stride_c_in_m,
|
| 30 |
+
stride_c_in_n,
|
| 31 |
+
stride_c_out_m,
|
| 32 |
+
stride_c_out_n,
|
| 33 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 34 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 35 |
+
ACTUAL_KSPLIT: tl.constexpr,
|
| 36 |
+
MAX_KSPLIT: tl.constexpr,
|
| 37 |
+
ADD_BIAS: tl.constexpr,
|
| 38 |
+
activation: tl.constexpr,
|
| 39 |
+
use_activation: tl.constexpr,
|
| 40 |
+
KERNEL_NAME: tl.constexpr = "_gemm_splitk_reduce_kernel",
|
| 41 |
+
):
|
| 42 |
+
tl.assume(stride_c_in_k > 0)
|
| 43 |
+
tl.assume(stride_c_in_m > 0)
|
| 44 |
+
tl.assume(stride_c_in_n > 0)
|
| 45 |
+
tl.assume(stride_c_out_m > 0)
|
| 46 |
+
tl.assume(stride_c_out_n > 0)
|
| 47 |
+
|
| 48 |
+
pid_m = tl.program_id(axis=0)
|
| 49 |
+
pid_n = tl.program_id(axis=1)
|
| 50 |
+
|
| 51 |
+
# Tell the AMD backend pid * stride stays non-negative so it can lower
|
| 52 |
+
# the loads/stores to buffer ops instead of generic global ops.
|
| 53 |
+
tl.assume(pid_m >= 0)
|
| 54 |
+
tl.assume(pid_n >= 0)
|
| 55 |
+
|
| 56 |
+
offs_m = (pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)) % M
|
| 57 |
+
offs_n = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)) % N
|
| 58 |
+
offs_k = tl.arange(0, MAX_KSPLIT)
|
| 59 |
+
c_in_ptrs = (
|
| 60 |
+
c_in_ptr
|
| 61 |
+
+ (offs_k[:, None, None] * stride_c_in_k)
|
| 62 |
+
+ (offs_m[None, :, None] * stride_c_in_m)
|
| 63 |
+
+ (offs_n[None, None, :] * stride_c_in_n)
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
if ACTUAL_KSPLIT == MAX_KSPLIT:
|
| 67 |
+
c = tl.load(c_in_ptrs)
|
| 68 |
+
else:
|
| 69 |
+
c = tl.load(c_in_ptrs, mask=offs_k[:, None, None] < ACTUAL_KSPLIT, other=0.0)
|
| 70 |
+
c = tl.sum(c, axis=0)
|
| 71 |
+
|
| 72 |
+
if ADD_BIAS:
|
| 73 |
+
acc_dtype = tl.float32 if c_in_ptr.type.element_ty != tl.int8 else tl.int32
|
| 74 |
+
bias = tl.load(bias_ptr + offs_n).to(dtype=acc_dtype)
|
| 75 |
+
bias = tl.broadcast_to(bias[None, :], (BLOCK_SIZE_M, BLOCK_SIZE_N))
|
| 76 |
+
c += bias
|
| 77 |
+
|
| 78 |
+
if use_activation:
|
| 79 |
+
c = activation(c)
|
| 80 |
+
|
| 81 |
+
c = c.to(c_out_ptr.type.element_ty)
|
| 82 |
+
|
| 83 |
+
c_out_ptrs = (
|
| 84 |
+
c_out_ptr
|
| 85 |
+
+ (offs_m[:, None] * stride_c_out_m)
|
| 86 |
+
+ (offs_n[None, :] * stride_c_out_n)
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
tl.store(c_out_ptrs, c)
|
build/torch-rocm/_triton_kernels/fusions/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
from ..._triton_kernels.fusions.mhc import (
|
| 5 |
+
_mhc_fused_kernel,
|
| 6 |
+
_mhc_fused_split_kernel,
|
| 7 |
+
_mhc_reduce_apply_kernel,
|
| 8 |
+
_mhc_post_kernel,
|
| 9 |
+
_mhc_post_pre_split_kernel,
|
| 10 |
+
_mhc_post_pre_reduce_apply_kernel,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
__all__ = [
|
| 14 |
+
"_mhc_fused_kernel",
|
| 15 |
+
"_mhc_fused_split_kernel",
|
| 16 |
+
"_mhc_reduce_apply_kernel",
|
| 17 |
+
"_mhc_post_kernel",
|
| 18 |
+
"_mhc_post_pre_split_kernel",
|
| 19 |
+
"_mhc_post_pre_reduce_apply_kernel",
|
| 20 |
+
]
|
build/torch-rocm/_triton_kernels/fusions/fused_bmm_rope_kv_cache.py
ADDED
|
@@ -0,0 +1,1043 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from ..._triton_kernels.quant.quant import _mxfp4_quant_op
|
| 8 |
+
from ...rope.rope import _get_gptj_rotated_x_1D, _get_neox_rotated_x_1D
|
| 9 |
+
from ...utils._triton.kernel_repr import make_kernel_repr
|
| 10 |
+
from ...utils._triton.pid_preprocessing import pid_grid
|
| 11 |
+
|
| 12 |
+
_fused_fp4_bmm_rope_cat_and_cache_mla_repr = make_kernel_repr(
|
| 13 |
+
"_fused_fp4_bmm_rope_cat_and_cache_mla_kernel",
|
| 14 |
+
[
|
| 15 |
+
"BLOCK_SIZE_M",
|
| 16 |
+
"BLOCK_SIZE_N",
|
| 17 |
+
"BLOCK_SIZE_K",
|
| 18 |
+
"GROUP_SIZE_M",
|
| 19 |
+
"NUM_KSPLIT",
|
| 20 |
+
"SPLITK_BLOCK_SIZE",
|
| 21 |
+
"QH_PER_KH",
|
| 22 |
+
"REUSE_FREQS_FRONT_PART",
|
| 23 |
+
"IS_NEOX",
|
| 24 |
+
"BLOCK_D_nope",
|
| 25 |
+
"BLOCK_DK_nope",
|
| 26 |
+
"BLOCK_D_pe",
|
| 27 |
+
"BLOCK_D_HALF_pe",
|
| 28 |
+
"PRE_QUANT",
|
| 29 |
+
"TRANSPOSE_BM",
|
| 30 |
+
"OUTPUT_Q_NOPE_ZEROS",
|
| 31 |
+
"HAVE_Y_SCALE",
|
| 32 |
+
"HAVE_K_SCALE",
|
| 33 |
+
"EVEN_K",
|
| 34 |
+
],
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
_fused_fp4_bmm_reduce_repr = make_kernel_repr(
|
| 39 |
+
"_fused_fp4_bmm_reduce_kernel",
|
| 40 |
+
[
|
| 41 |
+
"BLOCK_SIZE_M",
|
| 42 |
+
"BLOCK_SIZE_N",
|
| 43 |
+
"ACTUAL_KSPLIT",
|
| 44 |
+
"MAX_KSPLIT",
|
| 45 |
+
],
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
_fused_fp8_bmm_rope_cat_and_cache_mla_repr = make_kernel_repr(
|
| 50 |
+
"_fused_fp8_bmm_rope_cat_and_cache_mla_kernel",
|
| 51 |
+
[
|
| 52 |
+
"BLOCK_SIZE_M",
|
| 53 |
+
"BLOCK_SIZE_N",
|
| 54 |
+
"BLOCK_SIZE_K",
|
| 55 |
+
"GROUP_SIZE_M",
|
| 56 |
+
"QH_PER_KH",
|
| 57 |
+
"REUSE_FREQS_FRONT_PART",
|
| 58 |
+
"IS_NEOX",
|
| 59 |
+
"BLOCK_D_nope",
|
| 60 |
+
"BLOCK_DK_nope",
|
| 61 |
+
"BLOCK_D_pe",
|
| 62 |
+
"BLOCK_D_HALF_pe",
|
| 63 |
+
"TRANSPOSE_BM",
|
| 64 |
+
"OUTPUT_Q_NOPE_ZEROS",
|
| 65 |
+
"HAVE_K_SCALE",
|
| 66 |
+
"EVEN_K",
|
| 67 |
+
],
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@triton.jit
|
| 72 |
+
def _unit_rope(
|
| 73 |
+
x_ptrs,
|
| 74 |
+
cos,
|
| 75 |
+
sin,
|
| 76 |
+
d_pe_offs,
|
| 77 |
+
IS_NEOX: tl.constexpr,
|
| 78 |
+
BLOCK_D_pe: tl.constexpr,
|
| 79 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 80 |
+
):
|
| 81 |
+
"""Apply RoPE to a vector. Copied from fused_kv_cache.py"""
|
| 82 |
+
x_pe = tl.load(x_ptrs)
|
| 83 |
+
|
| 84 |
+
if IS_NEOX:
|
| 85 |
+
x_rotated_mask = d_pe_offs < BLOCK_D_HALF_pe
|
| 86 |
+
x_pe_rotated = _get_neox_rotated_x_1D(
|
| 87 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 88 |
+
)
|
| 89 |
+
else:
|
| 90 |
+
x_rotated_mask = d_pe_offs % 2 == 0
|
| 91 |
+
x_pe_rotated = _get_gptj_rotated_x_1D(
|
| 92 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
x_pe = x_pe * cos + x_pe_rotated * sin
|
| 96 |
+
return x_pe
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
@triton.heuristics(
|
| 100 |
+
{
|
| 101 |
+
"EVEN_K": lambda args: (args["K"] % (args["BLOCK_SIZE_K"] // 2) == 0)
|
| 102 |
+
and (args["SPLITK_BLOCK_SIZE"] % args["BLOCK_SIZE_K"] == 0)
|
| 103 |
+
and (args["K"] % (args["SPLITK_BLOCK_SIZE"] // 2) == 0),
|
| 104 |
+
"GRID_MN": lambda args: triton.cdiv(args["M"], args["BLOCK_SIZE_M"])
|
| 105 |
+
* triton.cdiv(args["N"], args["BLOCK_SIZE_N"]),
|
| 106 |
+
}
|
| 107 |
+
)
|
| 108 |
+
@triton.jit(repr=_fused_fp4_bmm_rope_cat_and_cache_mla_repr)
|
| 109 |
+
def _fused_fp4_bmm_rope_cat_and_cache_mla_kernel(
|
| 110 |
+
a_ptr,
|
| 111 |
+
b_ptr,
|
| 112 |
+
b_scales_ptr,
|
| 113 |
+
c_ptr,
|
| 114 |
+
c_scale_ptr,
|
| 115 |
+
q_pe_ptr,
|
| 116 |
+
k_nope_ptr,
|
| 117 |
+
k_pe_ptr,
|
| 118 |
+
pos_ptr,
|
| 119 |
+
cos_ptr,
|
| 120 |
+
sin_ptr,
|
| 121 |
+
q_out_ptr,
|
| 122 |
+
decode_q_pe_out_ptr,
|
| 123 |
+
k_pe_out_ptr,
|
| 124 |
+
q_nope_zeros_out_ptr,
|
| 125 |
+
kv_cache_ptr,
|
| 126 |
+
slot_mapping_ptr,
|
| 127 |
+
M,
|
| 128 |
+
N,
|
| 129 |
+
K,
|
| 130 |
+
B,
|
| 131 |
+
B_slot,
|
| 132 |
+
num_decode_toks_for_zeros,
|
| 133 |
+
QH,
|
| 134 |
+
KH,
|
| 135 |
+
bmm_programs,
|
| 136 |
+
grid_mn,
|
| 137 |
+
num_pid_m,
|
| 138 |
+
num_pid_n,
|
| 139 |
+
stride_ab,
|
| 140 |
+
stride_am,
|
| 141 |
+
stride_ak,
|
| 142 |
+
stride_bb,
|
| 143 |
+
stride_bn,
|
| 144 |
+
stride_bk,
|
| 145 |
+
stride_bsb,
|
| 146 |
+
stride_bsn,
|
| 147 |
+
stride_bsk,
|
| 148 |
+
stride_cb,
|
| 149 |
+
stride_ck,
|
| 150 |
+
stride_cm,
|
| 151 |
+
stride_cn,
|
| 152 |
+
q_pe_stride_b,
|
| 153 |
+
q_pe_stride_h,
|
| 154 |
+
q_pe_stride_d,
|
| 155 |
+
k_nope_stride_b,
|
| 156 |
+
k_nope_stride_h,
|
| 157 |
+
k_nope_stride_d,
|
| 158 |
+
k_pe_stride_b,
|
| 159 |
+
k_pe_stride_h,
|
| 160 |
+
k_pe_stride_d,
|
| 161 |
+
pos_stride_b,
|
| 162 |
+
cos_stride_b,
|
| 163 |
+
cos_stride_d,
|
| 164 |
+
q_out_stride_b,
|
| 165 |
+
q_out_stride_h,
|
| 166 |
+
q_out_stride_d,
|
| 167 |
+
decode_q_pe_out_stride_b,
|
| 168 |
+
decode_q_pe_out_stride_h,
|
| 169 |
+
decode_q_pe_out_stride_d,
|
| 170 |
+
k_pe_out_stride_b,
|
| 171 |
+
k_pe_out_stride_h,
|
| 172 |
+
k_pe_out_stride_d,
|
| 173 |
+
q_nope_zeros_out_stride_b,
|
| 174 |
+
q_nope_zeros_out_stride_h,
|
| 175 |
+
q_nope_zeros_out_stride_d,
|
| 176 |
+
kv_cache_stride_b,
|
| 177 |
+
kv_cache_stride_h,
|
| 178 |
+
kv_cache_stride_d,
|
| 179 |
+
k_scale_ptr,
|
| 180 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 181 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 182 |
+
BLOCK_SIZE_K: tl.constexpr,
|
| 183 |
+
GROUP_SIZE_M: tl.constexpr,
|
| 184 |
+
NUM_KSPLIT: tl.constexpr,
|
| 185 |
+
SPLITK_BLOCK_SIZE: tl.constexpr,
|
| 186 |
+
QH_PER_KH: tl.constexpr,
|
| 187 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 188 |
+
IS_NEOX: tl.constexpr,
|
| 189 |
+
BLOCK_D_nope: tl.constexpr,
|
| 190 |
+
BLOCK_DK_nope: tl.constexpr,
|
| 191 |
+
BLOCK_D_pe: tl.constexpr,
|
| 192 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 193 |
+
PRE_QUANT: tl.constexpr,
|
| 194 |
+
OUTPUT_Q_NOPE_ZEROS: tl.constexpr,
|
| 195 |
+
HAVE_Y_SCALE: tl.constexpr,
|
| 196 |
+
HAVE_K_SCALE: tl.constexpr,
|
| 197 |
+
EVEN_K: tl.constexpr,
|
| 198 |
+
GRID_MN: tl.constexpr,
|
| 199 |
+
cache_modifier: tl.constexpr = ".ca",
|
| 200 |
+
):
|
| 201 |
+
"""
|
| 202 |
+
Fused kernel for FP4 BMM + RoPE + KV cache write.
|
| 203 |
+
|
| 204 |
+
These are INDEPENDENT operations fused to reduce kernel launch overhead:
|
| 205 |
+
- BMM writes to c_ptr (either q_out[:, :, :kv_lora_rank] or y_pp for split-K)
|
| 206 |
+
- RoPE writes to q_out[:, :, kv_lora_rank:] and handles KV cache
|
| 207 |
+
|
| 208 |
+
Grid structure:
|
| 209 |
+
- Phase 1: pid < bmm_programs → BMM (tiled over heads, K-splits, M, N)
|
| 210 |
+
- Phase 2: pid in [bmm_programs, bmm_programs + B*QH) → RoPE + KV cache for decode
|
| 211 |
+
- Phase 3: pid >= bmm_programs + B*QH → KV cache only for prefill tokens
|
| 212 |
+
"""
|
| 213 |
+
|
| 214 |
+
pid = tl.program_id(0)
|
| 215 |
+
tl.assume(pid >= 0)
|
| 216 |
+
|
| 217 |
+
rope_start = bmm_programs
|
| 218 |
+
prefill_start = bmm_programs + B * QH
|
| 219 |
+
|
| 220 |
+
if pid < bmm_programs:
|
| 221 |
+
tl.assume(stride_ab > 0)
|
| 222 |
+
tl.assume(stride_am > 0)
|
| 223 |
+
tl.assume(stride_ak > 0)
|
| 224 |
+
tl.assume(stride_bb > 0)
|
| 225 |
+
tl.assume(stride_bk > 0)
|
| 226 |
+
tl.assume(stride_bn > 0)
|
| 227 |
+
tl.assume(stride_cb > 0)
|
| 228 |
+
tl.assume(stride_cm > 0)
|
| 229 |
+
tl.assume(stride_cn > 0)
|
| 230 |
+
tl.assume(stride_bsb > 0)
|
| 231 |
+
tl.assume(stride_bsk > 0)
|
| 232 |
+
tl.assume(stride_bsn > 0)
|
| 233 |
+
|
| 234 |
+
stride_ab_i64 = tl.cast(stride_ab, tl.int64)
|
| 235 |
+
stride_bb_i64 = tl.cast(stride_bb, tl.int64)
|
| 236 |
+
tl.cast(stride_cb, tl.int64)
|
| 237 |
+
stride_bsb_i64 = tl.cast(stride_bsb, tl.int64)
|
| 238 |
+
|
| 239 |
+
SCALE_GROUP_SIZE: tl.constexpr = 32
|
| 240 |
+
|
| 241 |
+
if HAVE_Y_SCALE:
|
| 242 |
+
c_scale = tl.load(c_scale_ptr)
|
| 243 |
+
else:
|
| 244 |
+
c_scale = 1
|
| 245 |
+
c_scale_rcprl = (1 / c_scale).to(tl.float32)
|
| 246 |
+
|
| 247 |
+
pid_head = pid // (NUM_KSPLIT * GRID_MN)
|
| 248 |
+
pid_unified = pid % (NUM_KSPLIT * GRID_MN)
|
| 249 |
+
pid_k = pid_unified % NUM_KSPLIT
|
| 250 |
+
pid_tile = pid_unified // NUM_KSPLIT
|
| 251 |
+
|
| 252 |
+
pid_head_i64 = tl.cast(pid_head, tl.int64)
|
| 253 |
+
|
| 254 |
+
if NUM_KSPLIT == 1:
|
| 255 |
+
# remap_xcd(pid_tile, GRID_MN)
|
| 256 |
+
pid_m, pid_n = pid_grid(
|
| 257 |
+
pid_tile, num_pid_m, num_pid_n, GROUP_SIZE_M=GROUP_SIZE_M
|
| 258 |
+
)
|
| 259 |
+
else:
|
| 260 |
+
pid_m = pid_tile // num_pid_n
|
| 261 |
+
pid_n = pid_tile % num_pid_n
|
| 262 |
+
|
| 263 |
+
tl.assume(pid_head >= 0)
|
| 264 |
+
tl.assume(pid_m >= 0)
|
| 265 |
+
tl.assume(pid_n >= 0)
|
| 266 |
+
|
| 267 |
+
if (pid_k * SPLITK_BLOCK_SIZE // 2) < K:
|
| 268 |
+
num_k_iter = tl.cdiv(SPLITK_BLOCK_SIZE // 2, BLOCK_SIZE_K // 2)
|
| 269 |
+
|
| 270 |
+
offs_k_bf16 = tl.arange(0, BLOCK_SIZE_K)
|
| 271 |
+
offs_k_split_bf16 = pid_k * SPLITK_BLOCK_SIZE + offs_k_bf16
|
| 272 |
+
offs_am = (pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)) % M
|
| 273 |
+
a_ptrs = a_ptr + (
|
| 274 |
+
pid_head_i64 * stride_ab_i64
|
| 275 |
+
+ offs_am[:, None] * stride_am
|
| 276 |
+
+ offs_k_split_bf16[None, :] * stride_ak
|
| 277 |
+
)
|
| 278 |
+
|
| 279 |
+
offs_k = tl.arange(0, BLOCK_SIZE_K // 2)
|
| 280 |
+
offs_k_split = pid_k * (SPLITK_BLOCK_SIZE // 2) + offs_k
|
| 281 |
+
offs_bn = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)) % N
|
| 282 |
+
b_ptrs = b_ptr + (
|
| 283 |
+
pid_head_i64 * stride_bb_i64
|
| 284 |
+
+ offs_k_split[:, None] * stride_bk
|
| 285 |
+
+ offs_bn[None, :] * stride_bn
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
offs_ks = (pid_k * (SPLITK_BLOCK_SIZE // SCALE_GROUP_SIZE)) + tl.arange(
|
| 289 |
+
0, BLOCK_SIZE_K // SCALE_GROUP_SIZE
|
| 290 |
+
)
|
| 291 |
+
b_scale_ptrs = (
|
| 292 |
+
b_scales_ptr
|
| 293 |
+
+ pid_head_i64 * stride_bsb_i64
|
| 294 |
+
+ offs_bn[:, None] * stride_bsn
|
| 295 |
+
+ offs_ks[None, :] * stride_bsk
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
|
| 299 |
+
|
| 300 |
+
for k_idx in range(pid_k * num_k_iter, (pid_k + 1) * num_k_iter):
|
| 301 |
+
b_scales = tl.load(b_scale_ptrs)
|
| 302 |
+
|
| 303 |
+
if EVEN_K:
|
| 304 |
+
a_bf16 = tl.load(a_ptrs)
|
| 305 |
+
b = tl.load(b_ptrs, cache_modifier=cache_modifier)
|
| 306 |
+
else:
|
| 307 |
+
a_bf16 = tl.load(
|
| 308 |
+
a_ptrs,
|
| 309 |
+
mask=offs_k_bf16[None, :] < K - k_idx * BLOCK_SIZE_K,
|
| 310 |
+
other=0,
|
| 311 |
+
)
|
| 312 |
+
b = tl.load(
|
| 313 |
+
b_ptrs,
|
| 314 |
+
mask=offs_k[:, None] < K - k_idx * (BLOCK_SIZE_K // 2),
|
| 315 |
+
other=0,
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
if PRE_QUANT:
|
| 319 |
+
a, a_scales = _mxfp4_quant_op(
|
| 320 |
+
a_bf16, BLOCK_SIZE_K, BLOCK_SIZE_M, SCALE_GROUP_SIZE
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
accumulator = tl.dot_scaled(
|
| 324 |
+
a, a_scales, "e2m1", b, b_scales, "e2m1", acc=accumulator
|
| 325 |
+
)
|
| 326 |
+
|
| 327 |
+
a_ptrs += BLOCK_SIZE_K * stride_ak
|
| 328 |
+
b_ptrs += (BLOCK_SIZE_K // 2) * stride_bk
|
| 329 |
+
b_scale_ptrs += (BLOCK_SIZE_K // SCALE_GROUP_SIZE) * stride_bsk
|
| 330 |
+
|
| 331 |
+
if HAVE_Y_SCALE:
|
| 332 |
+
accumulator = accumulator * c_scale_rcprl
|
| 333 |
+
|
| 334 |
+
c = accumulator.to(c_ptr.type.element_ty)
|
| 335 |
+
|
| 336 |
+
offs_cm = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M).to(tl.int64)
|
| 337 |
+
offs_cn = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N).to(tl.int64)
|
| 338 |
+
|
| 339 |
+
if NUM_KSPLIT == 1:
|
| 340 |
+
c_ptrs = (
|
| 341 |
+
c_ptr
|
| 342 |
+
+ pid_head_i64 * stride_cb
|
| 343 |
+
+ offs_cm[:, None] * stride_cm
|
| 344 |
+
+ offs_cn[None, :] * stride_cn
|
| 345 |
+
)
|
| 346 |
+
else:
|
| 347 |
+
c_ptrs = (
|
| 348 |
+
c_ptr
|
| 349 |
+
+ pid_head_i64 * stride_cb
|
| 350 |
+
+ pid_k * stride_ck
|
| 351 |
+
+ offs_cm[:, None] * stride_cm
|
| 352 |
+
+ offs_cn[None, :] * stride_cn
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
+
c_mask = (offs_cm[:, None] < M) & (offs_cn[None, :] < N)
|
| 356 |
+
tl.store(c_ptrs, c, mask=c_mask)
|
| 357 |
+
|
| 358 |
+
elif pid < prefill_start:
|
| 359 |
+
pid_adjusted = pid - rope_start
|
| 360 |
+
pid_b = pid_adjusted // QH
|
| 361 |
+
pid_hq = pid_adjusted % QH
|
| 362 |
+
|
| 363 |
+
tl.assume(pid_b >= 0)
|
| 364 |
+
tl.assume(pid_hq >= 0)
|
| 365 |
+
|
| 366 |
+
dk_nope_offs = tl.arange(0, BLOCK_DK_nope).to(tl.int64)
|
| 367 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 368 |
+
|
| 369 |
+
if REUSE_FREQS_FRONT_PART:
|
| 370 |
+
if IS_NEOX:
|
| 371 |
+
d_cos_offs = d_pe_offs
|
| 372 |
+
d_cos_offs = tl.where(
|
| 373 |
+
(d_cos_offs >= BLOCK_D_HALF_pe) & (d_cos_offs < BLOCK_D_pe),
|
| 374 |
+
d_cos_offs - BLOCK_D_HALF_pe,
|
| 375 |
+
d_cos_offs,
|
| 376 |
+
).to(d_cos_offs.dtype)
|
| 377 |
+
else:
|
| 378 |
+
d_cos_offs = d_pe_offs // 2
|
| 379 |
+
else:
|
| 380 |
+
d_cos_offs = d_pe_offs
|
| 381 |
+
|
| 382 |
+
pos = tl.load(pos_ptr + pid_b * pos_stride_b)
|
| 383 |
+
cos_offs = pos * cos_stride_b + d_cos_offs * cos_stride_d
|
| 384 |
+
cos = tl.load(cos_ptr + cos_offs)
|
| 385 |
+
sin = tl.load(sin_ptr + cos_offs)
|
| 386 |
+
|
| 387 |
+
q_pe_ptrs = (
|
| 388 |
+
q_pe_ptr
|
| 389 |
+
+ pid_b * q_pe_stride_b
|
| 390 |
+
+ pid_hq * q_pe_stride_h
|
| 391 |
+
+ d_pe_offs * q_pe_stride_d
|
| 392 |
+
)
|
| 393 |
+
q_pe = _unit_rope(
|
| 394 |
+
q_pe_ptrs,
|
| 395 |
+
cos,
|
| 396 |
+
sin,
|
| 397 |
+
d_pe_offs,
|
| 398 |
+
IS_NEOX,
|
| 399 |
+
BLOCK_D_pe,
|
| 400 |
+
BLOCK_D_HALF_pe,
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
q_out_base = q_out_ptr + pid_b * q_out_stride_b + pid_hq * q_out_stride_h
|
| 404 |
+
tl.store(
|
| 405 |
+
q_out_base + (d_pe_offs + BLOCK_D_nope) * q_out_stride_d,
|
| 406 |
+
q_pe.to(q_out_ptr.dtype.element_ty),
|
| 407 |
+
)
|
| 408 |
+
|
| 409 |
+
if pid_adjusted < num_decode_toks_for_zeros * QH:
|
| 410 |
+
decode_q_pe_out_ptrs = (
|
| 411 |
+
decode_q_pe_out_ptr
|
| 412 |
+
+ pid_b * decode_q_pe_out_stride_b
|
| 413 |
+
+ pid_hq * decode_q_pe_out_stride_h
|
| 414 |
+
+ d_pe_offs * decode_q_pe_out_stride_d
|
| 415 |
+
)
|
| 416 |
+
tl.store(
|
| 417 |
+
decode_q_pe_out_ptrs, q_pe.to(decode_q_pe_out_ptr.dtype.element_ty)
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
if OUTPUT_Q_NOPE_ZEROS:
|
| 421 |
+
if pid_adjusted < num_decode_toks_for_zeros * QH:
|
| 422 |
+
z = tl.zeros(
|
| 423 |
+
(BLOCK_DK_nope,), dtype=q_nope_zeros_out_ptr.dtype.element_ty
|
| 424 |
+
)
|
| 425 |
+
tl.store(
|
| 426 |
+
q_nope_zeros_out_ptr
|
| 427 |
+
+ pid_b * q_nope_zeros_out_stride_b
|
| 428 |
+
+ pid_hq * q_nope_zeros_out_stride_h
|
| 429 |
+
+ dk_nope_offs * q_nope_zeros_out_stride_d,
|
| 430 |
+
z,
|
| 431 |
+
)
|
| 432 |
+
|
| 433 |
+
if pid_hq % QH_PER_KH == 0:
|
| 434 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_b).to(tl.int64)
|
| 435 |
+
if pid_slot >= 0:
|
| 436 |
+
if HAVE_K_SCALE:
|
| 437 |
+
k_scale = tl.load(k_scale_ptr)
|
| 438 |
+
else:
|
| 439 |
+
k_scale = 1
|
| 440 |
+
|
| 441 |
+
pid_hk = pid_hq // QH_PER_KH
|
| 442 |
+
|
| 443 |
+
k_nope_ptrs = (
|
| 444 |
+
k_nope_ptr
|
| 445 |
+
+ pid_b * k_nope_stride_b
|
| 446 |
+
+ pid_hk * k_nope_stride_h
|
| 447 |
+
+ dk_nope_offs * k_nope_stride_d
|
| 448 |
+
)
|
| 449 |
+
k_nope = tl.load(k_nope_ptrs)
|
| 450 |
+
|
| 451 |
+
k_pe_load_ptrs = (
|
| 452 |
+
k_pe_ptr
|
| 453 |
+
+ pid_b * k_pe_stride_b
|
| 454 |
+
+ pid_hk * k_pe_stride_h
|
| 455 |
+
+ d_pe_offs * k_pe_stride_d
|
| 456 |
+
)
|
| 457 |
+
k_pe = _unit_rope(
|
| 458 |
+
k_pe_load_ptrs,
|
| 459 |
+
cos,
|
| 460 |
+
sin,
|
| 461 |
+
d_pe_offs,
|
| 462 |
+
IS_NEOX,
|
| 463 |
+
BLOCK_D_pe,
|
| 464 |
+
BLOCK_D_HALF_pe,
|
| 465 |
+
)
|
| 466 |
+
|
| 467 |
+
k_pe_out_ptrs = (
|
| 468 |
+
k_pe_out_ptr
|
| 469 |
+
+ pid_b * k_pe_out_stride_b
|
| 470 |
+
+ pid_hk * k_pe_out_stride_h
|
| 471 |
+
+ d_pe_offs * k_pe_out_stride_d
|
| 472 |
+
)
|
| 473 |
+
tl.store(k_pe_out_ptrs, k_pe.to(k_pe_out_ptr.dtype.element_ty))
|
| 474 |
+
|
| 475 |
+
k_scale_rcprl = (1 / k_scale).to(tl.float32)
|
| 476 |
+
k_nope_scaled = (k_nope.to(tl.float32) * k_scale_rcprl).to(
|
| 477 |
+
kv_cache_ptr.dtype.element_ty
|
| 478 |
+
)
|
| 479 |
+
k_pe_scaled = (k_pe.to(tl.float32) * k_scale_rcprl).to(
|
| 480 |
+
kv_cache_ptr.dtype.element_ty
|
| 481 |
+
)
|
| 482 |
+
|
| 483 |
+
kv_cache_ptrs = (
|
| 484 |
+
kv_cache_ptr
|
| 485 |
+
+ pid_slot * kv_cache_stride_b
|
| 486 |
+
+ pid_hk * kv_cache_stride_h
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
tl.store(
|
| 490 |
+
kv_cache_ptrs + dk_nope_offs * kv_cache_stride_d, k_nope_scaled
|
| 491 |
+
)
|
| 492 |
+
tl.store(
|
| 493 |
+
kv_cache_ptrs + (d_pe_offs + BLOCK_DK_nope) * kv_cache_stride_d,
|
| 494 |
+
k_pe_scaled,
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
else:
|
| 498 |
+
pid_adjusted = pid - prefill_start
|
| 499 |
+
pid_b = pid_adjusted // KH + B
|
| 500 |
+
pid_hk = pid_adjusted % KH
|
| 501 |
+
|
| 502 |
+
if pid_b < B_slot:
|
| 503 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_b).to(tl.int64)
|
| 504 |
+
|
| 505 |
+
if pid_slot >= 0:
|
| 506 |
+
if HAVE_K_SCALE:
|
| 507 |
+
k_scale = tl.load(k_scale_ptr)
|
| 508 |
+
else:
|
| 509 |
+
k_scale = 1
|
| 510 |
+
|
| 511 |
+
dk_nope_offs = tl.arange(0, BLOCK_DK_nope).to(tl.int64)
|
| 512 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 513 |
+
|
| 514 |
+
k_nope_ptrs = (
|
| 515 |
+
k_nope_ptr
|
| 516 |
+
+ pid_b * k_nope_stride_b
|
| 517 |
+
+ pid_hk * k_nope_stride_h
|
| 518 |
+
+ dk_nope_offs * k_nope_stride_d
|
| 519 |
+
)
|
| 520 |
+
k_nope = tl.load(k_nope_ptrs)
|
| 521 |
+
|
| 522 |
+
k_pe_load_ptrs = (
|
| 523 |
+
k_pe_ptr
|
| 524 |
+
+ pid_b * k_pe_stride_b
|
| 525 |
+
+ pid_hk * k_pe_stride_h
|
| 526 |
+
+ d_pe_offs * k_pe_stride_d
|
| 527 |
+
)
|
| 528 |
+
k_pe = tl.load(k_pe_load_ptrs)
|
| 529 |
+
|
| 530 |
+
k_pe_out_ptrs = (
|
| 531 |
+
k_pe_out_ptr
|
| 532 |
+
+ pid_b * k_pe_out_stride_b
|
| 533 |
+
+ pid_hk * k_pe_out_stride_h
|
| 534 |
+
+ d_pe_offs * k_pe_out_stride_d
|
| 535 |
+
)
|
| 536 |
+
tl.store(k_pe_out_ptrs, k_pe.to(k_pe_out_ptr.dtype.element_ty))
|
| 537 |
+
|
| 538 |
+
k_scale_rcprl = (1 / k_scale).to(tl.float32)
|
| 539 |
+
k_nope_scaled = (k_nope.to(tl.float32) * k_scale_rcprl).to(
|
| 540 |
+
kv_cache_ptr.dtype.element_ty
|
| 541 |
+
)
|
| 542 |
+
k_pe_scaled = (k_pe.to(tl.float32) * k_scale_rcprl).to(
|
| 543 |
+
kv_cache_ptr.dtype.element_ty
|
| 544 |
+
)
|
| 545 |
+
|
| 546 |
+
kv_cache_ptrs = (
|
| 547 |
+
kv_cache_ptr
|
| 548 |
+
+ pid_slot * kv_cache_stride_b
|
| 549 |
+
+ pid_hk * kv_cache_stride_h
|
| 550 |
+
)
|
| 551 |
+
|
| 552 |
+
tl.store(
|
| 553 |
+
kv_cache_ptrs + dk_nope_offs * kv_cache_stride_d, k_nope_scaled
|
| 554 |
+
)
|
| 555 |
+
tl.store(
|
| 556 |
+
kv_cache_ptrs + (d_pe_offs + BLOCK_DK_nope) * kv_cache_stride_d,
|
| 557 |
+
k_pe_scaled,
|
| 558 |
+
)
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
@triton.jit(repr=_fused_fp4_bmm_reduce_repr)
|
| 562 |
+
def _fused_fp4_bmm_reduce_kernel(
|
| 563 |
+
c_in_ptr,
|
| 564 |
+
c_out_ptr,
|
| 565 |
+
M,
|
| 566 |
+
N,
|
| 567 |
+
QH,
|
| 568 |
+
stride_c_in_b,
|
| 569 |
+
stride_c_in_k,
|
| 570 |
+
stride_c_in_m,
|
| 571 |
+
stride_c_in_n,
|
| 572 |
+
stride_c_out_b,
|
| 573 |
+
stride_c_out_h,
|
| 574 |
+
stride_c_out_d,
|
| 575 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 576 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 577 |
+
ACTUAL_KSPLIT: tl.constexpr,
|
| 578 |
+
MAX_KSPLIT: tl.constexpr,
|
| 579 |
+
TRANSPOSE_BM: tl.constexpr,
|
| 580 |
+
):
|
| 581 |
+
"""Reduce kernel for split-K BMM results."""
|
| 582 |
+
pid_head = tl.program_id(axis=0)
|
| 583 |
+
pid_m = tl.program_id(axis=1)
|
| 584 |
+
pid_n = tl.program_id(axis=2)
|
| 585 |
+
|
| 586 |
+
offs_m = (pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)) % M
|
| 587 |
+
offs_n = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)) % N
|
| 588 |
+
offs_k = tl.arange(0, MAX_KSPLIT)
|
| 589 |
+
|
| 590 |
+
c_in_ptrs = (
|
| 591 |
+
c_in_ptr
|
| 592 |
+
+ pid_head * stride_c_in_b
|
| 593 |
+
+ (offs_k[:, None, None] * stride_c_in_k)
|
| 594 |
+
+ (offs_m[None, :, None] * stride_c_in_m)
|
| 595 |
+
+ (offs_n[None, None, :] * stride_c_in_n)
|
| 596 |
+
)
|
| 597 |
+
|
| 598 |
+
if ACTUAL_KSPLIT == MAX_KSPLIT:
|
| 599 |
+
c = tl.load(c_in_ptrs)
|
| 600 |
+
else:
|
| 601 |
+
c = tl.load(c_in_ptrs, mask=offs_k[:, None, None] < ACTUAL_KSPLIT)
|
| 602 |
+
|
| 603 |
+
c = tl.sum(c, axis=0)
|
| 604 |
+
c = c.to(c_out_ptr.type.element_ty)
|
| 605 |
+
|
| 606 |
+
if TRANSPOSE_BM:
|
| 607 |
+
c_out_ptrs = (
|
| 608 |
+
c_out_ptr
|
| 609 |
+
+ (offs_m[:, None] * stride_c_out_b)
|
| 610 |
+
+ (pid_head * stride_c_out_h)
|
| 611 |
+
+ (offs_n[None, :] * stride_c_out_d)
|
| 612 |
+
)
|
| 613 |
+
else:
|
| 614 |
+
c_out_ptrs = (
|
| 615 |
+
c_out_ptr
|
| 616 |
+
+ (pid_head * stride_c_out_b)
|
| 617 |
+
+ (offs_m[:, None] * stride_c_out_h)
|
| 618 |
+
+ (offs_n[None, :] * stride_c_out_d)
|
| 619 |
+
)
|
| 620 |
+
|
| 621 |
+
c_mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
|
| 622 |
+
tl.store(c_out_ptrs, c, mask=c_mask)
|
| 623 |
+
|
| 624 |
+
|
| 625 |
+
@triton.heuristics(
|
| 626 |
+
{
|
| 627 |
+
"EVEN_K": lambda args: args["K"] % args["BLOCK_SIZE_K"] == 0,
|
| 628 |
+
"GRID_MN": lambda args: triton.cdiv(args["M"], args["BLOCK_SIZE_M"])
|
| 629 |
+
* triton.cdiv(args["N"], args["BLOCK_SIZE_N"]),
|
| 630 |
+
}
|
| 631 |
+
)
|
| 632 |
+
@triton.jit(repr=_fused_fp8_bmm_rope_cat_and_cache_mla_repr)
|
| 633 |
+
def _fused_fp8_bmm_rope_cat_and_cache_mla_kernel(
|
| 634 |
+
a_ptr,
|
| 635 |
+
b_ptr,
|
| 636 |
+
b_scale_ptr,
|
| 637 |
+
c_ptr,
|
| 638 |
+
q_pe_ptr,
|
| 639 |
+
k_nope_ptr,
|
| 640 |
+
k_pe_ptr,
|
| 641 |
+
pos_ptr,
|
| 642 |
+
cos_ptr,
|
| 643 |
+
sin_ptr,
|
| 644 |
+
decode_q_pe_out_ptr,
|
| 645 |
+
k_pe_out_ptr,
|
| 646 |
+
q_nope_zeros_out_ptr,
|
| 647 |
+
kv_cache_ptr,
|
| 648 |
+
slot_mapping_ptr,
|
| 649 |
+
M,
|
| 650 |
+
N,
|
| 651 |
+
K,
|
| 652 |
+
B,
|
| 653 |
+
B_slot,
|
| 654 |
+
num_decode_toks_for_zeros,
|
| 655 |
+
QH,
|
| 656 |
+
KH,
|
| 657 |
+
bmm_programs,
|
| 658 |
+
grid_mn,
|
| 659 |
+
num_pid_m,
|
| 660 |
+
num_pid_n,
|
| 661 |
+
stride_ab,
|
| 662 |
+
stride_am,
|
| 663 |
+
stride_ak,
|
| 664 |
+
stride_bb,
|
| 665 |
+
stride_bk,
|
| 666 |
+
stride_bn,
|
| 667 |
+
stride_cb,
|
| 668 |
+
stride_cm,
|
| 669 |
+
stride_cn,
|
| 670 |
+
q_pe_stride_b,
|
| 671 |
+
q_pe_stride_h,
|
| 672 |
+
q_pe_stride_d,
|
| 673 |
+
k_nope_stride_b,
|
| 674 |
+
k_nope_stride_h,
|
| 675 |
+
k_nope_stride_d,
|
| 676 |
+
k_pe_stride_b,
|
| 677 |
+
k_pe_stride_h,
|
| 678 |
+
k_pe_stride_d,
|
| 679 |
+
pos_stride_b,
|
| 680 |
+
cos_stride_b,
|
| 681 |
+
cos_stride_d,
|
| 682 |
+
q_out_stride_b,
|
| 683 |
+
q_out_stride_h,
|
| 684 |
+
q_out_stride_d,
|
| 685 |
+
decode_q_pe_out_stride_b,
|
| 686 |
+
decode_q_pe_out_stride_h,
|
| 687 |
+
decode_q_pe_out_stride_d,
|
| 688 |
+
k_pe_out_stride_b,
|
| 689 |
+
k_pe_out_stride_h,
|
| 690 |
+
k_pe_out_stride_d,
|
| 691 |
+
q_nope_zeros_out_stride_b,
|
| 692 |
+
q_nope_zeros_out_stride_h,
|
| 693 |
+
q_nope_zeros_out_stride_d,
|
| 694 |
+
kv_cache_stride_b,
|
| 695 |
+
kv_cache_stride_h,
|
| 696 |
+
kv_cache_stride_d,
|
| 697 |
+
k_scale_ptr,
|
| 698 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 699 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 700 |
+
BLOCK_SIZE_K: tl.constexpr,
|
| 701 |
+
GROUP_SIZE_M: tl.constexpr,
|
| 702 |
+
QH_PER_KH: tl.constexpr,
|
| 703 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 704 |
+
IS_NEOX: tl.constexpr,
|
| 705 |
+
BLOCK_D_nope: tl.constexpr,
|
| 706 |
+
BLOCK_DK_nope: tl.constexpr,
|
| 707 |
+
BLOCK_D_pe: tl.constexpr,
|
| 708 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 709 |
+
OUTPUT_Q_NOPE_ZEROS: tl.constexpr,
|
| 710 |
+
HAVE_K_SCALE: tl.constexpr,
|
| 711 |
+
DTYPE_MAX: tl.constexpr,
|
| 712 |
+
EVEN_K: tl.constexpr,
|
| 713 |
+
GRID_MN: tl.constexpr,
|
| 714 |
+
cache_modifier: tl.constexpr = ".ca",
|
| 715 |
+
):
|
| 716 |
+
"""
|
| 717 |
+
Fused kernel for FP8 BMM + RoPE + KV cache write.
|
| 718 |
+
|
| 719 |
+
These are INDEPENDENT operations fused to reduce kernel launch overhead:
|
| 720 |
+
- BMM writes to q_out[:, :, :kv_lora_rank]
|
| 721 |
+
- RoPE writes to q_out[:, :, kv_lora_rank:] and handles KV cache
|
| 722 |
+
|
| 723 |
+
Note: FP8 does not support split-K.
|
| 724 |
+
|
| 725 |
+
Grid structure:
|
| 726 |
+
- Phase 1: pid < bmm_programs → BMM (tiled over heads, M, N)
|
| 727 |
+
- Phase 2: pid in [bmm_programs, bmm_programs + B*QH) → RoPE + KV cache for decode
|
| 728 |
+
- Phase 3: pid >= bmm_programs + B*QH → KV cache only for prefill tokens
|
| 729 |
+
"""
|
| 730 |
+
|
| 731 |
+
pid = tl.program_id(0)
|
| 732 |
+
tl.assume(pid >= 0)
|
| 733 |
+
|
| 734 |
+
rope_start = bmm_programs
|
| 735 |
+
prefill_start = bmm_programs + B * QH
|
| 736 |
+
|
| 737 |
+
if pid < bmm_programs:
|
| 738 |
+
stride_ab_i64 = tl.cast(stride_ab, tl.int64)
|
| 739 |
+
stride_am_i64 = tl.cast(stride_am, tl.int64)
|
| 740 |
+
stride_ak_i64 = tl.cast(stride_ak, tl.int64)
|
| 741 |
+
stride_bb_i64 = tl.cast(stride_bb, tl.int64)
|
| 742 |
+
stride_bk_i64 = tl.cast(stride_bk, tl.int64)
|
| 743 |
+
stride_bn_i64 = tl.cast(stride_bn, tl.int64)
|
| 744 |
+
stride_cb_i64 = tl.cast(stride_cb, tl.int64)
|
| 745 |
+
stride_cm_i64 = tl.cast(stride_cm, tl.int64)
|
| 746 |
+
stride_cn_i64 = tl.cast(stride_cn, tl.int64)
|
| 747 |
+
|
| 748 |
+
tl.assume(stride_ab_i64 > 0)
|
| 749 |
+
tl.assume(stride_am_i64 > 0)
|
| 750 |
+
tl.assume(stride_ak_i64 > 0)
|
| 751 |
+
tl.assume(stride_bb_i64 > 0)
|
| 752 |
+
tl.assume(stride_bk_i64 > 0)
|
| 753 |
+
tl.assume(stride_bn_i64 > 0)
|
| 754 |
+
tl.assume(stride_cb_i64 > 0)
|
| 755 |
+
tl.assume(stride_cm_i64 > 0)
|
| 756 |
+
tl.assume(stride_cn_i64 > 0)
|
| 757 |
+
|
| 758 |
+
pid_head = pid // GRID_MN
|
| 759 |
+
pid_tile = pid % GRID_MN
|
| 760 |
+
|
| 761 |
+
pid_head_i64 = tl.cast(pid_head, tl.int64)
|
| 762 |
+
|
| 763 |
+
if GROUP_SIZE_M == 1:
|
| 764 |
+
pid_m = pid_tile // num_pid_n
|
| 765 |
+
pid_n = pid_tile % num_pid_n
|
| 766 |
+
else:
|
| 767 |
+
num_pid_in_group = GROUP_SIZE_M * num_pid_n
|
| 768 |
+
group_id = pid_tile // num_pid_in_group
|
| 769 |
+
first_pid_m = group_id * GROUP_SIZE_M
|
| 770 |
+
group_size_m = tl.minimum(num_pid_m - first_pid_m, GROUP_SIZE_M)
|
| 771 |
+
pid_m = first_pid_m + (pid_tile % group_size_m)
|
| 772 |
+
pid_n = (pid_tile % num_pid_in_group) // group_size_m
|
| 773 |
+
|
| 774 |
+
pid_m_i64 = tl.cast(pid_m, tl.int64)
|
| 775 |
+
pid_n_i64 = tl.cast(pid_n, tl.int64)
|
| 776 |
+
|
| 777 |
+
tl.assume(pid_m_i64 >= 0)
|
| 778 |
+
tl.assume(pid_n_i64 >= 0)
|
| 779 |
+
tl.assume(pid_head_i64 >= 0)
|
| 780 |
+
|
| 781 |
+
offs_k = tl.arange(0, BLOCK_SIZE_K)
|
| 782 |
+
offs_am = (pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)) % M
|
| 783 |
+
offs_bn = (pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)) % N
|
| 784 |
+
|
| 785 |
+
a_ptrs = a_ptr + (
|
| 786 |
+
pid_head_i64 * stride_ab_i64
|
| 787 |
+
+ offs_am[:, None] * stride_am_i64
|
| 788 |
+
+ offs_k[None, :] * stride_ak_i64
|
| 789 |
+
)
|
| 790 |
+
b_ptrs = b_ptr + (
|
| 791 |
+
pid_head_i64 * stride_bb_i64
|
| 792 |
+
+ offs_k[:, None] * stride_bk_i64
|
| 793 |
+
+ offs_bn[None, :] * stride_bn_i64
|
| 794 |
+
)
|
| 795 |
+
|
| 796 |
+
one_over_DTYPE_MAX = 1.0 / DTYPE_MAX
|
| 797 |
+
b_scale = tl.load(b_scale_ptr)
|
| 798 |
+
|
| 799 |
+
accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
|
| 800 |
+
|
| 801 |
+
for k_idx in range(0, tl.cdiv(K, BLOCK_SIZE_K)):
|
| 802 |
+
if EVEN_K:
|
| 803 |
+
a = tl.load(a_ptrs)
|
| 804 |
+
b = tl.load(b_ptrs, cache_modifier=cache_modifier)
|
| 805 |
+
else:
|
| 806 |
+
a = tl.load(
|
| 807 |
+
a_ptrs, mask=offs_k[None, :] < K - k_idx * BLOCK_SIZE_K, other=0.0
|
| 808 |
+
)
|
| 809 |
+
b = tl.load(
|
| 810 |
+
b_ptrs, mask=offs_k[:, None] < K - k_idx * BLOCK_SIZE_K, other=0.0
|
| 811 |
+
)
|
| 812 |
+
|
| 813 |
+
# Per-token group quantization
|
| 814 |
+
m = tl.maximum(tl.max(tl.abs(a), axis=-1), 1e-10)[:, None]
|
| 815 |
+
a_scale = m.to(tl.float32) * one_over_DTYPE_MAX
|
| 816 |
+
a_scale_recip = 1.0 / a_scale
|
| 817 |
+
a = tl.clamp(a * a_scale_recip, -DTYPE_MAX, DTYPE_MAX).to(
|
| 818 |
+
b_ptr.dtype.element_ty
|
| 819 |
+
)
|
| 820 |
+
|
| 821 |
+
accumulator += tl.dot(a, b) * a_scale
|
| 822 |
+
|
| 823 |
+
a_ptrs += BLOCK_SIZE_K * stride_ak_i64
|
| 824 |
+
b_ptrs += BLOCK_SIZE_K * stride_bk_i64
|
| 825 |
+
|
| 826 |
+
accumulator *= b_scale
|
| 827 |
+
|
| 828 |
+
c = accumulator.to(c_ptr.type.element_ty)
|
| 829 |
+
|
| 830 |
+
offs_cm = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
|
| 831 |
+
offs_cn = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
|
| 832 |
+
|
| 833 |
+
c_ptrs = (
|
| 834 |
+
c_ptr
|
| 835 |
+
+ pid_head_i64 * stride_cb_i64
|
| 836 |
+
+ offs_cm[:, None] * stride_cm_i64
|
| 837 |
+
+ offs_cn[None, :] * stride_cn_i64
|
| 838 |
+
)
|
| 839 |
+
|
| 840 |
+
c_mask = (offs_cm[:, None] < M) & (offs_cn[None, :] < N)
|
| 841 |
+
tl.store(c_ptrs, c, mask=c_mask)
|
| 842 |
+
|
| 843 |
+
elif pid < prefill_start:
|
| 844 |
+
pid_adjusted = pid - rope_start
|
| 845 |
+
pid_b = pid_adjusted // QH
|
| 846 |
+
pid_hq = pid_adjusted % QH
|
| 847 |
+
|
| 848 |
+
tl.assume(pid_b >= 0)
|
| 849 |
+
tl.assume(pid_hq >= 0)
|
| 850 |
+
|
| 851 |
+
dk_nope_offs = tl.arange(0, BLOCK_DK_nope).to(tl.int64)
|
| 852 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 853 |
+
|
| 854 |
+
if REUSE_FREQS_FRONT_PART:
|
| 855 |
+
if IS_NEOX:
|
| 856 |
+
d_cos_offs = d_pe_offs
|
| 857 |
+
d_cos_offs = tl.where(
|
| 858 |
+
(d_cos_offs >= BLOCK_D_HALF_pe) & (d_cos_offs < BLOCK_D_pe),
|
| 859 |
+
d_cos_offs - BLOCK_D_HALF_pe,
|
| 860 |
+
d_cos_offs,
|
| 861 |
+
).to(d_cos_offs.dtype)
|
| 862 |
+
else:
|
| 863 |
+
d_cos_offs = d_pe_offs // 2
|
| 864 |
+
else:
|
| 865 |
+
d_cos_offs = d_pe_offs
|
| 866 |
+
|
| 867 |
+
pos = tl.load(pos_ptr + pid_b * pos_stride_b)
|
| 868 |
+
cos_offs = pos * cos_stride_b + d_cos_offs * cos_stride_d
|
| 869 |
+
cos = tl.load(cos_ptr + cos_offs)
|
| 870 |
+
sin = tl.load(sin_ptr + cos_offs)
|
| 871 |
+
|
| 872 |
+
q_pe_ptrs = (
|
| 873 |
+
q_pe_ptr
|
| 874 |
+
+ pid_b * q_pe_stride_b
|
| 875 |
+
+ pid_hq * q_pe_stride_h
|
| 876 |
+
+ d_pe_offs * q_pe_stride_d
|
| 877 |
+
)
|
| 878 |
+
q_pe = _unit_rope(
|
| 879 |
+
q_pe_ptrs,
|
| 880 |
+
cos,
|
| 881 |
+
sin,
|
| 882 |
+
d_pe_offs,
|
| 883 |
+
IS_NEOX,
|
| 884 |
+
BLOCK_D_pe,
|
| 885 |
+
BLOCK_D_HALF_pe,
|
| 886 |
+
)
|
| 887 |
+
|
| 888 |
+
q_out_base = c_ptr + pid_b * q_out_stride_b + pid_hq * q_out_stride_h
|
| 889 |
+
tl.store(
|
| 890 |
+
q_out_base + (d_pe_offs + BLOCK_D_nope) * q_out_stride_d,
|
| 891 |
+
q_pe.to(c_ptr.dtype.element_ty),
|
| 892 |
+
)
|
| 893 |
+
|
| 894 |
+
if pid_adjusted < num_decode_toks_for_zeros * QH:
|
| 895 |
+
decode_q_pe_out_ptrs = (
|
| 896 |
+
decode_q_pe_out_ptr
|
| 897 |
+
+ pid_b * decode_q_pe_out_stride_b
|
| 898 |
+
+ pid_hq * decode_q_pe_out_stride_h
|
| 899 |
+
+ d_pe_offs * decode_q_pe_out_stride_d
|
| 900 |
+
)
|
| 901 |
+
tl.store(
|
| 902 |
+
decode_q_pe_out_ptrs, q_pe.to(decode_q_pe_out_ptr.dtype.element_ty)
|
| 903 |
+
)
|
| 904 |
+
|
| 905 |
+
if OUTPUT_Q_NOPE_ZEROS:
|
| 906 |
+
if pid_adjusted < num_decode_toks_for_zeros * QH:
|
| 907 |
+
z = tl.zeros(
|
| 908 |
+
(BLOCK_DK_nope,), dtype=q_nope_zeros_out_ptr.dtype.element_ty
|
| 909 |
+
)
|
| 910 |
+
tl.store(
|
| 911 |
+
q_nope_zeros_out_ptr
|
| 912 |
+
+ pid_b * q_nope_zeros_out_stride_b
|
| 913 |
+
+ pid_hq * q_nope_zeros_out_stride_h
|
| 914 |
+
+ dk_nope_offs * q_nope_zeros_out_stride_d,
|
| 915 |
+
z,
|
| 916 |
+
)
|
| 917 |
+
|
| 918 |
+
if pid_hq % QH_PER_KH == 0:
|
| 919 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_b).to(tl.int64)
|
| 920 |
+
if pid_slot >= 0:
|
| 921 |
+
if HAVE_K_SCALE:
|
| 922 |
+
k_scale = tl.load(k_scale_ptr)
|
| 923 |
+
else:
|
| 924 |
+
k_scale = 1
|
| 925 |
+
|
| 926 |
+
pid_hk = pid_hq // QH_PER_KH
|
| 927 |
+
|
| 928 |
+
k_nope_ptrs = (
|
| 929 |
+
k_nope_ptr
|
| 930 |
+
+ pid_b * k_nope_stride_b
|
| 931 |
+
+ pid_hk * k_nope_stride_h
|
| 932 |
+
+ dk_nope_offs * k_nope_stride_d
|
| 933 |
+
)
|
| 934 |
+
k_nope = tl.load(k_nope_ptrs)
|
| 935 |
+
|
| 936 |
+
k_pe_load_ptrs = (
|
| 937 |
+
k_pe_ptr
|
| 938 |
+
+ pid_b * k_pe_stride_b
|
| 939 |
+
+ pid_hk * k_pe_stride_h
|
| 940 |
+
+ d_pe_offs * k_pe_stride_d
|
| 941 |
+
)
|
| 942 |
+
k_pe = _unit_rope(
|
| 943 |
+
k_pe_load_ptrs,
|
| 944 |
+
cos,
|
| 945 |
+
sin,
|
| 946 |
+
d_pe_offs,
|
| 947 |
+
IS_NEOX,
|
| 948 |
+
BLOCK_D_pe,
|
| 949 |
+
BLOCK_D_HALF_pe,
|
| 950 |
+
)
|
| 951 |
+
|
| 952 |
+
k_pe_out_ptrs = (
|
| 953 |
+
k_pe_out_ptr
|
| 954 |
+
+ pid_b * k_pe_out_stride_b
|
| 955 |
+
+ pid_hk * k_pe_out_stride_h
|
| 956 |
+
+ d_pe_offs * k_pe_out_stride_d
|
| 957 |
+
)
|
| 958 |
+
tl.store(k_pe_out_ptrs, k_pe.to(k_pe_out_ptr.dtype.element_ty))
|
| 959 |
+
|
| 960 |
+
k_scale_rcprl = (1 / k_scale).to(tl.float32)
|
| 961 |
+
k_nope_scaled = (k_nope.to(tl.float32) * k_scale_rcprl).to(
|
| 962 |
+
kv_cache_ptr.dtype.element_ty
|
| 963 |
+
)
|
| 964 |
+
k_pe_scaled = (k_pe.to(tl.float32) * k_scale_rcprl).to(
|
| 965 |
+
kv_cache_ptr.dtype.element_ty
|
| 966 |
+
)
|
| 967 |
+
|
| 968 |
+
kv_cache_ptrs = (
|
| 969 |
+
kv_cache_ptr
|
| 970 |
+
+ pid_slot * kv_cache_stride_b
|
| 971 |
+
+ pid_hk * kv_cache_stride_h
|
| 972 |
+
)
|
| 973 |
+
|
| 974 |
+
tl.store(
|
| 975 |
+
kv_cache_ptrs + dk_nope_offs * kv_cache_stride_d, k_nope_scaled
|
| 976 |
+
)
|
| 977 |
+
tl.store(
|
| 978 |
+
kv_cache_ptrs + (d_pe_offs + BLOCK_DK_nope) * kv_cache_stride_d,
|
| 979 |
+
k_pe_scaled,
|
| 980 |
+
)
|
| 981 |
+
|
| 982 |
+
else:
|
| 983 |
+
pid_adjusted = pid - prefill_start
|
| 984 |
+
pid_b = pid_adjusted // KH + B
|
| 985 |
+
pid_hk = pid_adjusted % KH
|
| 986 |
+
|
| 987 |
+
if pid_b < B_slot:
|
| 988 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_b).to(tl.int64)
|
| 989 |
+
|
| 990 |
+
if pid_slot >= 0:
|
| 991 |
+
if HAVE_K_SCALE:
|
| 992 |
+
k_scale = tl.load(k_scale_ptr)
|
| 993 |
+
else:
|
| 994 |
+
k_scale = 1
|
| 995 |
+
|
| 996 |
+
dk_nope_offs = tl.arange(0, BLOCK_DK_nope).to(tl.int64)
|
| 997 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 998 |
+
|
| 999 |
+
k_nope_ptrs = (
|
| 1000 |
+
k_nope_ptr
|
| 1001 |
+
+ pid_b * k_nope_stride_b
|
| 1002 |
+
+ pid_hk * k_nope_stride_h
|
| 1003 |
+
+ dk_nope_offs * k_nope_stride_d
|
| 1004 |
+
)
|
| 1005 |
+
k_nope = tl.load(k_nope_ptrs)
|
| 1006 |
+
|
| 1007 |
+
k_pe_load_ptrs = (
|
| 1008 |
+
k_pe_ptr
|
| 1009 |
+
+ pid_b * k_pe_stride_b
|
| 1010 |
+
+ pid_hk * k_pe_stride_h
|
| 1011 |
+
+ d_pe_offs * k_pe_stride_d
|
| 1012 |
+
)
|
| 1013 |
+
k_pe = tl.load(k_pe_load_ptrs)
|
| 1014 |
+
|
| 1015 |
+
k_pe_out_ptrs = (
|
| 1016 |
+
k_pe_out_ptr
|
| 1017 |
+
+ pid_b * k_pe_out_stride_b
|
| 1018 |
+
+ pid_hk * k_pe_out_stride_h
|
| 1019 |
+
+ d_pe_offs * k_pe_out_stride_d
|
| 1020 |
+
)
|
| 1021 |
+
tl.store(k_pe_out_ptrs, k_pe.to(k_pe_out_ptr.dtype.element_ty))
|
| 1022 |
+
|
| 1023 |
+
k_scale_rcprl = (1 / k_scale).to(tl.float32)
|
| 1024 |
+
k_nope_scaled = (k_nope.to(tl.float32) * k_scale_rcprl).to(
|
| 1025 |
+
kv_cache_ptr.dtype.element_ty
|
| 1026 |
+
)
|
| 1027 |
+
k_pe_scaled = (k_pe.to(tl.float32) * k_scale_rcprl).to(
|
| 1028 |
+
kv_cache_ptr.dtype.element_ty
|
| 1029 |
+
)
|
| 1030 |
+
|
| 1031 |
+
kv_cache_ptrs = (
|
| 1032 |
+
kv_cache_ptr
|
| 1033 |
+
+ pid_slot * kv_cache_stride_b
|
| 1034 |
+
+ pid_hk * kv_cache_stride_h
|
| 1035 |
+
)
|
| 1036 |
+
|
| 1037 |
+
tl.store(
|
| 1038 |
+
kv_cache_ptrs + dk_nope_offs * kv_cache_stride_d, k_nope_scaled
|
| 1039 |
+
)
|
| 1040 |
+
tl.store(
|
| 1041 |
+
kv_cache_ptrs + (d_pe_offs + BLOCK_DK_nope) * kv_cache_stride_d,
|
| 1042 |
+
k_pe_scaled,
|
| 1043 |
+
)
|
build/torch-rocm/_triton_kernels/fusions/fused_clamp_act_mul.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
"""Fused SwiGLU clamp + SiLU * up + optional token weights + optional per-row FP8 group quant (128).
|
| 5 |
+
|
| 6 |
+
Each program handles one row (token). ``inp`` is ``[M, 2 * N]`` with gate in the first
|
| 7 |
+
``N`` columns and up in the second ``N`` (same layout as ``torch.chunk(2, dim=-1)``).
|
| 8 |
+
Gate clamp matches DeepSeek-V4 reference: ``clamp(gate, max=limit)`` only; up uses
|
| 9 |
+
``clamp(up, min=-limit, max=limit)``. When ``HAS_QUANT`` is False the result is written
|
| 10 |
+
directly to ``out`` in its native dtype and no scales are produced.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import triton
|
| 14 |
+
import triton.language as tl
|
| 15 |
+
|
| 16 |
+
from ..._triton_kernels.activation import _apply_activation_from_str
|
| 17 |
+
|
| 18 |
+
from ..._triton_kernels.quant.fused_fp8_quant import _fp8_quant_op
|
| 19 |
+
from ...utils._triton.kernel_repr import make_kernel_repr
|
| 20 |
+
|
| 21 |
+
_fused_clamp_silu_mul_repr = make_kernel_repr(
|
| 22 |
+
"_fused_clamp_silu_mul_kernel",
|
| 23 |
+
[
|
| 24 |
+
"BLOCK_SIZE_N",
|
| 25 |
+
"QUANT_BLOCK_SIZE",
|
| 26 |
+
"SCALE_FMT",
|
| 27 |
+
"HAVE_WEIGHTS",
|
| 28 |
+
"WEIGHT_BROADCAST",
|
| 29 |
+
"HAVE_SWIGLU_CLAMP",
|
| 30 |
+
"HAS_QUANT",
|
| 31 |
+
],
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@triton.jit(repr=_fused_clamp_silu_mul_repr)
|
| 36 |
+
def _fused_clamp_silu_mul_kernel(
|
| 37 |
+
inp_ptr,
|
| 38 |
+
out_ptr,
|
| 39 |
+
scale_ptr,
|
| 40 |
+
weights_ptr,
|
| 41 |
+
M,
|
| 42 |
+
n_half,
|
| 43 |
+
inp_stride_m,
|
| 44 |
+
inp_stride_n,
|
| 45 |
+
out_stride_m,
|
| 46 |
+
out_stride_n,
|
| 47 |
+
scale_stride_m,
|
| 48 |
+
scale_stride_n,
|
| 49 |
+
weights_stride_m,
|
| 50 |
+
weights_stride_n,
|
| 51 |
+
swiglu_limit,
|
| 52 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 53 |
+
QUANT_BLOCK_SIZE: tl.constexpr,
|
| 54 |
+
SCALE_FMT: tl.constexpr,
|
| 55 |
+
DTYPE_MAX: tl.constexpr,
|
| 56 |
+
DTYPE_MIN: tl.constexpr,
|
| 57 |
+
HAVE_WEIGHTS: tl.constexpr,
|
| 58 |
+
WEIGHT_BROADCAST: tl.constexpr,
|
| 59 |
+
HAVE_SWIGLU_CLAMP: tl.constexpr,
|
| 60 |
+
HAS_QUANT: tl.constexpr,
|
| 61 |
+
ACTIVATION: tl.constexpr,
|
| 62 |
+
SHUFFLE: tl.constexpr,
|
| 63 |
+
SCALE_N_PAD: tl.constexpr,
|
| 64 |
+
):
|
| 65 |
+
m_pid = tl.program_id(0)
|
| 66 |
+
n_offs = tl.arange(0, BLOCK_SIZE_N)
|
| 67 |
+
mask = n_offs < n_half
|
| 68 |
+
|
| 69 |
+
gate = tl.load(
|
| 70 |
+
inp_ptr + m_pid * inp_stride_m + n_offs * inp_stride_n,
|
| 71 |
+
mask=mask,
|
| 72 |
+
other=0.0,
|
| 73 |
+
cache_modifier=".cg",
|
| 74 |
+
).to(tl.float32)
|
| 75 |
+
up = tl.load(
|
| 76 |
+
inp_ptr + m_pid * inp_stride_m + (n_half + n_offs) * inp_stride_n,
|
| 77 |
+
mask=mask,
|
| 78 |
+
other=0.0,
|
| 79 |
+
cache_modifier=".cg",
|
| 80 |
+
).to(tl.float32)
|
| 81 |
+
|
| 82 |
+
if HAVE_SWIGLU_CLAMP:
|
| 83 |
+
up = tl.clamp(up, -swiglu_limit, swiglu_limit)
|
| 84 |
+
gate = tl.minimum(gate, swiglu_limit)
|
| 85 |
+
|
| 86 |
+
out = _apply_activation_from_str(gate, ACTIVATION) * up
|
| 87 |
+
|
| 88 |
+
if HAVE_WEIGHTS:
|
| 89 |
+
if WEIGHT_BROADCAST:
|
| 90 |
+
w = tl.load(weights_ptr + m_pid * weights_stride_m).to(tl.float32)
|
| 91 |
+
out = out * w
|
| 92 |
+
else:
|
| 93 |
+
w = tl.load(
|
| 94 |
+
weights_ptr + m_pid * weights_stride_m + n_offs * weights_stride_n,
|
| 95 |
+
mask=mask,
|
| 96 |
+
other=0.0,
|
| 97 |
+
cache_modifier=".cg",
|
| 98 |
+
).to(tl.float32)
|
| 99 |
+
out = out * w
|
| 100 |
+
|
| 101 |
+
if HAS_QUANT:
|
| 102 |
+
if SCALE_FMT == "ue8m0":
|
| 103 |
+
# Per-1×QUANT_BLOCK_SIZE MXFP8 emit: fp8 e4m3 values + uint8 ue8m0
|
| 104 |
+
# biased-exponent scales. Mirrors the ue8m0 path used by moe_gemm_a8w4.
|
| 105 |
+
NUM_QB: tl.constexpr = BLOCK_SIZE_N // QUANT_BLOCK_SIZE
|
| 106 |
+
out_3d = tl.reshape(out, [1, NUM_QB, QUANT_BLOCK_SIZE])
|
| 107 |
+
abs_3d = tl.abs(out_3d)
|
| 108 |
+
max_val = tl.max(abs_3d, axis=2, keep_dims=True)
|
| 109 |
+
dequant_scale = max_val / DTYPE_MAX
|
| 110 |
+
# ROUND_UP via exponent: 2 ** ceil(log2(dequant_scale))
|
| 111 |
+
dequant_scale_exp = (
|
| 112 |
+
dequant_scale.to(tl.uint32, bitcast=True) + 0x007FFFFF
|
| 113 |
+
) & 0x7F800000
|
| 114 |
+
dequant_scale_rounded = dequant_scale_exp.to(tl.float32, bitcast=True)
|
| 115 |
+
quant_scale = tl.where(
|
| 116 |
+
dequant_scale_rounded == 0, 0.0, 1.0 / dequant_scale_rounded
|
| 117 |
+
)
|
| 118 |
+
quant_tensor = out_3d * quant_scale
|
| 119 |
+
quant_2d = tl.reshape(quant_tensor, [1, BLOCK_SIZE_N])
|
| 120 |
+
out_q = tl.ravel(quant_2d)
|
| 121 |
+
scale_exp = (dequant_scale_exp >> 23).to(tl.uint8)
|
| 122 |
+
scale_exp_2d = tl.reshape(scale_exp, [1, NUM_QB])
|
| 123 |
+
block_scales = tl.ravel(scale_exp_2d)
|
| 124 |
+
else:
|
| 125 |
+
out_q, block_scales = _fp8_quant_op(
|
| 126 |
+
out, 1, BLOCK_SIZE_N, QUANT_BLOCK_SIZE, DTYPE_MAX, DTYPE_MIN
|
| 127 |
+
)
|
| 128 |
+
out_q = tl.ravel(out_q)
|
| 129 |
+
block_scales = tl.ravel(block_scales)
|
| 130 |
+
|
| 131 |
+
tl.store(
|
| 132 |
+
out_ptr + m_pid * out_stride_m + n_offs * out_stride_n,
|
| 133 |
+
out_q.to(out_ptr.dtype.element_ty),
|
| 134 |
+
mask=mask,
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
num_bs = tl.cdiv(n_half, QUANT_BLOCK_SIZE)
|
| 138 |
+
NUM_QB_S: tl.constexpr = BLOCK_SIZE_N // QUANT_BLOCK_SIZE
|
| 139 |
+
g_offs = tl.arange(0, NUM_QB_S)
|
| 140 |
+
if SHUFFLE:
|
| 141 |
+
bs_offs_0 = m_pid // 32
|
| 142 |
+
bs_offs_1 = m_pid % 32
|
| 143 |
+
bs_offs_2 = bs_offs_1 % 16
|
| 144 |
+
bs_offs_1 = bs_offs_1 // 16
|
| 145 |
+
bs_offs_3 = g_offs // 8
|
| 146 |
+
bs_offs_4 = g_offs % 8
|
| 147 |
+
bs_offs_5 = bs_offs_4 % 4
|
| 148 |
+
bs_offs_4 = bs_offs_4 // 4
|
| 149 |
+
bs_offs = (
|
| 150 |
+
bs_offs_1
|
| 151 |
+
+ bs_offs_4 * 2
|
| 152 |
+
+ bs_offs_2 * 2 * 2
|
| 153 |
+
+ bs_offs_5 * 2 * 2 * 16
|
| 154 |
+
+ bs_offs_3 * 2 * 2 * 16 * 4
|
| 155 |
+
+ bs_offs_0 * 2 * 16 * SCALE_N_PAD
|
| 156 |
+
)
|
| 157 |
+
tl.store(
|
| 158 |
+
scale_ptr + bs_offs,
|
| 159 |
+
block_scales.to(scale_ptr.dtype.element_ty),
|
| 160 |
+
mask=g_offs < num_bs,
|
| 161 |
+
)
|
| 162 |
+
else:
|
| 163 |
+
tl.store(
|
| 164 |
+
scale_ptr + m_pid * scale_stride_m + g_offs * scale_stride_n,
|
| 165 |
+
block_scales.to(scale_ptr.dtype.element_ty),
|
| 166 |
+
mask=g_offs < num_bs,
|
| 167 |
+
)
|
| 168 |
+
else:
|
| 169 |
+
tl.store(
|
| 170 |
+
out_ptr + m_pid * out_stride_m + n_offs * out_stride_n,
|
| 171 |
+
out.to(out_ptr.dtype.element_ty),
|
| 172 |
+
mask=mask,
|
| 173 |
+
)
|
build/torch-rocm/_triton_kernels/fusions/fused_kv_cache.py
ADDED
|
@@ -0,0 +1,1124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
from ...rope.rope import _get_gptj_rotated_x_1D, _get_neox_rotated_x_1D
|
| 4 |
+
from ..._triton_kernels.kv_cache import _store_mla_kv_cache
|
| 5 |
+
from ..._triton_kernels.quant.quant import _nvfp4_quant_op
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@triton.jit
|
| 9 |
+
def _store_kv_cache_kernel(
|
| 10 |
+
key_cache_ptr,
|
| 11 |
+
value_cache_ptr,
|
| 12 |
+
pid_t_slot,
|
| 13 |
+
pid_hk,
|
| 14 |
+
pid_b,
|
| 15 |
+
d_pe_offs,
|
| 16 |
+
k_pe,
|
| 17 |
+
v,
|
| 18 |
+
key_cache_stride_t,
|
| 19 |
+
key_cache_stride_h,
|
| 20 |
+
key_cache_stride_d,
|
| 21 |
+
key_cache_stride_b,
|
| 22 |
+
key_cache_stride_x,
|
| 23 |
+
value_cache_stride_t,
|
| 24 |
+
value_cache_stride_h,
|
| 25 |
+
value_cache_stride_d,
|
| 26 |
+
value_cache_stride_b,
|
| 27 |
+
value_cache_stride_x,
|
| 28 |
+
value_cache_stride_slot_chunk,
|
| 29 |
+
BLOCK_D_pe: tl.constexpr,
|
| 30 |
+
BLOCK_SIZE: tl.constexpr,
|
| 31 |
+
X_SIZE: tl.constexpr,
|
| 32 |
+
FLASH_LAYOUT: tl.constexpr,
|
| 33 |
+
VALUE_SHUFFLE_LAYOUT: tl.constexpr,
|
| 34 |
+
SCALE_K_WIDTH: tl.constexpr,
|
| 35 |
+
):
|
| 36 |
+
if key_cache_ptr.dtype.element_ty == tl.uint8:
|
| 37 |
+
K_WIDTH: tl.constexpr = 16
|
| 38 |
+
NVFP4_QUANT_BLOCK_SIZE: tl.constexpr = 16
|
| 39 |
+
BLOCK_D_pe_STORE: tl.constexpr = BLOCK_D_pe // 2
|
| 40 |
+
BLOCK_D_pe_scales: tl.constexpr = BLOCK_D_pe // NVFP4_QUANT_BLOCK_SIZE
|
| 41 |
+
|
| 42 |
+
k_pe, k_pe_scales = _nvfp4_quant_op(k_pe, BLOCK_D_pe, 1, NVFP4_QUANT_BLOCK_SIZE)
|
| 43 |
+
v, v_scales = _nvfp4_quant_op(v, BLOCK_D_pe, 1, NVFP4_QUANT_BLOCK_SIZE)
|
| 44 |
+
|
| 45 |
+
d_pe_offs_shfl = tl.arange(0, BLOCK_D_pe_STORE // K_WIDTH).to(tl.int64)
|
| 46 |
+
k_width_shfl = tl.arange(0, K_WIDTH).to(tl.int64)
|
| 47 |
+
k_pe = k_pe.reshape((BLOCK_D_pe_STORE // K_WIDTH, K_WIDTH))
|
| 48 |
+
v = v.reshape((BLOCK_D_pe_STORE // K_WIDTH, K_WIDTH))
|
| 49 |
+
|
| 50 |
+
key_cache_ptrs = (
|
| 51 |
+
key_cache_ptr
|
| 52 |
+
+ pid_t_slot * key_cache_stride_t
|
| 53 |
+
+ pid_hk * key_cache_stride_h
|
| 54 |
+
)
|
| 55 |
+
value_cache_ptrs = (
|
| 56 |
+
value_cache_ptr
|
| 57 |
+
+ pid_t_slot * value_cache_stride_t
|
| 58 |
+
+ pid_hk * value_cache_stride_h
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
key_value_cache_offs = (
|
| 62 |
+
(pid_b // 16) * BLOCK_D_pe_STORE * 16
|
| 63 |
+
+ (pid_b % 16) * K_WIDTH
|
| 64 |
+
+ d_pe_offs_shfl[:, None] * K_WIDTH * 16
|
| 65 |
+
+ k_width_shfl[None, :]
|
| 66 |
+
) * key_cache_stride_d
|
| 67 |
+
|
| 68 |
+
tl.store(
|
| 69 |
+
key_cache_ptrs + key_value_cache_offs,
|
| 70 |
+
k_pe.to(key_cache_ptr.dtype.element_ty),
|
| 71 |
+
)
|
| 72 |
+
tl.store(
|
| 73 |
+
value_cache_ptrs + key_value_cache_offs,
|
| 74 |
+
v.to(value_cache_ptr.dtype.element_ty),
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
d_pe_offs_shfl = tl.arange(0, BLOCK_D_pe_scales // SCALE_K_WIDTH).to(tl.int64)
|
| 78 |
+
k_pe_width_shfl = tl.arange(0, SCALE_K_WIDTH).to(tl.int64)
|
| 79 |
+
k_pe_scales = k_pe_scales.reshape(
|
| 80 |
+
(BLOCK_D_pe_scales // SCALE_K_WIDTH, SCALE_K_WIDTH)
|
| 81 |
+
)
|
| 82 |
+
v_scales = v_scales.reshape((BLOCK_D_pe_scales // SCALE_K_WIDTH, SCALE_K_WIDTH))
|
| 83 |
+
pid_sub_blk = pid_b % 128
|
| 84 |
+
key_cache_pe_scales_offs = (
|
| 85 |
+
BLOCK_SIZE * BLOCK_D_pe_STORE
|
| 86 |
+
+ (pid_b // 128) * BLOCK_D_pe_scales * 128
|
| 87 |
+
+ d_pe_offs_shfl[:, None] * SCALE_K_WIDTH * 128
|
| 88 |
+
+ (pid_sub_blk % 32) * 4 * SCALE_K_WIDTH
|
| 89 |
+
+ (pid_sub_blk // 32) * SCALE_K_WIDTH
|
| 90 |
+
+ k_pe_width_shfl[None, :]
|
| 91 |
+
) * key_cache_stride_d
|
| 92 |
+
e4m3_dtype = tl.float8e4nv
|
| 93 |
+
tl.store(
|
| 94 |
+
key_cache_ptrs + key_cache_pe_scales_offs,
|
| 95 |
+
k_pe_scales.to(e4m3_dtype).to(key_cache_ptr.dtype.element_ty, bitcast=True),
|
| 96 |
+
)
|
| 97 |
+
tl.store(
|
| 98 |
+
value_cache_ptrs + key_cache_pe_scales_offs,
|
| 99 |
+
v_scales.to(e4m3_dtype).to(value_cache_ptr.dtype.element_ty, bitcast=True),
|
| 100 |
+
)
|
| 101 |
+
else:
|
| 102 |
+
|
| 103 |
+
if FLASH_LAYOUT:
|
| 104 |
+
k_out_ptrs = (
|
| 105 |
+
key_cache_ptr
|
| 106 |
+
+ pid_t_slot * key_cache_stride_t
|
| 107 |
+
+ d_pe_offs * key_cache_stride_d
|
| 108 |
+
+ pid_b * key_cache_stride_b
|
| 109 |
+
+ pid_hk * key_cache_stride_h
|
| 110 |
+
)
|
| 111 |
+
else:
|
| 112 |
+
k_pe = tl.reshape(k_pe, (BLOCK_D_pe // X_SIZE, X_SIZE))
|
| 113 |
+
dx_offs = tl.arange(0, BLOCK_D_pe // X_SIZE).to(tl.int64)
|
| 114 |
+
x_offs = tl.arange(0, X_SIZE).to(tl.int64)
|
| 115 |
+
k_out_ptrs = (
|
| 116 |
+
key_cache_ptr
|
| 117 |
+
+ pid_t_slot * key_cache_stride_t
|
| 118 |
+
+ pid_hk * key_cache_stride_h
|
| 119 |
+
+ dx_offs[:, None] * key_cache_stride_d
|
| 120 |
+
+ pid_b * key_cache_stride_b
|
| 121 |
+
+ x_offs[None, :] * key_cache_stride_x
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
if VALUE_SHUFFLE_LAYOUT:
|
| 125 |
+
slot_chunk = pid_b // X_SIZE
|
| 126 |
+
x_off = pid_b % X_SIZE
|
| 127 |
+
v_out_ptrs = (
|
| 128 |
+
value_cache_ptr
|
| 129 |
+
+ pid_t_slot * value_cache_stride_t
|
| 130 |
+
+ pid_hk * value_cache_stride_h
|
| 131 |
+
+ slot_chunk * value_cache_stride_slot_chunk
|
| 132 |
+
+ d_pe_offs * value_cache_stride_d
|
| 133 |
+
+ x_off * value_cache_stride_x
|
| 134 |
+
)
|
| 135 |
+
else:
|
| 136 |
+
v_out_ptrs = (
|
| 137 |
+
value_cache_ptr
|
| 138 |
+
+ pid_t_slot * value_cache_stride_t
|
| 139 |
+
+ pid_hk * value_cache_stride_h
|
| 140 |
+
+ d_pe_offs * value_cache_stride_d
|
| 141 |
+
+ pid_b * value_cache_stride_b
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
tl.store(k_out_ptrs, k_pe.to(key_cache_ptr.dtype.element_ty))
|
| 145 |
+
tl.store(v_out_ptrs, v.to(value_cache_ptr.dtype.element_ty))
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
@triton.jit
|
| 149 |
+
def _unit_cat(
|
| 150 |
+
x1_ptr,
|
| 151 |
+
x2_ptr,
|
| 152 |
+
x_out_ptr,
|
| 153 |
+
b_in,
|
| 154 |
+
b_out,
|
| 155 |
+
h,
|
| 156 |
+
d1_offs,
|
| 157 |
+
d2_offs,
|
| 158 |
+
x1_stride_b,
|
| 159 |
+
x1_stride_h,
|
| 160 |
+
x1_stride_d,
|
| 161 |
+
x2_stride_b,
|
| 162 |
+
x2_stride_h,
|
| 163 |
+
x2_stride_d,
|
| 164 |
+
x_out_stride_b,
|
| 165 |
+
x_out_stride_h,
|
| 166 |
+
x_out_stride_d,
|
| 167 |
+
k_scale,
|
| 168 |
+
BLOCK_D1: tl.constexpr,
|
| 169 |
+
):
|
| 170 |
+
x1_offs = b_in * x1_stride_b + h * x1_stride_h + d1_offs * x1_stride_d
|
| 171 |
+
x2_offs = b_in * x2_stride_b + h * x2_stride_h + d2_offs * x2_stride_d
|
| 172 |
+
x_out_offs = b_out * x_out_stride_b + h * x_out_stride_h
|
| 173 |
+
|
| 174 |
+
x1 = tl.load(x1_ptr + x1_offs)
|
| 175 |
+
x2 = tl.load(x2_ptr + x2_offs)
|
| 176 |
+
|
| 177 |
+
x1 = (x1 / k_scale).to(x_out_ptr.dtype.element_ty)
|
| 178 |
+
x2 = (x2 / k_scale).to(x_out_ptr.dtype.element_ty)
|
| 179 |
+
tl.store(x_out_ptr + x_out_offs + d1_offs * x_out_stride_d, x1)
|
| 180 |
+
tl.store(x_out_ptr + x_out_offs + (d2_offs + BLOCK_D1) * x_out_stride_d, x2)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
@triton.jit
|
| 184 |
+
def _unit_rope_cat(
|
| 185 |
+
x_nope_ptr,
|
| 186 |
+
x_pe_ptr,
|
| 187 |
+
cos,
|
| 188 |
+
sin,
|
| 189 |
+
x_out_ptr,
|
| 190 |
+
b_in,
|
| 191 |
+
b_out,
|
| 192 |
+
h,
|
| 193 |
+
d_nope_offs,
|
| 194 |
+
d_pe_offs,
|
| 195 |
+
x_nope_stride_b,
|
| 196 |
+
x_nope_stride_h,
|
| 197 |
+
x_nope_stride_d,
|
| 198 |
+
x_pe_stride_b,
|
| 199 |
+
x_pe_stride_h,
|
| 200 |
+
x_pe_stride_d,
|
| 201 |
+
x_out_stride_b,
|
| 202 |
+
x_out_stride_h,
|
| 203 |
+
x_out_stride_d,
|
| 204 |
+
k_scale,
|
| 205 |
+
IS_NEOX: tl.constexpr,
|
| 206 |
+
BLOCK_D_nope: tl.constexpr,
|
| 207 |
+
BLOCK_D_pe: tl.constexpr,
|
| 208 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 209 |
+
):
|
| 210 |
+
x_nope_offs = (
|
| 211 |
+
b_in * x_nope_stride_b + h * x_nope_stride_h + d_nope_offs * x_nope_stride_d
|
| 212 |
+
)
|
| 213 |
+
x_pe_offs = b_in * x_pe_stride_b + h * x_pe_stride_h + d_pe_offs * x_pe_stride_d
|
| 214 |
+
x_out_offs = b_out * x_out_stride_b + h * x_out_stride_h
|
| 215 |
+
|
| 216 |
+
x_nope = tl.load(x_nope_ptr + x_nope_offs)
|
| 217 |
+
x_pe = tl.load(x_pe_ptr + x_pe_offs)
|
| 218 |
+
|
| 219 |
+
if IS_NEOX:
|
| 220 |
+
x_rotated_mask = d_pe_offs < BLOCK_D_HALF_pe
|
| 221 |
+
x_pe_rotated = _get_neox_rotated_x_1D(
|
| 222 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 223 |
+
)
|
| 224 |
+
else:
|
| 225 |
+
x_rotated_mask = d_pe_offs % 2 == 0
|
| 226 |
+
x_pe_rotated = _get_gptj_rotated_x_1D(
|
| 227 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
x_pe = x_pe * cos + x_pe_rotated * sin
|
| 231 |
+
x_pe = x_pe / k_scale
|
| 232 |
+
x_nope = x_nope / k_scale
|
| 233 |
+
x_nope = x_nope.to(x_out_ptr.dtype.element_ty)
|
| 234 |
+
x_pe = x_pe.to(x_out_ptr.dtype.element_ty)
|
| 235 |
+
|
| 236 |
+
tl.store(x_out_ptr + x_out_offs + d_nope_offs * x_out_stride_d, x_nope)
|
| 237 |
+
tl.store(x_out_ptr + x_out_offs + (d_pe_offs + BLOCK_D_nope) * x_out_stride_d, x_pe)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
@triton.jit
|
| 241 |
+
def _fused_qk_rope_cat_and_cache_mla_kernel(
|
| 242 |
+
q_nope_ptr,
|
| 243 |
+
q_pe_ptr,
|
| 244 |
+
k_nope_ptr,
|
| 245 |
+
k_pe_ptr,
|
| 246 |
+
pos_ptr,
|
| 247 |
+
cos_ptr,
|
| 248 |
+
sin_ptr,
|
| 249 |
+
q_out_ptr,
|
| 250 |
+
decode_q_pe_out_ptr,
|
| 251 |
+
k_pe_out_ptr,
|
| 252 |
+
q_nope_zeros_out_ptr,
|
| 253 |
+
kv_cache_ptr,
|
| 254 |
+
slot_mapping_ptr,
|
| 255 |
+
B,
|
| 256 |
+
B_slot,
|
| 257 |
+
num_decode_toks_for_zeros,
|
| 258 |
+
q_nope_stride_b,
|
| 259 |
+
q_nope_stride_h,
|
| 260 |
+
q_nope_stride_d,
|
| 261 |
+
q_pe_stride_b,
|
| 262 |
+
q_pe_stride_h,
|
| 263 |
+
q_pe_stride_d,
|
| 264 |
+
k_nope_stride_b,
|
| 265 |
+
k_nope_stride_h,
|
| 266 |
+
k_nope_stride_d,
|
| 267 |
+
k_pe_stride_b,
|
| 268 |
+
k_pe_stride_h,
|
| 269 |
+
k_pe_stride_d,
|
| 270 |
+
pos_stride_b,
|
| 271 |
+
cos_stride_b,
|
| 272 |
+
cos_stride_d,
|
| 273 |
+
q_out_stride_b,
|
| 274 |
+
q_out_stride_h,
|
| 275 |
+
q_out_stride_d,
|
| 276 |
+
decode_q_pe_out_stride_b,
|
| 277 |
+
decode_q_pe_out_stride_h,
|
| 278 |
+
decode_q_pe_out_stride_d,
|
| 279 |
+
k_pe_out_stride_b,
|
| 280 |
+
k_pe_out_stride_h,
|
| 281 |
+
k_pe_out_stride_d,
|
| 282 |
+
q_nope_zeros_out_stride_b,
|
| 283 |
+
q_nope_zeros_out_stride_h,
|
| 284 |
+
q_nope_zeros_out_stride_d,
|
| 285 |
+
kv_cache_stride_b,
|
| 286 |
+
kv_cache_stride_h,
|
| 287 |
+
kv_cache_stride_d,
|
| 288 |
+
k_scale_ptr,
|
| 289 |
+
QH_PER_KH: tl.constexpr,
|
| 290 |
+
QH: tl.constexpr,
|
| 291 |
+
KH: tl.constexpr,
|
| 292 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 293 |
+
IS_NEOX: tl.constexpr,
|
| 294 |
+
BLOCK_D_nope: tl.constexpr,
|
| 295 |
+
BLOCK_D_pe: tl.constexpr,
|
| 296 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 297 |
+
BLOCK_SIZE: tl.constexpr = 1,
|
| 298 |
+
SHUFFLED_KV_CACHE: tl.constexpr = False,
|
| 299 |
+
SCALE_K_WIDTH_NOPE: tl.constexpr = 4,
|
| 300 |
+
SCALE_K_WIDTH_ROPE: tl.constexpr = 4,
|
| 301 |
+
OUTPUT_Q_NOPE_ZEROS_AND_Q_PE: tl.constexpr = False,
|
| 302 |
+
HAVE_K_SCALE: tl.constexpr = False,
|
| 303 |
+
UPCAST_OPERAND: tl.constexpr = False,
|
| 304 |
+
):
|
| 305 |
+
pid = tl.program_id(0)
|
| 306 |
+
|
| 307 |
+
d_nope_offs = tl.arange(0, BLOCK_D_nope).to(tl.int64)
|
| 308 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 309 |
+
|
| 310 |
+
if pid < B * QH:
|
| 311 |
+
# pid_b = pid // QH
|
| 312 |
+
# pid_hq = pid % QH
|
| 313 |
+
# This is a new optimization that prioritized heavy workload WGs first
|
| 314 |
+
pid_hq = pid // B
|
| 315 |
+
pid_b = pid % B
|
| 316 |
+
|
| 317 |
+
if REUSE_FREQS_FRONT_PART:
|
| 318 |
+
if IS_NEOX:
|
| 319 |
+
d_cos_offs = d_pe_offs
|
| 320 |
+
d_cos_offs = tl.where(
|
| 321 |
+
(d_cos_offs >= BLOCK_D_HALF_pe) & (d_cos_offs < BLOCK_D_pe),
|
| 322 |
+
d_cos_offs - BLOCK_D_HALF_pe,
|
| 323 |
+
d_cos_offs,
|
| 324 |
+
).to(d_cos_offs.dtype)
|
| 325 |
+
else:
|
| 326 |
+
d_cos_offs = d_pe_offs // 2
|
| 327 |
+
else:
|
| 328 |
+
d_cos_offs = d_pe_offs
|
| 329 |
+
|
| 330 |
+
pos = tl.load(pos_ptr + pid_b * pos_stride_b)
|
| 331 |
+
cos_offs = pos * cos_stride_b + d_cos_offs * cos_stride_d
|
| 332 |
+
cos = tl.load(cos_ptr + cos_offs)
|
| 333 |
+
sin = tl.load(sin_ptr + cos_offs)
|
| 334 |
+
if UPCAST_OPERAND:
|
| 335 |
+
cos = cos.to(tl.float32)
|
| 336 |
+
sin = sin.to(tl.float32)
|
| 337 |
+
|
| 338 |
+
q_nope_ptrs = (
|
| 339 |
+
q_nope_ptr
|
| 340 |
+
+ pid_b * q_nope_stride_b
|
| 341 |
+
+ pid_hq * q_nope_stride_h
|
| 342 |
+
+ d_nope_offs * q_nope_stride_d
|
| 343 |
+
)
|
| 344 |
+
q_pe_ptrs = (
|
| 345 |
+
q_pe_ptr
|
| 346 |
+
+ pid_b * q_pe_stride_b
|
| 347 |
+
+ pid_hq * q_pe_stride_h
|
| 348 |
+
+ d_pe_offs * q_pe_stride_d
|
| 349 |
+
)
|
| 350 |
+
q_out_ptrs = q_out_ptr + pid_b * q_out_stride_b + pid_hq * q_out_stride_h
|
| 351 |
+
q_nope = tl.load(q_nope_ptrs)
|
| 352 |
+
q_pe = _unit_rope(
|
| 353 |
+
q_pe_ptrs,
|
| 354 |
+
cos,
|
| 355 |
+
sin,
|
| 356 |
+
d_pe_offs,
|
| 357 |
+
IS_NEOX,
|
| 358 |
+
BLOCK_D_pe,
|
| 359 |
+
BLOCK_D_HALF_pe,
|
| 360 |
+
)
|
| 361 |
+
tl.store(
|
| 362 |
+
q_out_ptrs + d_nope_offs * q_out_stride_d,
|
| 363 |
+
q_nope.to(q_out_ptr.dtype.element_ty),
|
| 364 |
+
)
|
| 365 |
+
tl.store(
|
| 366 |
+
q_out_ptrs + (d_pe_offs + BLOCK_D_nope) * q_out_stride_d,
|
| 367 |
+
q_pe.to(q_out_ptr.dtype.element_ty),
|
| 368 |
+
)
|
| 369 |
+
|
| 370 |
+
if OUTPUT_Q_NOPE_ZEROS_AND_Q_PE:
|
| 371 |
+
if pid < num_decode_toks_for_zeros * QH:
|
| 372 |
+
decode_q_pe_out_ptrs = (
|
| 373 |
+
decode_q_pe_out_ptr
|
| 374 |
+
+ pid_b * decode_q_pe_out_stride_b
|
| 375 |
+
+ pid_hq * decode_q_pe_out_stride_h
|
| 376 |
+
)
|
| 377 |
+
tl.store(
|
| 378 |
+
decode_q_pe_out_ptrs + d_pe_offs * decode_q_pe_out_stride_d,
|
| 379 |
+
q_pe.to(decode_q_pe_out_ptr.dtype.element_ty),
|
| 380 |
+
)
|
| 381 |
+
z = tl.zeros(
|
| 382 |
+
(BLOCK_D_nope,), dtype=q_nope_zeros_out_ptr.dtype.element_ty
|
| 383 |
+
)
|
| 384 |
+
tl.store(
|
| 385 |
+
q_nope_zeros_out_ptr
|
| 386 |
+
+ pid_b * q_nope_zeros_out_stride_b
|
| 387 |
+
+ pid_hq * q_nope_zeros_out_stride_h
|
| 388 |
+
+ d_nope_offs * q_nope_zeros_out_stride_d,
|
| 389 |
+
z,
|
| 390 |
+
)
|
| 391 |
+
|
| 392 |
+
# pid_hk = pid_hq // QH_PER_KH
|
| 393 |
+
# is_kv = pid_hq % QH_PER_KH == 0
|
| 394 |
+
# This is a new optimization that prioritized heavy workload WGs first
|
| 395 |
+
pid_hk = pid_hq
|
| 396 |
+
is_kv = pid_hk < KH
|
| 397 |
+
if is_kv:
|
| 398 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_b).to(tl.int64)
|
| 399 |
+
if pid_slot >= 0:
|
| 400 |
+
if BLOCK_SIZE > 1:
|
| 401 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 402 |
+
pid_blk = pid_slot % BLOCK_SIZE
|
| 403 |
+
else:
|
| 404 |
+
pid_t_slot = pid_slot
|
| 405 |
+
pid_blk = 0
|
| 406 |
+
if BLOCK_SIZE > 1:
|
| 407 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 408 |
+
pid_blk = pid_slot % BLOCK_SIZE
|
| 409 |
+
else:
|
| 410 |
+
pid_t_slot = pid_slot
|
| 411 |
+
pid_blk = 0
|
| 412 |
+
if HAVE_K_SCALE:
|
| 413 |
+
k_scale = tl.load(k_scale_ptr)
|
| 414 |
+
else:
|
| 415 |
+
k_scale = 1
|
| 416 |
+
|
| 417 |
+
k_nope_ptrs = (
|
| 418 |
+
k_nope_ptr
|
| 419 |
+
+ pid_b * k_nope_stride_b
|
| 420 |
+
+ pid_hk * k_nope_stride_h
|
| 421 |
+
+ d_nope_offs * k_nope_stride_d
|
| 422 |
+
)
|
| 423 |
+
k_pe_ptrs = (
|
| 424 |
+
k_pe_ptr
|
| 425 |
+
+ pid_b * k_pe_stride_b
|
| 426 |
+
+ pid_hk * k_pe_stride_h
|
| 427 |
+
+ d_pe_offs * k_pe_stride_d
|
| 428 |
+
)
|
| 429 |
+
k_pe_out_ptrs = (
|
| 430 |
+
k_pe_out_ptr
|
| 431 |
+
+ pid_b * k_pe_out_stride_b
|
| 432 |
+
+ pid_hk * k_pe_out_stride_h
|
| 433 |
+
+ d_pe_offs * k_pe_out_stride_d
|
| 434 |
+
)
|
| 435 |
+
k_nope = tl.load(k_nope_ptrs)
|
| 436 |
+
k_pe = _unit_rope(
|
| 437 |
+
k_pe_ptrs,
|
| 438 |
+
cos,
|
| 439 |
+
sin,
|
| 440 |
+
d_pe_offs,
|
| 441 |
+
IS_NEOX,
|
| 442 |
+
BLOCK_D_pe,
|
| 443 |
+
BLOCK_D_HALF_pe,
|
| 444 |
+
)
|
| 445 |
+
tl.store(k_pe_out_ptrs, k_pe.to(k_pe_out_ptr.dtype.element_ty))
|
| 446 |
+
k_scale_rcprl = (1 / k_scale).to(tl.float32)
|
| 447 |
+
k_nope = k_nope.to(tl.float32) * k_scale_rcprl
|
| 448 |
+
k_pe = k_pe.to(tl.float32) * k_scale_rcprl
|
| 449 |
+
|
| 450 |
+
_store_mla_kv_cache(
|
| 451 |
+
kv_cache_ptr,
|
| 452 |
+
pid_t_slot,
|
| 453 |
+
pid_hk,
|
| 454 |
+
pid_blk,
|
| 455 |
+
d_nope_offs,
|
| 456 |
+
d_pe_offs,
|
| 457 |
+
kv_cache_stride_b,
|
| 458 |
+
kv_cache_stride_h,
|
| 459 |
+
kv_cache_stride_d,
|
| 460 |
+
k_nope,
|
| 461 |
+
k_pe,
|
| 462 |
+
BLOCK_D_nope,
|
| 463 |
+
BLOCK_D_pe,
|
| 464 |
+
BLOCK_SIZE,
|
| 465 |
+
SHUFFLED_KV_CACHE,
|
| 466 |
+
SCALE_K_WIDTH_NOPE,
|
| 467 |
+
SCALE_K_WIDTH_ROPE,
|
| 468 |
+
)
|
| 469 |
+
else:
|
| 470 |
+
pid = pid - B * QH + B * KH
|
| 471 |
+
if pid < B_slot * KH:
|
| 472 |
+
pid_b = pid // KH
|
| 473 |
+
pid_hk = pid % KH
|
| 474 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_b).to(tl.int64)
|
| 475 |
+
if pid_slot >= 0:
|
| 476 |
+
if BLOCK_SIZE > 1:
|
| 477 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 478 |
+
pid_blk = pid_slot % BLOCK_SIZE
|
| 479 |
+
else:
|
| 480 |
+
pid_t_slot = pid_slot
|
| 481 |
+
pid_blk = 0
|
| 482 |
+
if BLOCK_SIZE > 1:
|
| 483 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 484 |
+
pid_blk = pid_slot % BLOCK_SIZE
|
| 485 |
+
else:
|
| 486 |
+
pid_t_slot = pid_slot
|
| 487 |
+
pid_blk = 0
|
| 488 |
+
if HAVE_K_SCALE:
|
| 489 |
+
k_scale = tl.load(k_scale_ptr)
|
| 490 |
+
else:
|
| 491 |
+
k_scale = 1
|
| 492 |
+
|
| 493 |
+
k_nope_ptrs = (
|
| 494 |
+
k_nope_ptr
|
| 495 |
+
+ pid_b * k_nope_stride_b
|
| 496 |
+
+ pid_hk * k_nope_stride_h
|
| 497 |
+
+ d_nope_offs * k_nope_stride_d
|
| 498 |
+
)
|
| 499 |
+
k_pe_ptrs = (
|
| 500 |
+
k_pe_ptr
|
| 501 |
+
+ pid_b * k_pe_stride_b
|
| 502 |
+
+ pid_hk * k_pe_stride_h
|
| 503 |
+
+ d_pe_offs * k_pe_stride_d
|
| 504 |
+
)
|
| 505 |
+
k_pe_out_ptrs = (
|
| 506 |
+
k_pe_out_ptr
|
| 507 |
+
+ pid_b * k_pe_out_stride_b
|
| 508 |
+
+ pid_hk * k_pe_out_stride_h
|
| 509 |
+
+ d_pe_offs * k_pe_out_stride_d
|
| 510 |
+
)
|
| 511 |
+
k_nope = tl.load(k_nope_ptrs)
|
| 512 |
+
k_pe = tl.load(k_pe_ptrs)
|
| 513 |
+
tl.store(k_pe_out_ptrs, k_pe.to(k_pe_out_ptr.dtype.element_ty))
|
| 514 |
+
k_scale_rcprl = (1 / k_scale).to(tl.float32)
|
| 515 |
+
k_nope = k_nope.to(tl.float32) * k_scale_rcprl
|
| 516 |
+
k_pe = k_pe.to(tl.float32) * k_scale_rcprl
|
| 517 |
+
|
| 518 |
+
_store_mla_kv_cache(
|
| 519 |
+
kv_cache_ptr,
|
| 520 |
+
pid_t_slot,
|
| 521 |
+
pid_hk,
|
| 522 |
+
pid_blk,
|
| 523 |
+
d_nope_offs,
|
| 524 |
+
d_pe_offs,
|
| 525 |
+
kv_cache_stride_b,
|
| 526 |
+
kv_cache_stride_h,
|
| 527 |
+
kv_cache_stride_d,
|
| 528 |
+
k_nope,
|
| 529 |
+
k_pe,
|
| 530 |
+
BLOCK_D_nope,
|
| 531 |
+
BLOCK_D_pe,
|
| 532 |
+
BLOCK_SIZE,
|
| 533 |
+
SHUFFLED_KV_CACHE,
|
| 534 |
+
SCALE_K_WIDTH_NOPE,
|
| 535 |
+
SCALE_K_WIDTH_ROPE,
|
| 536 |
+
)
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
@triton.jit
|
| 540 |
+
def _unit_rope(
|
| 541 |
+
x_ptrs,
|
| 542 |
+
cos,
|
| 543 |
+
sin,
|
| 544 |
+
d_pe_offs,
|
| 545 |
+
IS_NEOX: tl.constexpr,
|
| 546 |
+
BLOCK_D_pe: tl.constexpr,
|
| 547 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 548 |
+
):
|
| 549 |
+
x_pe = tl.load(x_ptrs)
|
| 550 |
+
|
| 551 |
+
if IS_NEOX:
|
| 552 |
+
x_rotated_mask = d_pe_offs < BLOCK_D_HALF_pe
|
| 553 |
+
x_pe_rotated = _get_neox_rotated_x_1D(
|
| 554 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 555 |
+
)
|
| 556 |
+
else:
|
| 557 |
+
x_rotated_mask = d_pe_offs % 2 == 0
|
| 558 |
+
x_pe_rotated = _get_gptj_rotated_x_1D(
|
| 559 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
x_pe = x_pe * cos + x_pe_rotated * sin
|
| 563 |
+
|
| 564 |
+
return x_pe
|
| 565 |
+
|
| 566 |
+
|
| 567 |
+
@triton.jit
|
| 568 |
+
def _fused_qk_rope_reshape_and_cache_kernel(
|
| 569 |
+
q_ptr,
|
| 570 |
+
k_ptr,
|
| 571 |
+
v_ptr,
|
| 572 |
+
pos_ptr,
|
| 573 |
+
cos_ptr,
|
| 574 |
+
sin_ptr,
|
| 575 |
+
offs_ptr,
|
| 576 |
+
key_cache_ptr,
|
| 577 |
+
value_cache_ptr,
|
| 578 |
+
slot_mapping_ptr,
|
| 579 |
+
q_out_ptr,
|
| 580 |
+
k_out_ptr,
|
| 581 |
+
zeros_out_ptr,
|
| 582 |
+
T,
|
| 583 |
+
T_slot,
|
| 584 |
+
q_stride_t,
|
| 585 |
+
q_stride_h,
|
| 586 |
+
q_stride_d,
|
| 587 |
+
k_stride_t,
|
| 588 |
+
k_stride_h,
|
| 589 |
+
k_stride_d,
|
| 590 |
+
v_stride_t,
|
| 591 |
+
v_stride_h,
|
| 592 |
+
v_stride_d,
|
| 593 |
+
cos_stride_t,
|
| 594 |
+
cos_stride_d,
|
| 595 |
+
q_out_stride_t,
|
| 596 |
+
q_out_stride_h,
|
| 597 |
+
q_out_stride_d,
|
| 598 |
+
k_out_stride_t,
|
| 599 |
+
k_out_stride_h,
|
| 600 |
+
k_out_stride_d,
|
| 601 |
+
key_cache_stride_t,
|
| 602 |
+
key_cache_stride_h,
|
| 603 |
+
key_cache_stride_d,
|
| 604 |
+
key_cache_stride_b,
|
| 605 |
+
key_cache_stride_x,
|
| 606 |
+
value_cache_stride_t,
|
| 607 |
+
value_cache_stride_h,
|
| 608 |
+
value_cache_stride_d,
|
| 609 |
+
value_cache_stride_b,
|
| 610 |
+
value_cache_stride_slot_chunk,
|
| 611 |
+
value_cache_stride_x,
|
| 612 |
+
zeros_out_stride_t,
|
| 613 |
+
zeros_out_stride_h,
|
| 614 |
+
zeros_out_stride_d,
|
| 615 |
+
k_scale_ptr,
|
| 616 |
+
v_scale_ptr,
|
| 617 |
+
QH_PER_KH: tl.constexpr,
|
| 618 |
+
QH: tl.constexpr,
|
| 619 |
+
KH: tl.constexpr,
|
| 620 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 621 |
+
IS_NEOX: tl.constexpr,
|
| 622 |
+
BLOCK_D_pe: tl.constexpr,
|
| 623 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 624 |
+
BLOCK_SIZE: tl.constexpr,
|
| 625 |
+
X_SIZE: tl.constexpr,
|
| 626 |
+
SCALE_K_WIDTH: tl.constexpr,
|
| 627 |
+
FLASH_LAYOUT: tl.constexpr,
|
| 628 |
+
VALUE_SHUFFLE_LAYOUT: tl.constexpr = False,
|
| 629 |
+
HAVE_POS: tl.constexpr = False,
|
| 630 |
+
HAVE_K_SCALE: tl.constexpr = False,
|
| 631 |
+
HAVE_V_SCALE: tl.constexpr = False,
|
| 632 |
+
HAVE_ZEROS: tl.constexpr = False,
|
| 633 |
+
UPCAST_OPERAND: tl.constexpr = False,
|
| 634 |
+
):
|
| 635 |
+
|
| 636 |
+
tl.assume(q_stride_t >= 0)
|
| 637 |
+
tl.assume(q_stride_h >= 0)
|
| 638 |
+
tl.assume(q_stride_d >= 0)
|
| 639 |
+
tl.assume(k_stride_t >= 0)
|
| 640 |
+
tl.assume(k_stride_h >= 0)
|
| 641 |
+
tl.assume(k_stride_d >= 0)
|
| 642 |
+
tl.assume(v_stride_t >= 0)
|
| 643 |
+
tl.assume(v_stride_h >= 0)
|
| 644 |
+
tl.assume(v_stride_d >= 0)
|
| 645 |
+
tl.assume(cos_stride_t >= 0)
|
| 646 |
+
tl.assume(cos_stride_d >= 0)
|
| 647 |
+
tl.assume(q_out_stride_t >= 0)
|
| 648 |
+
tl.assume(q_out_stride_h >= 0)
|
| 649 |
+
tl.assume(q_out_stride_d >= 0)
|
| 650 |
+
tl.assume(k_out_stride_t >= 0)
|
| 651 |
+
tl.assume(k_out_stride_h >= 0)
|
| 652 |
+
tl.assume(k_out_stride_d >= 0)
|
| 653 |
+
tl.assume(key_cache_stride_t >= 0)
|
| 654 |
+
tl.assume(key_cache_stride_h >= 0)
|
| 655 |
+
tl.assume(key_cache_stride_d >= 0)
|
| 656 |
+
tl.assume(key_cache_stride_b >= 0)
|
| 657 |
+
tl.assume(key_cache_stride_x >= 0)
|
| 658 |
+
tl.assume(value_cache_stride_t >= 0)
|
| 659 |
+
tl.assume(value_cache_stride_h >= 0)
|
| 660 |
+
tl.assume(value_cache_stride_d >= 0)
|
| 661 |
+
tl.assume(value_cache_stride_b >= 0)
|
| 662 |
+
tl.assume(value_cache_stride_slot_chunk >= 0)
|
| 663 |
+
tl.assume(value_cache_stride_x >= 0)
|
| 664 |
+
tl.assume(zeros_out_stride_t >= 0)
|
| 665 |
+
tl.assume(zeros_out_stride_h >= 0)
|
| 666 |
+
tl.assume(zeros_out_stride_d >= 0)
|
| 667 |
+
|
| 668 |
+
pid = tl.program_id(0)
|
| 669 |
+
tl.assume(pid >= 0)
|
| 670 |
+
|
| 671 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 672 |
+
|
| 673 |
+
if pid < T * QH:
|
| 674 |
+
pid_t = pid // QH
|
| 675 |
+
pid_hq = pid % QH
|
| 676 |
+
if REUSE_FREQS_FRONT_PART:
|
| 677 |
+
if IS_NEOX:
|
| 678 |
+
d_cos_offs = d_pe_offs
|
| 679 |
+
d_cos_offs = tl.where(
|
| 680 |
+
(d_cos_offs >= BLOCK_D_HALF_pe) & (d_cos_offs < BLOCK_D_pe),
|
| 681 |
+
d_cos_offs - BLOCK_D_HALF_pe,
|
| 682 |
+
d_cos_offs,
|
| 683 |
+
).to(d_cos_offs.dtype)
|
| 684 |
+
else:
|
| 685 |
+
d_cos_offs = d_pe_offs // 2
|
| 686 |
+
else:
|
| 687 |
+
d_cos_offs = d_pe_offs
|
| 688 |
+
|
| 689 |
+
pos = tl.load(pos_ptr + pid_t)
|
| 690 |
+
if HAVE_POS:
|
| 691 |
+
offset = tl.load(offs_ptr + pid_t)
|
| 692 |
+
pos = pos + offset
|
| 693 |
+
cos_offs = pos * cos_stride_t + d_cos_offs * cos_stride_d
|
| 694 |
+
cos = tl.load(cos_ptr + cos_offs)
|
| 695 |
+
sin = tl.load(sin_ptr + cos_offs)
|
| 696 |
+
if UPCAST_OPERAND:
|
| 697 |
+
cos = cos.to(tl.float32)
|
| 698 |
+
sin = sin.to(tl.float32)
|
| 699 |
+
|
| 700 |
+
q_ptrs = (
|
| 701 |
+
q_ptr + pid_t * q_stride_t + pid_hq * q_stride_h + d_pe_offs * q_stride_d
|
| 702 |
+
)
|
| 703 |
+
q_pe = _unit_rope(
|
| 704 |
+
q_ptrs,
|
| 705 |
+
cos,
|
| 706 |
+
sin,
|
| 707 |
+
d_pe_offs,
|
| 708 |
+
IS_NEOX,
|
| 709 |
+
BLOCK_D_pe,
|
| 710 |
+
BLOCK_D_HALF_pe,
|
| 711 |
+
)
|
| 712 |
+
q_out_ptrs = (
|
| 713 |
+
q_out_ptr
|
| 714 |
+
+ pid_t * q_out_stride_t
|
| 715 |
+
+ pid_hq * q_out_stride_h
|
| 716 |
+
+ d_pe_offs * q_out_stride_d
|
| 717 |
+
)
|
| 718 |
+
tl.store(q_out_ptrs, q_pe.to(q_out_ptr.dtype.element_ty))
|
| 719 |
+
|
| 720 |
+
if HAVE_ZEROS:
|
| 721 |
+
z = tl.zeros((BLOCK_D_pe,), dtype=zeros_out_ptr.dtype.element_ty)
|
| 722 |
+
zeros_out_ptrs = (
|
| 723 |
+
zeros_out_ptr
|
| 724 |
+
+ pid_t * zeros_out_stride_t
|
| 725 |
+
+ pid_hq * zeros_out_stride_h
|
| 726 |
+
+ d_pe_offs * zeros_out_stride_d
|
| 727 |
+
)
|
| 728 |
+
tl.store(zeros_out_ptrs, z)
|
| 729 |
+
|
| 730 |
+
if pid_hq % QH_PER_KH == 0:
|
| 731 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_t).to(tl.int64)
|
| 732 |
+
if pid_slot >= 0:
|
| 733 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 734 |
+
pid_b = pid_slot % BLOCK_SIZE
|
| 735 |
+
pid_hk = pid_hq // QH_PER_KH
|
| 736 |
+
if HAVE_K_SCALE:
|
| 737 |
+
k_scale = tl.load(k_scale_ptr)
|
| 738 |
+
else:
|
| 739 |
+
k_scale = 1
|
| 740 |
+
k_ptrs = (
|
| 741 |
+
k_ptr
|
| 742 |
+
+ pid_t * k_stride_t
|
| 743 |
+
+ pid_hk * k_stride_h
|
| 744 |
+
+ d_pe_offs * k_stride_d
|
| 745 |
+
)
|
| 746 |
+
k_pe = _unit_rope(
|
| 747 |
+
k_ptrs,
|
| 748 |
+
cos,
|
| 749 |
+
sin,
|
| 750 |
+
d_pe_offs,
|
| 751 |
+
IS_NEOX,
|
| 752 |
+
BLOCK_D_pe,
|
| 753 |
+
BLOCK_D_HALF_pe,
|
| 754 |
+
)
|
| 755 |
+
|
| 756 |
+
k_out_ptrs = (
|
| 757 |
+
k_out_ptr
|
| 758 |
+
+ pid_t * k_out_stride_t
|
| 759 |
+
+ pid_hk * k_out_stride_h
|
| 760 |
+
+ d_pe_offs * k_out_stride_d
|
| 761 |
+
)
|
| 762 |
+
tl.store(k_out_ptrs, k_pe.to(k_out_ptr.dtype.element_ty))
|
| 763 |
+
|
| 764 |
+
k_scale_rcprl = 1 / k_scale
|
| 765 |
+
k_pe = k_pe * k_scale_rcprl
|
| 766 |
+
|
| 767 |
+
v_ptrs = (
|
| 768 |
+
v_ptr
|
| 769 |
+
+ pid_t * v_stride_t
|
| 770 |
+
+ pid_hk * v_stride_h
|
| 771 |
+
+ d_pe_offs * v_stride_d
|
| 772 |
+
)
|
| 773 |
+
if HAVE_V_SCALE:
|
| 774 |
+
v_scale = tl.load(v_scale_ptr)
|
| 775 |
+
else:
|
| 776 |
+
v_scale = 1
|
| 777 |
+
v_scale_rcprl = 1 / v_scale
|
| 778 |
+
v = tl.load(v_ptrs) * v_scale_rcprl
|
| 779 |
+
|
| 780 |
+
_store_kv_cache_kernel(
|
| 781 |
+
key_cache_ptr,
|
| 782 |
+
value_cache_ptr,
|
| 783 |
+
pid_t_slot,
|
| 784 |
+
pid_hk,
|
| 785 |
+
pid_b,
|
| 786 |
+
d_pe_offs,
|
| 787 |
+
k_pe,
|
| 788 |
+
v,
|
| 789 |
+
key_cache_stride_t,
|
| 790 |
+
key_cache_stride_h,
|
| 791 |
+
key_cache_stride_d,
|
| 792 |
+
key_cache_stride_b,
|
| 793 |
+
key_cache_stride_x,
|
| 794 |
+
value_cache_stride_t,
|
| 795 |
+
value_cache_stride_h,
|
| 796 |
+
value_cache_stride_d,
|
| 797 |
+
value_cache_stride_b,
|
| 798 |
+
value_cache_stride_x,
|
| 799 |
+
value_cache_stride_slot_chunk,
|
| 800 |
+
BLOCK_D_pe,
|
| 801 |
+
BLOCK_SIZE,
|
| 802 |
+
X_SIZE,
|
| 803 |
+
FLASH_LAYOUT,
|
| 804 |
+
VALUE_SHUFFLE_LAYOUT,
|
| 805 |
+
SCALE_K_WIDTH,
|
| 806 |
+
)
|
| 807 |
+
else:
|
| 808 |
+
pid = pid - T * QH + T * KH
|
| 809 |
+
if pid < T_slot * KH:
|
| 810 |
+
pid_t = pid // KH
|
| 811 |
+
pid_hk = pid % KH
|
| 812 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_t).to(tl.int64)
|
| 813 |
+
if pid_slot >= 0:
|
| 814 |
+
pid_t_slot = pid_slot // BLOCK_SIZE
|
| 815 |
+
pid_b = pid_slot % BLOCK_SIZE
|
| 816 |
+
if HAVE_K_SCALE:
|
| 817 |
+
k_scale = tl.load(k_scale_ptr)
|
| 818 |
+
else:
|
| 819 |
+
k_scale = 1
|
| 820 |
+
k_ptrs = (
|
| 821 |
+
k_ptr
|
| 822 |
+
+ pid_t * k_stride_t
|
| 823 |
+
+ pid_hk * k_stride_h
|
| 824 |
+
+ d_pe_offs * k_stride_d
|
| 825 |
+
)
|
| 826 |
+
|
| 827 |
+
k_pe = tl.load(k_ptrs)
|
| 828 |
+
|
| 829 |
+
k_out_ptrs = (
|
| 830 |
+
k_out_ptr
|
| 831 |
+
+ pid_t * k_out_stride_t
|
| 832 |
+
+ pid_hk * k_out_stride_h
|
| 833 |
+
+ d_pe_offs * k_out_stride_d
|
| 834 |
+
)
|
| 835 |
+
tl.store(k_out_ptrs, k_pe.to(k_out_ptr.dtype.element_ty))
|
| 836 |
+
|
| 837 |
+
k_scale_rcprl = 1 / k_scale
|
| 838 |
+
k_pe = k_pe * k_scale_rcprl
|
| 839 |
+
|
| 840 |
+
v_ptrs = (
|
| 841 |
+
v_ptr
|
| 842 |
+
+ pid_t * v_stride_t
|
| 843 |
+
+ pid_hk * v_stride_h
|
| 844 |
+
+ d_pe_offs * v_stride_d
|
| 845 |
+
)
|
| 846 |
+
if HAVE_V_SCALE:
|
| 847 |
+
v_scale = tl.load(v_scale_ptr)
|
| 848 |
+
else:
|
| 849 |
+
v_scale = 1
|
| 850 |
+
v_scale_rcprl = 1 / v_scale
|
| 851 |
+
v = tl.load(v_ptrs) * v_scale_rcprl
|
| 852 |
+
|
| 853 |
+
_store_kv_cache_kernel(
|
| 854 |
+
key_cache_ptr,
|
| 855 |
+
value_cache_ptr,
|
| 856 |
+
pid_t_slot,
|
| 857 |
+
pid_hk,
|
| 858 |
+
pid_b,
|
| 859 |
+
d_pe_offs,
|
| 860 |
+
k_pe,
|
| 861 |
+
v,
|
| 862 |
+
key_cache_stride_t,
|
| 863 |
+
key_cache_stride_h,
|
| 864 |
+
key_cache_stride_d,
|
| 865 |
+
key_cache_stride_b,
|
| 866 |
+
key_cache_stride_x,
|
| 867 |
+
value_cache_stride_t,
|
| 868 |
+
value_cache_stride_h,
|
| 869 |
+
value_cache_stride_d,
|
| 870 |
+
value_cache_stride_b,
|
| 871 |
+
value_cache_stride_x,
|
| 872 |
+
value_cache_stride_slot_chunk,
|
| 873 |
+
BLOCK_D_pe,
|
| 874 |
+
BLOCK_SIZE,
|
| 875 |
+
X_SIZE,
|
| 876 |
+
FLASH_LAYOUT,
|
| 877 |
+
VALUE_SHUFFLE_LAYOUT,
|
| 878 |
+
SCALE_K_WIDTH,
|
| 879 |
+
)
|
| 880 |
+
|
| 881 |
+
|
| 882 |
+
@triton.jit
|
| 883 |
+
def _fused_qk_rope_cosine_cache_llama_kernel(
|
| 884 |
+
q_ptr,
|
| 885 |
+
k_ptr,
|
| 886 |
+
v_ptr,
|
| 887 |
+
pos_ptr,
|
| 888 |
+
cos_ptr,
|
| 889 |
+
sin_ptr,
|
| 890 |
+
offs_ptr,
|
| 891 |
+
key_cache_ptr,
|
| 892 |
+
value_cache_ptr,
|
| 893 |
+
slot_mapping_ptr,
|
| 894 |
+
q_out_ptr,
|
| 895 |
+
T,
|
| 896 |
+
T_slot,
|
| 897 |
+
q_stride_t,
|
| 898 |
+
q_stride_h,
|
| 899 |
+
q_stride_d,
|
| 900 |
+
k_stride_t,
|
| 901 |
+
k_stride_h,
|
| 902 |
+
k_stride_d,
|
| 903 |
+
v_stride_t,
|
| 904 |
+
v_stride_h,
|
| 905 |
+
v_stride_d,
|
| 906 |
+
cos_stride_t,
|
| 907 |
+
cos_stride_d,
|
| 908 |
+
q_out_stride_t,
|
| 909 |
+
q_out_stride_h,
|
| 910 |
+
q_out_stride_d,
|
| 911 |
+
key_cache_stride_t,
|
| 912 |
+
key_cache_stride_h,
|
| 913 |
+
key_cache_stride_d,
|
| 914 |
+
key_cache_stride_b,
|
| 915 |
+
key_cache_stride_x,
|
| 916 |
+
value_cache_stride_t,
|
| 917 |
+
value_cache_stride_h,
|
| 918 |
+
value_cache_stride_d,
|
| 919 |
+
value_cache_stride_b,
|
| 920 |
+
k_scale_ptr,
|
| 921 |
+
v_scale_ptr,
|
| 922 |
+
QH_PER_KH: tl.constexpr,
|
| 923 |
+
QH: tl.constexpr,
|
| 924 |
+
KH: tl.constexpr,
|
| 925 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 926 |
+
IS_NEOX: tl.constexpr,
|
| 927 |
+
BLOCK_D_pe: tl.constexpr,
|
| 928 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 929 |
+
BLOCK_SIZE: tl.constexpr,
|
| 930 |
+
X_SIZE: tl.constexpr,
|
| 931 |
+
FLASH_LAYOUT: tl.constexpr,
|
| 932 |
+
HAVE_POS: tl.constexpr = False,
|
| 933 |
+
HAVE_K_SCALE: tl.constexpr = False,
|
| 934 |
+
HAVE_V_SCALE: tl.constexpr = False,
|
| 935 |
+
):
|
| 936 |
+
pid = tl.program_id(0)
|
| 937 |
+
|
| 938 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe).to(tl.int64)
|
| 939 |
+
|
| 940 |
+
if pid < T * QH:
|
| 941 |
+
pid_t = pid // QH
|
| 942 |
+
pid_hq = pid % QH
|
| 943 |
+
if REUSE_FREQS_FRONT_PART:
|
| 944 |
+
if IS_NEOX:
|
| 945 |
+
d_cos_offs = d_pe_offs
|
| 946 |
+
d_cos_offs = tl.where(
|
| 947 |
+
(d_cos_offs >= BLOCK_D_HALF_pe) & (d_cos_offs < BLOCK_D_pe),
|
| 948 |
+
d_cos_offs - BLOCK_D_HALF_pe,
|
| 949 |
+
d_cos_offs,
|
| 950 |
+
).to(d_cos_offs.dtype)
|
| 951 |
+
else:
|
| 952 |
+
d_cos_offs = d_pe_offs // 2
|
| 953 |
+
d_cos_mask = d_cos_offs < BLOCK_D_HALF_pe
|
| 954 |
+
|
| 955 |
+
else:
|
| 956 |
+
d_cos_offs = d_pe_offs
|
| 957 |
+
|
| 958 |
+
pos = tl.load(pos_ptr + pid_t)
|
| 959 |
+
if HAVE_POS:
|
| 960 |
+
offset = tl.load(offs_ptr + pid_t)
|
| 961 |
+
pos = pos + offset
|
| 962 |
+
cos_offs = pos * cos_stride_t + d_cos_offs * cos_stride_d
|
| 963 |
+
cos = tl.load(cos_ptr + cos_offs).to(tl.float64)
|
| 964 |
+
sin = tl.load(sin_ptr + cos_offs).to(tl.float64)
|
| 965 |
+
|
| 966 |
+
q_ptrs = (
|
| 967 |
+
q_ptr + pid_t * q_stride_t + pid_hq * q_stride_h + d_pe_offs * q_stride_d
|
| 968 |
+
)
|
| 969 |
+
q_pe = _unit_rope(
|
| 970 |
+
q_ptrs,
|
| 971 |
+
cos,
|
| 972 |
+
sin,
|
| 973 |
+
d_pe_offs,
|
| 974 |
+
IS_NEOX,
|
| 975 |
+
BLOCK_D_pe,
|
| 976 |
+
BLOCK_D_HALF_pe,
|
| 977 |
+
)
|
| 978 |
+
q_out_ptrs = (
|
| 979 |
+
q_out_ptr
|
| 980 |
+
+ pid_t * q_out_stride_t
|
| 981 |
+
+ pid_hq * q_out_stride_h
|
| 982 |
+
+ d_pe_offs * q_out_stride_d
|
| 983 |
+
)
|
| 984 |
+
tl.store(q_out_ptrs, q_pe.to(q_out_ptr.dtype.element_ty))
|
| 985 |
+
|
| 986 |
+
if pid_hq % QH_PER_KH == 0:
|
| 987 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_t).to(tl.int64)
|
| 988 |
+
if pid_slot >= 0:
|
| 989 |
+
pid_t_slot = pid_t
|
| 990 |
+
pid_b = pid_slot
|
| 991 |
+
pid_hk = pid_hq // QH_PER_KH
|
| 992 |
+
if HAVE_K_SCALE:
|
| 993 |
+
k_scale = tl.load(k_scale_ptr)
|
| 994 |
+
else:
|
| 995 |
+
k_scale = 1
|
| 996 |
+
k_ptrs = (
|
| 997 |
+
k_ptr
|
| 998 |
+
+ pid_t * k_stride_t
|
| 999 |
+
+ pid_hk * k_stride_h
|
| 1000 |
+
+ d_pe_offs * k_stride_d
|
| 1001 |
+
)
|
| 1002 |
+
k_pe = _unit_rope(
|
| 1003 |
+
k_ptrs,
|
| 1004 |
+
cos,
|
| 1005 |
+
sin,
|
| 1006 |
+
d_pe_offs,
|
| 1007 |
+
IS_NEOX,
|
| 1008 |
+
BLOCK_D_pe,
|
| 1009 |
+
BLOCK_D_HALF_pe,
|
| 1010 |
+
)
|
| 1011 |
+
|
| 1012 |
+
k_scale_rcprl = 1 / k_scale
|
| 1013 |
+
k_pe = k_pe * k_scale_rcprl
|
| 1014 |
+
|
| 1015 |
+
if FLASH_LAYOUT:
|
| 1016 |
+
k_out_ptrs = (
|
| 1017 |
+
key_cache_ptr
|
| 1018 |
+
+ pid_t_slot * key_cache_stride_t
|
| 1019 |
+
+ pid_b * key_cache_stride_b
|
| 1020 |
+
+ pid_hk * key_cache_stride_h
|
| 1021 |
+
+ d_pe_offs * key_cache_stride_d
|
| 1022 |
+
)
|
| 1023 |
+
else:
|
| 1024 |
+
k_pe = tl.reshape(k_pe, (BLOCK_D_pe // X_SIZE, X_SIZE))
|
| 1025 |
+
dx_offs = tl.arange(0, BLOCK_D_pe // X_SIZE).to(tl.int64)
|
| 1026 |
+
x_offs = tl.arange(0, X_SIZE).to(tl.int64)
|
| 1027 |
+
k_out_ptrs = (
|
| 1028 |
+
key_cache_ptr
|
| 1029 |
+
+ pid_t_slot * key_cache_stride_t
|
| 1030 |
+
+ pid_hk * key_cache_stride_h
|
| 1031 |
+
+ dx_offs[:, None] * key_cache_stride_d
|
| 1032 |
+
+ pid_b * key_cache_stride_b
|
| 1033 |
+
+ x_offs[None, :] * key_cache_stride_x
|
| 1034 |
+
)
|
| 1035 |
+
|
| 1036 |
+
tl.store(k_out_ptrs, k_pe.to(key_cache_ptr.dtype.element_ty))
|
| 1037 |
+
|
| 1038 |
+
v_ptrs = (
|
| 1039 |
+
v_ptr
|
| 1040 |
+
+ pid_t * v_stride_t
|
| 1041 |
+
+ pid_hk * v_stride_h
|
| 1042 |
+
+ d_pe_offs * v_stride_d
|
| 1043 |
+
)
|
| 1044 |
+
if HAVE_V_SCALE:
|
| 1045 |
+
v_scale = tl.load(v_scale_ptr)
|
| 1046 |
+
else:
|
| 1047 |
+
v_scale = 1
|
| 1048 |
+
v_scale_rcprl = 1 / v_scale
|
| 1049 |
+
v = tl.load(v_ptrs) * v_scale_rcprl
|
| 1050 |
+
v_out_ptrs = (
|
| 1051 |
+
value_cache_ptr
|
| 1052 |
+
+ pid_t_slot * value_cache_stride_t
|
| 1053 |
+
+ pid_hk * value_cache_stride_h
|
| 1054 |
+
+ d_pe_offs * value_cache_stride_d
|
| 1055 |
+
+ pid_b * value_cache_stride_b
|
| 1056 |
+
)
|
| 1057 |
+
tl.store(v_out_ptrs, v.to(value_cache_ptr.dtype.element_ty))
|
| 1058 |
+
else:
|
| 1059 |
+
pid = pid - T * QH + T * KH
|
| 1060 |
+
if pid < T_slot * KH:
|
| 1061 |
+
pid_t = pid // KH
|
| 1062 |
+
pid_hk = pid % KH
|
| 1063 |
+
pid_slot = tl.load(slot_mapping_ptr + pid_t).to(tl.int64)
|
| 1064 |
+
if pid_slot >= 0:
|
| 1065 |
+
pid_t_slot = pid_t
|
| 1066 |
+
pid_b = pid_slot
|
| 1067 |
+
if HAVE_K_SCALE:
|
| 1068 |
+
k_scale = tl.load(k_scale_ptr)
|
| 1069 |
+
else:
|
| 1070 |
+
k_scale = 1
|
| 1071 |
+
k_ptrs = (
|
| 1072 |
+
k_ptr
|
| 1073 |
+
+ pid_t * k_stride_t
|
| 1074 |
+
+ pid_hk * k_stride_h
|
| 1075 |
+
+ d_pe_offs * k_stride_d
|
| 1076 |
+
)
|
| 1077 |
+
|
| 1078 |
+
k_pe = tl.load(k_ptrs)
|
| 1079 |
+
|
| 1080 |
+
k_scale_rcprl = 1 / k_scale
|
| 1081 |
+
k_pe = k_pe * k_scale_rcprl
|
| 1082 |
+
|
| 1083 |
+
if FLASH_LAYOUT:
|
| 1084 |
+
k_out_ptrs = (
|
| 1085 |
+
key_cache_ptr
|
| 1086 |
+
+ pid_t_slot * key_cache_stride_t
|
| 1087 |
+
+ d_pe_offs * key_cache_stride_d
|
| 1088 |
+
+ pid_b * key_cache_stride_b
|
| 1089 |
+
+ pid_hk * key_cache_stride_h
|
| 1090 |
+
)
|
| 1091 |
+
else:
|
| 1092 |
+
k_pe = tl.reshape(k_pe, (BLOCK_D_pe // X_SIZE, X_SIZE))
|
| 1093 |
+
dx_offs = tl.arange(0, BLOCK_D_pe // X_SIZE).to(tl.int64)
|
| 1094 |
+
x_offs = tl.arange(0, X_SIZE).to(tl.int64)
|
| 1095 |
+
k_out_ptrs = (
|
| 1096 |
+
key_cache_ptr
|
| 1097 |
+
+ pid_t_slot * key_cache_stride_t
|
| 1098 |
+
+ pid_hk * key_cache_stride_h
|
| 1099 |
+
+ dx_offs[:, None] * key_cache_stride_d
|
| 1100 |
+
+ pid_b * key_cache_stride_b
|
| 1101 |
+
+ x_offs[None, :] * key_cache_stride_x
|
| 1102 |
+
)
|
| 1103 |
+
tl.store(k_out_ptrs, k_pe.to(key_cache_ptr.dtype.element_ty))
|
| 1104 |
+
|
| 1105 |
+
v_ptrs = (
|
| 1106 |
+
v_ptr
|
| 1107 |
+
+ pid_t * v_stride_t
|
| 1108 |
+
+ pid_hk * v_stride_h
|
| 1109 |
+
+ d_pe_offs * v_stride_d
|
| 1110 |
+
)
|
| 1111 |
+
if HAVE_V_SCALE:
|
| 1112 |
+
v_scale = tl.load(v_scale_ptr)
|
| 1113 |
+
else:
|
| 1114 |
+
v_scale = 1
|
| 1115 |
+
v_scale_rcprl = 1 / v_scale
|
| 1116 |
+
v = tl.load(v_ptrs) * v_scale_rcprl
|
| 1117 |
+
v_out_ptrs = (
|
| 1118 |
+
value_cache_ptr
|
| 1119 |
+
+ pid_t_slot * value_cache_stride_t
|
| 1120 |
+
+ pid_hk * value_cache_stride_h
|
| 1121 |
+
+ d_pe_offs * value_cache_stride_d
|
| 1122 |
+
+ pid_b * value_cache_stride_b
|
| 1123 |
+
)
|
| 1124 |
+
tl.store(v_out_ptrs, v.to(value_cache_ptr.dtype.element_ty))
|
build/torch-rocm/_triton_kernels/fusions/fused_mul_add.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@triton.jit
|
| 6 |
+
def _fused_mul_add_kernel(
|
| 7 |
+
x_ptr,
|
| 8 |
+
a_ptr,
|
| 9 |
+
b_ptr,
|
| 10 |
+
out_ptr,
|
| 11 |
+
N,
|
| 12 |
+
BLOCK_SIZE_N: tl.constexpr,
|
| 13 |
+
NEED_MASK: tl.constexpr,
|
| 14 |
+
IS_A_SCALAR: tl.constexpr,
|
| 15 |
+
IS_B_SCALAR: tl.constexpr,
|
| 16 |
+
IS_A_TENSOR: tl.constexpr,
|
| 17 |
+
IS_B_TENSOR: tl.constexpr,
|
| 18 |
+
):
|
| 19 |
+
pid = tl.program_id(0)
|
| 20 |
+
|
| 21 |
+
x_offs = pid * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
|
| 22 |
+
|
| 23 |
+
x_mask = None
|
| 24 |
+
if NEED_MASK:
|
| 25 |
+
x_mask = x_offs < N
|
| 26 |
+
|
| 27 |
+
x = tl.load(x_ptr + x_offs, mask=x_mask).to(tl.float32)
|
| 28 |
+
|
| 29 |
+
if IS_A_SCALAR and IS_A_TENSOR:
|
| 30 |
+
a = tl.load(a_ptr)
|
| 31 |
+
elif IS_A_SCALAR:
|
| 32 |
+
a = a_ptr
|
| 33 |
+
else:
|
| 34 |
+
a = tl.load(a_ptr + x_offs, mask=x_mask)
|
| 35 |
+
a = a.to(tl.float32)
|
| 36 |
+
|
| 37 |
+
if IS_B_SCALAR and IS_B_TENSOR:
|
| 38 |
+
b = tl.load(b_ptr)
|
| 39 |
+
elif IS_B_SCALAR:
|
| 40 |
+
b = b_ptr
|
| 41 |
+
else:
|
| 42 |
+
b = tl.load(b_ptr + x_offs, mask=x_mask)
|
| 43 |
+
b = b.to(tl.float32)
|
| 44 |
+
|
| 45 |
+
out = a * x + b
|
| 46 |
+
out = out.to(out_ptr.dtype.element_ty)
|
| 47 |
+
out = tl.store(out_ptr + x_offs, out, mask=x_mask)
|
build/torch-rocm/_triton_kernels/fusions/fused_qk_concat.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
from ..._triton_kernels.rope.rope import (
|
| 4 |
+
_get_gptj_rotated_x_1D,
|
| 5 |
+
_get_neox_rotated_x_1D,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@triton.jit
|
| 10 |
+
def _unit_cat(
|
| 11 |
+
x1_ptr,
|
| 12 |
+
x2_ptr,
|
| 13 |
+
x_out_ptr,
|
| 14 |
+
b,
|
| 15 |
+
h,
|
| 16 |
+
d1_offs,
|
| 17 |
+
d2_offs,
|
| 18 |
+
x1_stride_b,
|
| 19 |
+
x1_stride_h,
|
| 20 |
+
x1_stride_d,
|
| 21 |
+
x2_stride_b,
|
| 22 |
+
x2_stride_h,
|
| 23 |
+
x2_stride_d,
|
| 24 |
+
x_out_stride_b,
|
| 25 |
+
x_out_stride_h,
|
| 26 |
+
x_out_stride_d,
|
| 27 |
+
BLOCK_D1: tl.constexpr,
|
| 28 |
+
):
|
| 29 |
+
x1_offs = b * x1_stride_b + h * x1_stride_h + d1_offs * x1_stride_d
|
| 30 |
+
x2_offs = b * x2_stride_b + h * x2_stride_h + d2_offs * x2_stride_d
|
| 31 |
+
x_out_offs = b * x_out_stride_b + h * x_out_stride_h
|
| 32 |
+
|
| 33 |
+
x1 = tl.load(x1_ptr + x1_offs)
|
| 34 |
+
x2 = tl.load(x2_ptr + x2_offs)
|
| 35 |
+
|
| 36 |
+
tl.store(x_out_ptr + x_out_offs + d1_offs * x_out_stride_d, x1)
|
| 37 |
+
tl.store(x_out_ptr + x_out_offs + (d2_offs + BLOCK_D1) * x_out_stride_d, x2)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@triton.jit
|
| 41 |
+
def _qk_cat_kernel(
|
| 42 |
+
q1_ptr,
|
| 43 |
+
q2_ptr,
|
| 44 |
+
k1_ptr,
|
| 45 |
+
k2_ptr,
|
| 46 |
+
q_out_ptr,
|
| 47 |
+
k_out_ptr,
|
| 48 |
+
q1_stride_b,
|
| 49 |
+
q1_stride_h,
|
| 50 |
+
q1_stride_d,
|
| 51 |
+
q2_stride_b,
|
| 52 |
+
q2_stride_h,
|
| 53 |
+
q2_stride_d,
|
| 54 |
+
k1_stride_b,
|
| 55 |
+
k1_stride_h,
|
| 56 |
+
k1_stride_d,
|
| 57 |
+
k2_stride_b,
|
| 58 |
+
k2_stride_h,
|
| 59 |
+
k2_stride_d,
|
| 60 |
+
q_out_stride_b,
|
| 61 |
+
q_out_stride_h,
|
| 62 |
+
q_out_stride_d,
|
| 63 |
+
k_out_stride_b,
|
| 64 |
+
k_out_stride_h,
|
| 65 |
+
k_out_stride_d,
|
| 66 |
+
QH_PER_KH: tl.constexpr,
|
| 67 |
+
BLOCK_D1: tl.constexpr,
|
| 68 |
+
BLOCK_D2: tl.constexpr,
|
| 69 |
+
):
|
| 70 |
+
pid_b = tl.program_id(0)
|
| 71 |
+
pid_hq = tl.program_id(1)
|
| 72 |
+
|
| 73 |
+
d1_offs = tl.arange(0, BLOCK_D1)
|
| 74 |
+
d2_offs = tl.arange(0, BLOCK_D2)
|
| 75 |
+
|
| 76 |
+
_unit_cat(
|
| 77 |
+
q1_ptr,
|
| 78 |
+
q2_ptr,
|
| 79 |
+
q_out_ptr,
|
| 80 |
+
pid_b,
|
| 81 |
+
pid_hq,
|
| 82 |
+
d1_offs,
|
| 83 |
+
d2_offs,
|
| 84 |
+
q1_stride_b,
|
| 85 |
+
q1_stride_h,
|
| 86 |
+
q1_stride_d,
|
| 87 |
+
q2_stride_b,
|
| 88 |
+
q2_stride_h,
|
| 89 |
+
q2_stride_d,
|
| 90 |
+
q_out_stride_b,
|
| 91 |
+
q_out_stride_h,
|
| 92 |
+
q_out_stride_d,
|
| 93 |
+
BLOCK_D1,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
if pid_hq % QH_PER_KH == 0:
|
| 97 |
+
_unit_cat(
|
| 98 |
+
k1_ptr,
|
| 99 |
+
k2_ptr,
|
| 100 |
+
k_out_ptr,
|
| 101 |
+
pid_b,
|
| 102 |
+
pid_hq // QH_PER_KH,
|
| 103 |
+
d1_offs,
|
| 104 |
+
d2_offs,
|
| 105 |
+
k1_stride_b,
|
| 106 |
+
k1_stride_h,
|
| 107 |
+
k1_stride_d,
|
| 108 |
+
k2_stride_b,
|
| 109 |
+
k2_stride_h,
|
| 110 |
+
k2_stride_d,
|
| 111 |
+
k_out_stride_b,
|
| 112 |
+
k_out_stride_h,
|
| 113 |
+
k_out_stride_d,
|
| 114 |
+
BLOCK_D1,
|
| 115 |
+
)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
@triton.jit
|
| 119 |
+
def _unit_rope_cat(
|
| 120 |
+
x_nope_ptr,
|
| 121 |
+
x_pe_ptr,
|
| 122 |
+
cos,
|
| 123 |
+
sin,
|
| 124 |
+
x_out_ptr,
|
| 125 |
+
b,
|
| 126 |
+
h,
|
| 127 |
+
d_nope_offs,
|
| 128 |
+
d_pe_offs,
|
| 129 |
+
x_nope_stride_b,
|
| 130 |
+
x_nope_stride_h,
|
| 131 |
+
x_nope_stride_d,
|
| 132 |
+
x_pe_stride_b,
|
| 133 |
+
x_pe_stride_h,
|
| 134 |
+
x_pe_stride_d,
|
| 135 |
+
x_out_stride_b,
|
| 136 |
+
x_out_stride_h,
|
| 137 |
+
x_out_stride_d,
|
| 138 |
+
IS_NEOX: tl.constexpr,
|
| 139 |
+
BLOCK_D_nope: tl.constexpr,
|
| 140 |
+
BLOCK_D_pe: tl.constexpr,
|
| 141 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 142 |
+
):
|
| 143 |
+
x_nope_offs = (
|
| 144 |
+
b * x_nope_stride_b + h * x_nope_stride_h + d_nope_offs * x_nope_stride_d
|
| 145 |
+
)
|
| 146 |
+
x_pe_offs = b * x_pe_stride_b + h * x_pe_stride_h + d_pe_offs * x_pe_stride_d
|
| 147 |
+
x_out_offs = b * x_out_stride_b + h * x_out_stride_h
|
| 148 |
+
|
| 149 |
+
x_nope = tl.load(x_nope_ptr + x_nope_offs)
|
| 150 |
+
x_pe = tl.load(x_pe_ptr + x_pe_offs)
|
| 151 |
+
|
| 152 |
+
if IS_NEOX:
|
| 153 |
+
x_rotated_mask = d_pe_offs < BLOCK_D_HALF_pe
|
| 154 |
+
x_pe_rotated = _get_neox_rotated_x_1D(
|
| 155 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 156 |
+
)
|
| 157 |
+
else:
|
| 158 |
+
x_rotated_mask = d_pe_offs % 2 == 0
|
| 159 |
+
x_pe_rotated = _get_gptj_rotated_x_1D(
|
| 160 |
+
x_pe, x_rotated_mask, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
x_pe = x_pe * cos + x_pe_rotated * sin
|
| 164 |
+
x_pe = x_pe.to(x_pe_ptr.dtype.element_ty)
|
| 165 |
+
|
| 166 |
+
tl.store(x_out_ptr + x_out_offs + d_nope_offs * x_out_stride_d, x_nope)
|
| 167 |
+
tl.store(x_out_ptr + x_out_offs + (d_pe_offs + BLOCK_D_nope) * x_out_stride_d, x_pe)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
@triton.jit
|
| 171 |
+
def _qk_rope_cat_kernel(
|
| 172 |
+
q_nope_ptr,
|
| 173 |
+
q_pe_ptr,
|
| 174 |
+
k_nope_ptr,
|
| 175 |
+
k_pe_ptr,
|
| 176 |
+
pos_ptr,
|
| 177 |
+
cos_ptr,
|
| 178 |
+
sin_ptr,
|
| 179 |
+
q_out_ptr,
|
| 180 |
+
k_out_ptr,
|
| 181 |
+
q_nope_stride_b,
|
| 182 |
+
q_nope_stride_h,
|
| 183 |
+
q_nope_stride_d,
|
| 184 |
+
q_pe_stride_b,
|
| 185 |
+
q_pe_stride_h,
|
| 186 |
+
q_pe_stride_d,
|
| 187 |
+
k_nope_stride_b,
|
| 188 |
+
k_nope_stride_h,
|
| 189 |
+
k_nope_stride_d,
|
| 190 |
+
k_pe_stride_b,
|
| 191 |
+
k_pe_stride_h,
|
| 192 |
+
k_pe_stride_d,
|
| 193 |
+
pos_stride_b,
|
| 194 |
+
cos_stride_b,
|
| 195 |
+
cos_stride_d,
|
| 196 |
+
q_out_stride_b,
|
| 197 |
+
q_out_stride_h,
|
| 198 |
+
q_out_stride_d,
|
| 199 |
+
k_out_stride_b,
|
| 200 |
+
k_out_stride_h,
|
| 201 |
+
k_out_stride_d,
|
| 202 |
+
QH_PER_KH: tl.constexpr,
|
| 203 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 204 |
+
IS_NEOX: tl.constexpr,
|
| 205 |
+
BLOCK_D_nope: tl.constexpr,
|
| 206 |
+
BLOCK_D_pe: tl.constexpr,
|
| 207 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 208 |
+
):
|
| 209 |
+
pid_b = tl.program_id(0)
|
| 210 |
+
pid_hq = tl.program_id(1)
|
| 211 |
+
|
| 212 |
+
d_nope_offs = tl.arange(0, BLOCK_D_nope)
|
| 213 |
+
d_pe_offs = tl.arange(0, BLOCK_D_pe)
|
| 214 |
+
|
| 215 |
+
if REUSE_FREQS_FRONT_PART:
|
| 216 |
+
if IS_NEOX:
|
| 217 |
+
d_cos_offs = d_pe_offs
|
| 218 |
+
d_cos_offs = tl.where(
|
| 219 |
+
(d_cos_offs >= BLOCK_D_HALF_pe) & (d_cos_offs < BLOCK_D_pe),
|
| 220 |
+
d_cos_offs - BLOCK_D_HALF_pe,
|
| 221 |
+
d_cos_offs,
|
| 222 |
+
).to(d_cos_offs.dtype)
|
| 223 |
+
# d_cos_mask = d_cos_offs < BLOCK_D_pe
|
| 224 |
+
else:
|
| 225 |
+
d_cos_offs = d_pe_offs // 2
|
| 226 |
+
# d_cos_mask = d_cos_offs < BLOCK_D_HALF_pe
|
| 227 |
+
else:
|
| 228 |
+
d_cos_offs = d_pe_offs
|
| 229 |
+
# d_cos_mask = d_cos_offs < BLOCK_D_pe
|
| 230 |
+
|
| 231 |
+
pos = tl.load(pos_ptr + pid_b * pos_stride_b)
|
| 232 |
+
cos_offs = pos * cos_stride_b + d_cos_offs * cos_stride_d
|
| 233 |
+
cos = tl.load(cos_ptr + cos_offs)
|
| 234 |
+
sin = tl.load(sin_ptr + cos_offs)
|
| 235 |
+
|
| 236 |
+
_unit_rope_cat(
|
| 237 |
+
q_nope_ptr,
|
| 238 |
+
q_pe_ptr,
|
| 239 |
+
cos,
|
| 240 |
+
sin,
|
| 241 |
+
q_out_ptr,
|
| 242 |
+
pid_b,
|
| 243 |
+
pid_hq,
|
| 244 |
+
d_nope_offs,
|
| 245 |
+
d_pe_offs,
|
| 246 |
+
q_nope_stride_b,
|
| 247 |
+
q_nope_stride_h,
|
| 248 |
+
q_nope_stride_d,
|
| 249 |
+
q_pe_stride_b,
|
| 250 |
+
q_pe_stride_h,
|
| 251 |
+
q_pe_stride_d,
|
| 252 |
+
q_out_stride_b,
|
| 253 |
+
q_out_stride_h,
|
| 254 |
+
q_out_stride_d,
|
| 255 |
+
IS_NEOX,
|
| 256 |
+
BLOCK_D_nope,
|
| 257 |
+
BLOCK_D_pe,
|
| 258 |
+
BLOCK_D_HALF_pe,
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
if pid_hq % QH_PER_KH == 0:
|
| 262 |
+
_unit_rope_cat(
|
| 263 |
+
k_nope_ptr,
|
| 264 |
+
k_pe_ptr,
|
| 265 |
+
cos,
|
| 266 |
+
sin,
|
| 267 |
+
k_out_ptr,
|
| 268 |
+
pid_b,
|
| 269 |
+
pid_hq // QH_PER_KH,
|
| 270 |
+
d_nope_offs,
|
| 271 |
+
d_pe_offs,
|
| 272 |
+
k_nope_stride_b,
|
| 273 |
+
k_nope_stride_h,
|
| 274 |
+
k_nope_stride_d,
|
| 275 |
+
k_pe_stride_b,
|
| 276 |
+
k_pe_stride_h,
|
| 277 |
+
k_pe_stride_d,
|
| 278 |
+
k_out_stride_b,
|
| 279 |
+
k_out_stride_h,
|
| 280 |
+
k_out_stride_d,
|
| 281 |
+
IS_NEOX,
|
| 282 |
+
BLOCK_D_nope,
|
| 283 |
+
BLOCK_D_pe,
|
| 284 |
+
BLOCK_D_HALF_pe,
|
| 285 |
+
)
|
build/torch-rocm/_triton_kernels/fusions/fused_reduce_qk_norm_rope_swa_write.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
"""Fused split-K reduce + per-head weighted RMSNorm + RoPE (tail) on Q,
|
| 5 |
+
per-row weighted RMSNorm + RoPE (tail) on KV (+ optional SWA KV write).
|
| 6 |
+
|
| 7 |
+
Grid: ``(cdiv(M, BLOCK_SIZE_M), num_local_heads + 1)``. Each program tile
|
| 8 |
+
handles ``BLOCK_SIZE_M`` tokens. Programs with ``pid_h < num_local_heads``
|
| 9 |
+
load a query head tile ``[NUM_SPLITK, BLOCK_SIZE_M, HEAD_DIM]`` from
|
| 10 |
+
``q_in``, reduce over the split-K axis, apply per-head weighted batched
|
| 11 |
+
RMSNorm, store the (pre-RoPE) head into ``q_out``, then call the batched
|
| 12 |
+
RoPE on the last ``rope_head_dim`` elements. Programs with
|
| 13 |
+
``pid_h == num_local_heads`` load the full ``[BLOCK_SIZE_M, HEAD_DIM]``
|
| 14 |
+
kv tile, apply weighted batched RMSNorm over ``head_dim``, store the
|
| 15 |
+
normed nope part back into ``kv``, then extract the tail with the same
|
| 16 |
+
reshape+sum trick used for q, apply RoPE, write the result to the kv
|
| 17 |
+
tail, and optionally scatter both parts into ``swa_kv``.
|
| 18 |
+
|
| 19 |
+
``q_in`` layout (driven by API helper):
|
| 20 |
+
- 2D: ``[M, N]`` — ``q_in_splitk_stride`` = 0, ``NUM_SPLITK`` = 1.
|
| 21 |
+
- 3D: ``[num_splitk, M, N]`` — ``q_in_splitk_stride`` = ``q_in.stride(0)``,
|
| 22 |
+
``NUM_SPLITK`` = ``num_splitk``.
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
import triton
|
| 26 |
+
import triton.language as tl
|
| 27 |
+
|
| 28 |
+
from ...utils._triton.kernel_repr import make_kernel_repr
|
| 29 |
+
from ...rope.rope import _get_neox_rotated_x, _get_gptj_rotated_x
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@triton.jit
|
| 33 |
+
def _batched_rmsnorm_op(row, weight, n_cols, epsilon):
|
| 34 |
+
"""Per-row RMSNorm over the last axis of a [BLOCK_M, N] tile (row in fp32)."""
|
| 35 |
+
row_norm = row * row
|
| 36 |
+
row_norm = tl.sum(row_norm, axis=-1)
|
| 37 |
+
norm_factor = tl.math.rsqrt((row_norm / n_cols) + epsilon)
|
| 38 |
+
if weight is not None:
|
| 39 |
+
rms_norm = row * norm_factor[:, None] * weight[None, :]
|
| 40 |
+
else:
|
| 41 |
+
rms_norm = row * norm_factor[:, None]
|
| 42 |
+
return rms_norm
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@triton.jit
|
| 46 |
+
def _batched_unit_rope(
|
| 47 |
+
x_pe,
|
| 48 |
+
cos,
|
| 49 |
+
sin,
|
| 50 |
+
d_pe_offs,
|
| 51 |
+
IS_NEOX: tl.constexpr,
|
| 52 |
+
BLOCK_M: tl.constexpr,
|
| 53 |
+
BLOCK_D_pe: tl.constexpr,
|
| 54 |
+
BLOCK_D_HALF_pe: tl.constexpr,
|
| 55 |
+
):
|
| 56 |
+
"""RoPE on a [BLOCK_M, BLOCK_D_pe] tile; cos/sin are [BLOCK_M, BLOCK_D_pe]."""
|
| 57 |
+
if IS_NEOX:
|
| 58 |
+
x_rotated_mask = (d_pe_offs < BLOCK_D_HALF_pe)[None, :]
|
| 59 |
+
x_pe_rotated = _get_neox_rotated_x(
|
| 60 |
+
x_pe, x_rotated_mask, BLOCK_M, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 61 |
+
)
|
| 62 |
+
else:
|
| 63 |
+
x_rotated_mask = (d_pe_offs % 2 == 0)[None, :]
|
| 64 |
+
x_pe_rotated = _get_gptj_rotated_x(
|
| 65 |
+
x_pe, x_rotated_mask, BLOCK_M, BLOCK_D_pe, BLOCK_D_HALF_pe
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
return x_pe * cos + x_pe_rotated * sin
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
_fused_reduce_qk_norm_rope_swa_write_repr = make_kernel_repr(
|
| 72 |
+
"_fused_reduce_qk_norm_rope_swa_write_kernel",
|
| 73 |
+
[
|
| 74 |
+
"BLOCK_SIZE_M",
|
| 75 |
+
"HEAD_DIM",
|
| 76 |
+
"ROPE_DIM",
|
| 77 |
+
"NUM_LOCAL_HEADS",
|
| 78 |
+
"NUM_SPLITK",
|
| 79 |
+
"HAS_SWA",
|
| 80 |
+
"IS_NEOX",
|
| 81 |
+
"REUSE_FREQS_FRONT_PART",
|
| 82 |
+
],
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
@triton.jit(repr=_fused_reduce_qk_norm_rope_swa_write_repr)
|
| 87 |
+
def _fused_reduce_qk_norm_rope_swa_write_kernel(
|
| 88 |
+
q_in_ptr,
|
| 89 |
+
q_out_ptr,
|
| 90 |
+
kv_ptr,
|
| 91 |
+
q_norm_weight_ptr,
|
| 92 |
+
kv_norm_weight_ptr,
|
| 93 |
+
positions_ptr,
|
| 94 |
+
cos_ptr,
|
| 95 |
+
sin_ptr,
|
| 96 |
+
swa_write_active_ptr,
|
| 97 |
+
batch_id_per_token_ptr,
|
| 98 |
+
state_slot_per_seq_ptr,
|
| 99 |
+
swa_kv_ptr,
|
| 100 |
+
M,
|
| 101 |
+
q_in_splitk_stride,
|
| 102 |
+
q_in_m_stride,
|
| 103 |
+
q_in_d_stride,
|
| 104 |
+
stride_qm,
|
| 105 |
+
stride_qh,
|
| 106 |
+
stride_qd,
|
| 107 |
+
stride_kv_m,
|
| 108 |
+
stride_kv_d,
|
| 109 |
+
cos_stride_t,
|
| 110 |
+
cos_stride_d,
|
| 111 |
+
swa_kv_slot_stride,
|
| 112 |
+
swa_kv_pos_stride,
|
| 113 |
+
win,
|
| 114 |
+
q_eps,
|
| 115 |
+
kv_eps,
|
| 116 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 117 |
+
HEAD_DIM: tl.constexpr,
|
| 118 |
+
ROPE_DIM: tl.constexpr,
|
| 119 |
+
NUM_LOCAL_HEADS: tl.constexpr,
|
| 120 |
+
NUM_SPLITK: tl.constexpr,
|
| 121 |
+
HAS_SWA: tl.constexpr,
|
| 122 |
+
IS_NEOX: tl.constexpr,
|
| 123 |
+
REUSE_FREQS_FRONT_PART: tl.constexpr,
|
| 124 |
+
):
|
| 125 |
+
pid_m = tl.program_id(0).to(tl.int64)
|
| 126 |
+
pid_h = tl.program_id(1).to(tl.int64)
|
| 127 |
+
NOPE_DIM: tl.constexpr = HEAD_DIM - ROPE_DIM
|
| 128 |
+
NUM_PE_CHUNKS: tl.constexpr = HEAD_DIM // ROPE_DIM
|
| 129 |
+
|
| 130 |
+
m_offs = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M).to(tl.int64)
|
| 131 |
+
m_mask = m_offs < M
|
| 132 |
+
|
| 133 |
+
offs_d_full = tl.arange(0, HEAD_DIM)
|
| 134 |
+
nope_d_mask = offs_d_full < NOPE_DIM
|
| 135 |
+
|
| 136 |
+
d_pe_offs = tl.arange(0, ROPE_DIM).to(tl.int64)
|
| 137 |
+
if REUSE_FREQS_FRONT_PART:
|
| 138 |
+
if IS_NEOX:
|
| 139 |
+
d_cos_offs = d_pe_offs
|
| 140 |
+
d_cos_offs = tl.where(
|
| 141 |
+
(d_cos_offs >= (ROPE_DIM // 2)) & (d_cos_offs < ROPE_DIM),
|
| 142 |
+
d_cos_offs - (ROPE_DIM // 2),
|
| 143 |
+
d_cos_offs,
|
| 144 |
+
).to(d_cos_offs.dtype)
|
| 145 |
+
else:
|
| 146 |
+
d_cos_offs = d_pe_offs // 2
|
| 147 |
+
else:
|
| 148 |
+
d_cos_offs = d_pe_offs
|
| 149 |
+
|
| 150 |
+
if pid_h < NUM_LOCAL_HEADS:
|
| 151 |
+
head_id = pid_h.to(tl.int32)
|
| 152 |
+
offs_n = head_id * HEAD_DIM + offs_d_full
|
| 153 |
+
|
| 154 |
+
splitk_offs = tl.arange(0, NUM_SPLITK).to(tl.int64)
|
| 155 |
+
q_ptrs = (
|
| 156 |
+
q_in_ptr
|
| 157 |
+
+ splitk_offs[:, None, None] * q_in_splitk_stride
|
| 158 |
+
+ m_offs[None, :, None] * q_in_m_stride
|
| 159 |
+
+ offs_n[None, None, :] * q_in_d_stride
|
| 160 |
+
)
|
| 161 |
+
q_tile = tl.load(
|
| 162 |
+
q_ptrs,
|
| 163 |
+
mask=m_mask[None, :, None],
|
| 164 |
+
other=0.0,
|
| 165 |
+
).to(
|
| 166 |
+
tl.float32
|
| 167 |
+
) # [NUM_SPLITK, BLOCK_SIZE_M, HEAD_DIM]
|
| 168 |
+
q_acc = tl.sum(q_tile, axis=0) # [BLOCK_SIZE_M, HEAD_DIM]
|
| 169 |
+
|
| 170 |
+
if q_norm_weight_ptr is not None:
|
| 171 |
+
w_q = tl.load(q_norm_weight_ptr + offs_d_full).to(tl.float32)
|
| 172 |
+
else:
|
| 173 |
+
w_q = None
|
| 174 |
+
q_out_normed = _batched_rmsnorm_op(q_acc, w_q, HEAD_DIM, q_eps)
|
| 175 |
+
|
| 176 |
+
q_base_ptrs = q_out_ptr + m_offs[:, None] * stride_qm + pid_h * stride_qh
|
| 177 |
+
tl.store(
|
| 178 |
+
q_base_ptrs + offs_d_full[None, :] * stride_qd,
|
| 179 |
+
q_out_normed.to(q_out_ptr.dtype.element_ty),
|
| 180 |
+
mask=m_mask[:, None] & nope_d_mask[None, :],
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
# Slice the trailing ROPE_DIM elements: only the last chunk is nonzero.
|
| 184 |
+
q_pe = tl.where(
|
| 185 |
+
(offs_d_full >= NOPE_DIM)[None, :], q_out_normed, 0.0
|
| 186 |
+
) # [BLOCK_SIZE_M, HEAD_DIM]
|
| 187 |
+
q_pe = q_pe.reshape(BLOCK_SIZE_M, NUM_PE_CHUNKS, ROPE_DIM)
|
| 188 |
+
q_pe = tl.sum(q_pe, axis=1) # [BLOCK_SIZE_M, ROPE_DIM]
|
| 189 |
+
|
| 190 |
+
pos = tl.load(positions_ptr + m_offs, mask=m_mask, other=0) # [BLOCK_SIZE_M]
|
| 191 |
+
cos_offs = pos[:, None] * cos_stride_t + d_cos_offs[None, :] * cos_stride_d
|
| 192 |
+
cos = tl.load(cos_ptr + cos_offs, mask=m_mask[:, None], other=0)
|
| 193 |
+
sin = tl.load(sin_ptr + cos_offs, mask=m_mask[:, None], other=0)
|
| 194 |
+
|
| 195 |
+
q_pe_ptrs = q_base_ptrs + (NOPE_DIM + d_pe_offs[None, :]) * stride_qd
|
| 196 |
+
q_pe = _batched_unit_rope(
|
| 197 |
+
q_pe,
|
| 198 |
+
cos,
|
| 199 |
+
sin,
|
| 200 |
+
d_pe_offs,
|
| 201 |
+
IS_NEOX,
|
| 202 |
+
BLOCK_SIZE_M,
|
| 203 |
+
ROPE_DIM,
|
| 204 |
+
ROPE_DIM // 2,
|
| 205 |
+
)
|
| 206 |
+
tl.store(
|
| 207 |
+
q_pe_ptrs,
|
| 208 |
+
q_pe.to(q_out_ptr.dtype.element_ty),
|
| 209 |
+
mask=m_mask[:, None],
|
| 210 |
+
)
|
| 211 |
+
return
|
| 212 |
+
|
| 213 |
+
if HAS_SWA:
|
| 214 |
+
src_id = tl.load(
|
| 215 |
+
swa_write_active_ptr + m_offs, mask=m_mask, other=-1
|
| 216 |
+
) # [BLOCK_SIZE_M]
|
| 217 |
+
else:
|
| 218 |
+
src_id = m_offs.to(tl.int32)
|
| 219 |
+
src_mask = m_mask & (src_id >= 0)
|
| 220 |
+
|
| 221 |
+
pos = tl.load(positions_ptr + src_id, mask=src_mask, other=0)
|
| 222 |
+
cos_offs = pos[:, None] * cos_stride_t + d_cos_offs[None, :] * cos_stride_d
|
| 223 |
+
cos = tl.load(cos_ptr + cos_offs, mask=src_mask[:, None], other=0)
|
| 224 |
+
sin = tl.load(sin_ptr + cos_offs, mask=src_mask[:, None], other=0)
|
| 225 |
+
|
| 226 |
+
kv_base_ptrs = kv_ptr + src_id[:, None].to(tl.int64) * stride_kv_m
|
| 227 |
+
kv_full_ptrs = kv_base_ptrs + offs_d_full[None, :] * stride_kv_d
|
| 228 |
+
kv_pe_ptrs = kv_base_ptrs + (NOPE_DIM + d_pe_offs[None, :]) * stride_kv_d
|
| 229 |
+
|
| 230 |
+
# Load the entire kv row (nope + pe) so we can RMSNorm over head_dim.
|
| 231 |
+
kv_full = tl.load(kv_full_ptrs, mask=src_mask[:, None], other=0.0).to(tl.float32)
|
| 232 |
+
|
| 233 |
+
if kv_norm_weight_ptr is not None:
|
| 234 |
+
w_kv = tl.load(kv_norm_weight_ptr + offs_d_full).to(tl.float32)
|
| 235 |
+
else:
|
| 236 |
+
w_kv = None
|
| 237 |
+
kv_normed = _batched_rmsnorm_op(
|
| 238 |
+
kv_full, w_kv, HEAD_DIM, kv_eps
|
| 239 |
+
) # [BLOCK_SIZE_M, HEAD_DIM]
|
| 240 |
+
|
| 241 |
+
# Store the normed nope portion back into kv.
|
| 242 |
+
tl.store(
|
| 243 |
+
kv_full_ptrs,
|
| 244 |
+
kv_normed.to(kv_ptr.dtype.element_ty),
|
| 245 |
+
mask=src_mask[:, None] & nope_d_mask[None, :],
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
# Extract pe via the same reshape+sum trick used for q.
|
| 249 |
+
kv_pe = tl.where(
|
| 250 |
+
(offs_d_full >= NOPE_DIM)[None, :], kv_normed, 0.0
|
| 251 |
+
) # [BLOCK_SIZE_M, HEAD_DIM]
|
| 252 |
+
kv_pe = kv_pe.reshape(BLOCK_SIZE_M, NUM_PE_CHUNKS, ROPE_DIM)
|
| 253 |
+
kv_pe = tl.sum(kv_pe, axis=1) # [BLOCK_SIZE_M, ROPE_DIM]
|
| 254 |
+
|
| 255 |
+
kv_pe = _batched_unit_rope(
|
| 256 |
+
kv_pe,
|
| 257 |
+
cos,
|
| 258 |
+
sin,
|
| 259 |
+
d_pe_offs,
|
| 260 |
+
IS_NEOX,
|
| 261 |
+
BLOCK_SIZE_M,
|
| 262 |
+
ROPE_DIM,
|
| 263 |
+
ROPE_DIM // 2,
|
| 264 |
+
)
|
| 265 |
+
tl.store(
|
| 266 |
+
kv_pe_ptrs,
|
| 267 |
+
kv_pe.to(kv_ptr.dtype.element_ty),
|
| 268 |
+
mask=src_mask[:, None],
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
if HAS_SWA:
|
| 272 |
+
bid = tl.load(batch_id_per_token_ptr + src_id, mask=src_mask, other=0)
|
| 273 |
+
slot = tl.load(state_slot_per_seq_ptr + bid, mask=src_mask, other=0)
|
| 274 |
+
ring_idx = pos % win
|
| 275 |
+
swa_kv_ptrs = (
|
| 276 |
+
swa_kv_ptr
|
| 277 |
+
+ slot[:, None].to(tl.int64) * swa_kv_slot_stride
|
| 278 |
+
+ ring_idx[:, None].to(tl.int64) * swa_kv_pos_stride
|
| 279 |
+
)
|
| 280 |
+
tl.store(
|
| 281 |
+
swa_kv_ptrs + offs_d_full[None, :],
|
| 282 |
+
kv_normed.to(swa_kv_ptr.dtype.element_ty),
|
| 283 |
+
mask=src_mask[:, None] & nope_d_mask[None, :],
|
| 284 |
+
)
|
| 285 |
+
tl.store(
|
| 286 |
+
swa_kv_ptrs + NOPE_DIM + d_pe_offs[None, :],
|
| 287 |
+
kv_pe.to(swa_kv_ptr.dtype.element_ty),
|
| 288 |
+
mask=src_mask[:, None],
|
| 289 |
+
)
|
build/torch-rocm/_triton_kernels/fusions/fused_routing_from_topk.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2025-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
# Triton kernels that convert FusedMoE topk outputs (topk_weights, topk_ids)
|
| 5 |
+
# into the (gather_indx, scatter_indx, gate_scal, hist) routing data consumed
|
| 6 |
+
# by triton_kernels.matmul_ogs. Three single-CTA kernels implement a counting
|
| 7 |
+
# sort over (token, slot) pairs by their expert id; replaces ~12 small torch
|
| 8 |
+
# ops (per-row sort, gather, two stable argsorts, advanced indexing, fp32
|
| 9 |
+
# histc, plus dtype casts) with three kernel launches.
|
| 10 |
+
import triton
|
| 11 |
+
import triton.language as tl
|
| 12 |
+
from ...utils._triton.kernel_repr import make_kernel_repr
|
| 13 |
+
|
| 14 |
+
_fused_routing_from_topk_hist_kernel_repr = make_kernel_repr(
|
| 15 |
+
"_fused_routing_from_topk_hist_kernel",
|
| 16 |
+
[
|
| 17 |
+
"E",
|
| 18 |
+
"HAS_EXPERT_MAP",
|
| 19 |
+
"BLOCK_NK",
|
| 20 |
+
"BLOCK_E",
|
| 21 |
+
],
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
_fused_routing_from_topk_offset_kernel_repr = make_kernel_repr(
|
| 25 |
+
"_fused_routing_from_topk_offset_kernel",
|
| 26 |
+
[
|
| 27 |
+
"E",
|
| 28 |
+
"BLOCK_E",
|
| 29 |
+
],
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
_fused_routing_from_topk_place_kernel_repr = make_kernel_repr(
|
| 33 |
+
"_fused_routing_from_topk_place_kernel",
|
| 34 |
+
[
|
| 35 |
+
"HAS_EXPERT_MAP",
|
| 36 |
+
"BLOCK_NK",
|
| 37 |
+
],
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
@triton.jit(repr=_fused_routing_from_topk_hist_kernel_repr)
|
| 42 |
+
def _fused_routing_from_topk_hist_kernel(
|
| 43 |
+
# inputs
|
| 44 |
+
topk_ids_ptr, # [NK] int32 — flattened topk_ids
|
| 45 |
+
expert_map_ptr, # [N_EXPERTS_GLOBAL] int32 or identity map fallback
|
| 46 |
+
expert_map_numel, # runtime int — bounds for expert_map_ptr
|
| 47 |
+
# outputs
|
| 48 |
+
hist_ptr, # [E] int32 — tokens-per-expert histogram
|
| 49 |
+
# shapes
|
| 50 |
+
NK, # runtime int — actual valid item count (≤ BLOCK_NK)
|
| 51 |
+
E: tl.constexpr,
|
| 52 |
+
HAS_EXPERT_MAP: tl.constexpr,
|
| 53 |
+
BLOCK_NK: tl.constexpr, # padded to next pow2 of NK
|
| 54 |
+
BLOCK_E: tl.constexpr, # padded to next pow2 of E (tl.histogram needs pow2)
|
| 55 |
+
):
|
| 56 |
+
"""Phase A: histogram via tl.histogram (warp-local shared-memory reduction).
|
| 57 |
+
|
| 58 |
+
No global atomics, no debug barrier — the reduction completes within a
|
| 59 |
+
single wave/CTA and the result is written with a plain tl.store.
|
| 60 |
+
``tl.histogram`` requires ``num_bins`` to be a power of two, so the
|
| 61 |
+
reduction is over BLOCK_E bins; bins ``>= E`` are unreachable because
|
| 62 |
+
expert ids are in ``[0, E)`` and the trailing entries are dropped via
|
| 63 |
+
a masked store.
|
| 64 |
+
"""
|
| 65 |
+
item_offs = tl.arange(0, BLOCK_NK)
|
| 66 |
+
item_mask = item_offs < NK
|
| 67 |
+
# Clamp the offset for masked-out lanes to 0 so the pointer arithmetic
|
| 68 |
+
# below stays within the allocated buffers.
|
| 69 |
+
safe_item = tl.where(item_mask, item_offs, 0)
|
| 70 |
+
global_expt = tl.load(topk_ids_ptr + safe_item, mask=item_mask, other=0).to(
|
| 71 |
+
tl.int32
|
| 72 |
+
)
|
| 73 |
+
if HAS_EXPERT_MAP:
|
| 74 |
+
map_mask = item_mask & (global_expt >= 0) & (global_expt < expert_map_numel)
|
| 75 |
+
safe_global_expt = tl.where(map_mask, global_expt, 0)
|
| 76 |
+
local_expt = tl.load(
|
| 77 |
+
expert_map_ptr + safe_global_expt, mask=map_mask, other=-1
|
| 78 |
+
).to(tl.int32)
|
| 79 |
+
# Match reference semantics: invalid experts are redirected to bucket 0
|
| 80 |
+
# and later zeroed in gate_scal.
|
| 81 |
+
expt = tl.where(local_expt >= 0, local_expt, 0)
|
| 82 |
+
else:
|
| 83 |
+
expt = global_expt
|
| 84 |
+
|
| 85 |
+
hist = tl.histogram(expt, BLOCK_E, mask=item_mask)
|
| 86 |
+
|
| 87 |
+
e_offs = tl.arange(0, BLOCK_E)
|
| 88 |
+
tl.store(hist_ptr + e_offs, hist, mask=e_offs < E)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@triton.jit(repr=_fused_routing_from_topk_offset_kernel_repr)
|
| 92 |
+
def _fused_routing_from_topk_offset_kernel(
|
| 93 |
+
# inputs
|
| 94 |
+
hist_ptr, # [E] int32 — published by the hist kernel
|
| 95 |
+
# outputs
|
| 96 |
+
offset_ptr, # [E] int32 — exclusive prefix sum of hist
|
| 97 |
+
# shapes
|
| 98 |
+
E: tl.constexpr,
|
| 99 |
+
BLOCK_E: tl.constexpr, # padded to next pow2 of E
|
| 100 |
+
):
|
| 101 |
+
"""Phase B: exclusive prefix-sum hist → offset.
|
| 102 |
+
|
| 103 |
+
The previous kernel's exit publishes hist, so this kernel observes
|
| 104 |
+
them on entry without an explicit fence.
|
| 105 |
+
"""
|
| 106 |
+
e_offs = tl.arange(0, BLOCK_E)
|
| 107 |
+
e_mask = e_offs < E
|
| 108 |
+
safe_e = tl.where(e_mask, e_offs, 0)
|
| 109 |
+
h = tl.load(hist_ptr + safe_e, mask=e_mask, other=0)
|
| 110 |
+
incl = tl.cumsum(h, axis=0)
|
| 111 |
+
excl = incl - h
|
| 112 |
+
tl.store(offset_ptr + safe_e, excl, mask=e_mask)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
@triton.jit(repr=_fused_routing_from_topk_place_kernel_repr)
|
| 116 |
+
def _fused_routing_from_topk_place_kernel(
|
| 117 |
+
# inputs
|
| 118 |
+
topk_ids_ptr, # [NK] int32 — flattened topk_ids
|
| 119 |
+
topk_weights_ptr, # [NK] (any float dtype) — flattened topk_weights
|
| 120 |
+
expert_map_ptr, # [N_EXPERTS_GLOBAL] int32 or identity map fallback
|
| 121 |
+
expert_map_numel, # runtime int — bounds for expert_map_ptr
|
| 122 |
+
offset_ptr, # [E] int32 — exclusive prefix sums from the offset kernel
|
| 123 |
+
# outputs
|
| 124 |
+
topk_indx_ptr, # [NK] int32 — output gather_indx.src_indx
|
| 125 |
+
gate_indx_ptr, # [NK] int32 — output gather_indx.dst_indx
|
| 126 |
+
gate_scal_ptr, # [NK] same dtype as topk_weights
|
| 127 |
+
# shapes
|
| 128 |
+
NK, # runtime int — actual valid item count (≤ BLOCK_NK)
|
| 129 |
+
HAS_EXPERT_MAP: tl.constexpr,
|
| 130 |
+
BLOCK_NK: tl.constexpr, # padded to next pow2 of NK
|
| 131 |
+
):
|
| 132 |
+
"""Phase C: place items.
|
| 133 |
+
|
| 134 |
+
For each valid item, atomic_add on offset[expert] returns its
|
| 135 |
+
expert-sorted position; write topk_indx, gate_indx, gate_scal.
|
| 136 |
+
|
| 137 |
+
The kernel does NOT pre-sort each token's K experts. The resulting
|
| 138 |
+
topk_indx / gate_indx differ from a stable-argsort reference at
|
| 139 |
+
intra-expert ordering, but they form a valid inverse permutation pair
|
| 140 |
+
and matmul_ogs produces the same per-token aggregation (gather +
|
| 141 |
+
weighted scatter sum are both commutative over a per-expert slice).
|
| 142 |
+
"""
|
| 143 |
+
item_offs = tl.arange(0, BLOCK_NK)
|
| 144 |
+
item_mask = item_offs < NK
|
| 145 |
+
safe_item = tl.where(item_mask, item_offs, 0)
|
| 146 |
+
global_expt = tl.load(topk_ids_ptr + safe_item, mask=item_mask, other=0).to(
|
| 147 |
+
tl.int32
|
| 148 |
+
)
|
| 149 |
+
weights = tl.load(topk_weights_ptr + safe_item, mask=item_mask, other=0.0)
|
| 150 |
+
if HAS_EXPERT_MAP:
|
| 151 |
+
map_mask = item_mask & (global_expt >= 0) & (global_expt < expert_map_numel)
|
| 152 |
+
safe_global_expt = tl.where(map_mask, global_expt, 0)
|
| 153 |
+
local_expt = tl.load(
|
| 154 |
+
expert_map_ptr + safe_global_expt, mask=map_mask, other=-1
|
| 155 |
+
).to(tl.int32)
|
| 156 |
+
invalid = local_expt < 0
|
| 157 |
+
expt = tl.where(invalid, 0, local_expt)
|
| 158 |
+
weights = tl.where(invalid, 0.0, weights)
|
| 159 |
+
else:
|
| 160 |
+
expt = global_expt
|
| 161 |
+
|
| 162 |
+
pos = tl.atomic_add(offset_ptr + expt, 1, mask=item_mask)
|
| 163 |
+
|
| 164 |
+
# Clamp pos for masked-out lanes — `pos` is undefined there, and
|
| 165 |
+
# `topk_indx_ptr + pos` / `gate_scal_ptr + pos` would otherwise be
|
| 166 |
+
# arbitrary addresses. The mask=False store doesn't write, but the
|
| 167 |
+
# address calc is still evaluated and may fault on OOB pages.
|
| 168 |
+
safe_pos = tl.where(item_mask, pos, 0)
|
| 169 |
+
|
| 170 |
+
# gate_indx[i] = pos (original_flat → expert_sorted_pos)
|
| 171 |
+
tl.store(gate_indx_ptr + safe_item, pos, mask=item_mask)
|
| 172 |
+
# topk_indx[pos] = i (expert_sorted_pos → original_flat)
|
| 173 |
+
tl.store(topk_indx_ptr + safe_pos, item_offs.to(tl.int32), mask=item_mask)
|
| 174 |
+
# gate_scal[pos] = weight at the original flat item
|
| 175 |
+
tl.store(gate_scal_ptr + safe_pos, weights, mask=item_mask)
|
build/torch-rocm/_triton_kernels/fusions/mhc.py
ADDED
|
@@ -0,0 +1,1277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
"""Triton kernel for mHC (manifold-constrained Hyper Connection) operations."""
|
| 5 |
+
|
| 6 |
+
import triton
|
| 7 |
+
import triton.language as tl
|
| 8 |
+
from ...utils._triton.kernel_repr import make_kernel_repr
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@triton.jit
|
| 12 |
+
def _mhc_apply_pre_mix_tile(
|
| 13 |
+
x_ptr,
|
| 14 |
+
out_ptr,
|
| 15 |
+
pre_mix_2d, # (BLOCK_M, N_POW2) fp32, caller-supplied
|
| 16 |
+
rm,
|
| 17 |
+
rc,
|
| 18 |
+
i_n,
|
| 19 |
+
M,
|
| 20 |
+
C: tl.constexpr,
|
| 21 |
+
n: tl.constexpr,
|
| 22 |
+
stride_xm,
|
| 23 |
+
stride_xk,
|
| 24 |
+
stride_om,
|
| 25 |
+
stride_oc,
|
| 26 |
+
):
|
| 27 |
+
"""Compute one (M-tile, C-tile) of the pre-stream apply step:
|
| 28 |
+
|
| 29 |
+
out[rm, rc] = sum_{i in [0, n)} pre_mix_2d[rm, i] * x[rm, i*C + rc]
|
| 30 |
+
|
| 31 |
+
`pre_mix_2d` must already be padded to width `N_POW2` along the n-axis
|
| 32 |
+
(entries with `i_n >= n` masked to 0 by the caller).
|
| 33 |
+
"""
|
| 34 |
+
x_tile = tl.load(
|
| 35 |
+
x_ptr
|
| 36 |
+
+ rm[:, None, None] * stride_xm
|
| 37 |
+
+ (i_n[None, :, None] * C + rc[None, None, :]) * stride_xk,
|
| 38 |
+
mask=(rm[:, None, None] < M)
|
| 39 |
+
& (i_n[None, :, None] < n)
|
| 40 |
+
& (rc[None, None, :] < C),
|
| 41 |
+
other=0.0,
|
| 42 |
+
).to(tl.float32)
|
| 43 |
+
li_acc = tl.sum(pre_mix_2d[:, :, None] * x_tile, axis=1)
|
| 44 |
+
tl.store(
|
| 45 |
+
out_ptr + rm[:, None] * stride_om + rc[None, :] * stride_oc,
|
| 46 |
+
li_acc.to(out_ptr.dtype.element_ty),
|
| 47 |
+
mask=(rm[:, None] < M) & (rc[None, :] < C),
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@triton.jit
|
| 52 |
+
def _mhc_fused_kernel(
|
| 53 |
+
x_ptr,
|
| 54 |
+
phi_ptr, # Unified phi: (K, n + n + n_res), layout [pre | post | res]
|
| 55 |
+
alpha_pre,
|
| 56 |
+
alpha_post,
|
| 57 |
+
alpha_res,
|
| 58 |
+
bias_ptr,
|
| 59 |
+
out_ptr, # Shrunk output: (M, n + n_squared), layout [post | res]
|
| 60 |
+
layer_input_ptr, # (M, C); written directly via the inline apply step
|
| 61 |
+
M: tl.constexpr,
|
| 62 |
+
K: tl.constexpr,
|
| 63 |
+
N: tl.constexpr,
|
| 64 |
+
n: tl.constexpr,
|
| 65 |
+
n_squared: tl.constexpr,
|
| 66 |
+
C: tl.constexpr,
|
| 67 |
+
eps: tl.constexpr,
|
| 68 |
+
hc_pre_eps: tl.constexpr,
|
| 69 |
+
hc_post_mult_value: tl.constexpr,
|
| 70 |
+
stride_xm,
|
| 71 |
+
stride_xk,
|
| 72 |
+
stride_phi_k, # Stride for K dimension
|
| 73 |
+
stride_phi_n, # Stride for N dimension (total_cols)
|
| 74 |
+
stride_out_m, # Stride for M dimension
|
| 75 |
+
stride_out_n, # Stride for N dimension (post + res)
|
| 76 |
+
stride_li_m, # Stride for M dimension of layer_input
|
| 77 |
+
stride_li_c, # Stride for C dimension of layer_input
|
| 78 |
+
BLOCK_M: tl.constexpr,
|
| 79 |
+
BLOCK_N: tl.constexpr,
|
| 80 |
+
BLOCK_K: tl.constexpr,
|
| 81 |
+
BLOCK_C: tl.constexpr,
|
| 82 |
+
N_POW2: tl.constexpr,
|
| 83 |
+
NUM_SINKHORN_ITERS: tl.constexpr,
|
| 84 |
+
):
|
| 85 |
+
"""
|
| 86 |
+
Fused kernel for mHC equations 14-18 + the apply step (non-split-K path).
|
| 87 |
+
|
| 88 |
+
Computes three separate outputs:
|
| 89 |
+
- H^pre: (M, n) - sigmoid activation (Eq 17). The pre-stream program runs
|
| 90 |
+
the inline 3D-broadcast apply directly to `layer_input_ptr`, producing
|
| 91 |
+
``layer_input[m, c] = sum_i (sigmoid(H_pre[m, i]) + hc_pre_eps) * x[m, i*C + c]``.
|
| 92 |
+
- H^post: (M, n) with hc_post_mult_value * sigmoid activation (Eq 18)
|
| 93 |
+
- H^res: (M, n, n) doubly-stochastic Sinkhorn-Knopp output when
|
| 94 |
+
NUM_SINKHORN_ITERS > 0 (Eq 19), or raw logits when 0.
|
| 95 |
+
|
| 96 |
+
Post and res streams write to a unified `(M, n + n_squared)` tensor following
|
| 97 |
+
`[post | res]`. phi/bias indexing follows `[pre | post | res]` layout. When
|
| 98 |
+
NUM_SINKHORN_ITERS > 0, the res branch reshapes its `(BLOCK_M, BLOCK_N)`
|
| 99 |
+
tile to `(BLOCK_M, n, n)` and runs log-domain Sinkhorn-Knopp inline before
|
| 100 |
+
the store; this requires `BLOCK_N == n_squared` (enforced by the wrapper).
|
| 101 |
+
|
| 102 |
+
Grid structure:
|
| 103 |
+
- The grid is organized per-stream so each program processes exactly one stream
|
| 104 |
+
- pid_n maps to: [0, n_blocks_pre) = pre, [n_blocks_pre, n_blocks_pre+post) = post, rest = res
|
| 105 |
+
"""
|
| 106 |
+
pid_m = tl.program_id(0)
|
| 107 |
+
pid_n = tl.program_id(1)
|
| 108 |
+
|
| 109 |
+
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 110 |
+
|
| 111 |
+
n_blocks_pre = tl.cdiv(n, BLOCK_N)
|
| 112 |
+
n_blocks_post = n_blocks_pre
|
| 113 |
+
|
| 114 |
+
# Determine stream type from pid_n, each program processes exactly one stream
|
| 115 |
+
is_pre_program = pid_n < n_blocks_pre
|
| 116 |
+
is_post_program = (pid_n >= n_blocks_pre) & (pid_n < n_blocks_pre + n_blocks_post)
|
| 117 |
+
is_res_program = ~is_pre_program & ~is_post_program
|
| 118 |
+
is_post_i32 = is_post_program.to(tl.int32)
|
| 119 |
+
is_res_i32 = is_res_program.to(tl.int32)
|
| 120 |
+
|
| 121 |
+
stream_offset = is_post_i32 * n_blocks_pre + is_res_i32 * (
|
| 122 |
+
n_blocks_pre + n_blocks_post
|
| 123 |
+
)
|
| 124 |
+
local_pid_n = pid_n - stream_offset
|
| 125 |
+
|
| 126 |
+
rn_local = local_pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 127 |
+
|
| 128 |
+
n_out = n + (n_squared - n) * is_res_i32
|
| 129 |
+
|
| 130 |
+
acc = tl.zeros([BLOCK_M, BLOCK_N], dtype=tl.float32)
|
| 131 |
+
acc_sq = tl.zeros([BLOCK_M], dtype=tl.float32)
|
| 132 |
+
|
| 133 |
+
# Compute phi column offset in unified tensor layout: [pre: 0..n-1, post: n..2n-1, res: 2n..2n+n_res-1]
|
| 134 |
+
# phi/bias indexing keeps the original [pre | post | res] layout
|
| 135 |
+
phi_col_start = tl.where(is_pre_program, 0, tl.where(is_post_program, n, 2 * n))
|
| 136 |
+
rn_global = rn_local + phi_col_start
|
| 137 |
+
|
| 138 |
+
# Unified phi tensor - strides are the same for all streams
|
| 139 |
+
for k in range(0, K, BLOCK_K):
|
| 140 |
+
rk = k + tl.arange(0, BLOCK_K)
|
| 141 |
+
|
| 142 |
+
x_tile = tl.load(
|
| 143 |
+
x_ptr + rm[:, None] * stride_xm + rk[None, :] * stride_xk,
|
| 144 |
+
mask=(rm[:, None] < M) & (rk[None, :] < K),
|
| 145 |
+
other=0.0,
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
phi_col_offset = phi_col_start + rn_local
|
| 149 |
+
phi_tile = tl.load(
|
| 150 |
+
phi_ptr
|
| 151 |
+
+ rk[:, None] * stride_phi_k
|
| 152 |
+
+ phi_col_offset[None, :] * stride_phi_n,
|
| 153 |
+
mask=(rk[:, None] < K) & (rn_local[None, :] < n_out),
|
| 154 |
+
other=0.0,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
acc = tl.dot(x_tile, phi_tile, acc=acc)
|
| 158 |
+
x_tile_f32 = x_tile.to(tl.float32)
|
| 159 |
+
acc_sq += tl.sum(x_tile_f32 * x_tile_f32, axis=1)
|
| 160 |
+
|
| 161 |
+
rms = tl.sqrt(acc_sq / K + eps)
|
| 162 |
+
rsigma = 1.0 / rms
|
| 163 |
+
|
| 164 |
+
bias = tl.load(bias_ptr + rn_global, mask=rn_global < N, other=0.0).to(tl.float32)
|
| 165 |
+
alpha_val = tl.where(
|
| 166 |
+
is_pre_program, alpha_pre, tl.where(is_post_program, alpha_post, alpha_res)
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
out = rsigma[:, None] * alpha_val * acc + bias[None, :]
|
| 170 |
+
|
| 171 |
+
if is_pre_program:
|
| 172 |
+
pre_mix = tl.sigmoid(out) + hc_pre_eps # (BLOCK_M, BLOCK_N)
|
| 173 |
+
# Run the apply step inline via a 3D-broadcast reduction
|
| 174 |
+
i_n = tl.arange(0, N_POW2)
|
| 175 |
+
pre_mix_2d = tl.sum(
|
| 176 |
+
tl.where(
|
| 177 |
+
rn_local[None, None, :] == i_n[None, :, None],
|
| 178 |
+
pre_mix[:, None, :],
|
| 179 |
+
0.0,
|
| 180 |
+
),
|
| 181 |
+
axis=2,
|
| 182 |
+
) # (BLOCK_M, N_POW2)
|
| 183 |
+
for c0 in range(0, C, BLOCK_C):
|
| 184 |
+
rc = c0 + tl.arange(0, BLOCK_C)
|
| 185 |
+
_mhc_apply_pre_mix_tile(
|
| 186 |
+
x_ptr,
|
| 187 |
+
layer_input_ptr,
|
| 188 |
+
pre_mix_2d,
|
| 189 |
+
rm,
|
| 190 |
+
rc,
|
| 191 |
+
i_n,
|
| 192 |
+
M,
|
| 193 |
+
C,
|
| 194 |
+
n,
|
| 195 |
+
stride_xm,
|
| 196 |
+
stride_xk,
|
| 197 |
+
stride_li_m,
|
| 198 |
+
stride_li_c,
|
| 199 |
+
)
|
| 200 |
+
else:
|
| 201 |
+
# Post or Res branch.
|
| 202 |
+
if is_post_program:
|
| 203 |
+
out_activated = tl.sigmoid(out) * hc_post_mult_value
|
| 204 |
+
out_col_start = 0
|
| 205 |
+
else:
|
| 206 |
+
# Res branch: log-domain Sinkhorn-Knopp on (BLOCK_M, n, n) sub-tile,
|
| 207 |
+
# or raw logits when NUM_SINKHORN_ITERS == 0. Requires BLOCK_N == n_squared.
|
| 208 |
+
if NUM_SINKHORN_ITERS > 0:
|
| 209 |
+
LOG2_E: tl.constexpr = 1.4426950408889634
|
| 210 |
+
|
| 211 |
+
log2_A = tl.reshape(out, (BLOCK_M, n, n)) * LOG2_E
|
| 212 |
+
|
| 213 |
+
log2_u = tl.zeros((BLOCK_M, n), dtype=tl.float32)
|
| 214 |
+
log2_v = tl.zeros((BLOCK_M, n), dtype=tl.float32)
|
| 215 |
+
|
| 216 |
+
for _ in range(NUM_SINKHORN_ITERS):
|
| 217 |
+
scaled_row = log2_A + log2_v[:, None, :]
|
| 218 |
+
row_max = tl.max(scaled_row, axis=2)
|
| 219 |
+
exp_shifted = tl.exp2(scaled_row - row_max[:, :, None])
|
| 220 |
+
row_sum_exp = tl.sum(exp_shifted, axis=2)
|
| 221 |
+
log2_row_sums = row_max + tl.log2(row_sum_exp)
|
| 222 |
+
log2_u = -log2_row_sums
|
| 223 |
+
|
| 224 |
+
scaled_col = log2_A + log2_u[:, :, None]
|
| 225 |
+
col_max = tl.max(scaled_col, axis=1)
|
| 226 |
+
exp_shifted = tl.exp2(scaled_col - col_max[:, None, :])
|
| 227 |
+
col_sum_exp = tl.sum(exp_shifted, axis=1)
|
| 228 |
+
log2_col_sums = col_max + tl.log2(col_sum_exp)
|
| 229 |
+
log2_v = -log2_col_sums
|
| 230 |
+
|
| 231 |
+
log2_P = log2_A + log2_u[:, :, None] + log2_v[:, None, :]
|
| 232 |
+
P = tl.exp2(log2_P)
|
| 233 |
+
out_activated = tl.reshape(P, (BLOCK_M, n_squared))
|
| 234 |
+
else:
|
| 235 |
+
out_activated = out
|
| 236 |
+
out_col_start = n
|
| 237 |
+
out_col_offset = out_col_start + rn_local
|
| 238 |
+
tl.store(
|
| 239 |
+
out_ptr
|
| 240 |
+
+ rm[:, None] * stride_out_m
|
| 241 |
+
+ out_col_offset[None, :] * stride_out_n,
|
| 242 |
+
out_activated,
|
| 243 |
+
mask=(rm[:, None] < M) & (rn_local[None, :] < n_out),
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
@triton.jit
|
| 248 |
+
def _mhc_fused_split_kernel(
|
| 249 |
+
x_ptr,
|
| 250 |
+
phi_ptr, # Unified phi: (K, n + n + n_squared)
|
| 251 |
+
acc_ptr, # Single unified output: (NUM_KSPLIT, M, n + n + n_squared)
|
| 252 |
+
acc_sq_ptr,
|
| 253 |
+
M: tl.constexpr,
|
| 254 |
+
K: tl.constexpr,
|
| 255 |
+
N: tl.constexpr, # = 2*n + n_squared (logical width of unified phi)
|
| 256 |
+
n: tl.constexpr,
|
| 257 |
+
n_squared: tl.constexpr,
|
| 258 |
+
stride_xm,
|
| 259 |
+
stride_xk,
|
| 260 |
+
stride_phi_k, # Stride for K dimension
|
| 261 |
+
stride_phi_n, # Stride for N dimension (total_cols)
|
| 262 |
+
stride_acc_k, # Stride for NUM_KSPLIT dimension
|
| 263 |
+
stride_acc_m, # Stride for M dimension
|
| 264 |
+
stride_acc_n, # Stride for N dimension (total_cols)
|
| 265 |
+
stride_acc_sq_k,
|
| 266 |
+
stride_acc_sq_m,
|
| 267 |
+
BLOCK_M: tl.constexpr,
|
| 268 |
+
N_TOTAL_POW2: tl.constexpr, # = next_pow2(N), full N-tile per program
|
| 269 |
+
BLOCK_K: tl.constexpr,
|
| 270 |
+
SPLITK_BLOCK_SIZE: tl.constexpr,
|
| 271 |
+
):
|
| 272 |
+
"""
|
| 273 |
+
Split-K kernel for mHC - computes partial results for equations 14-15.
|
| 274 |
+
|
| 275 |
+
Each program owns the *full* (BLOCK_M, N_TOTAL_POW2) tile for one
|
| 276 |
+
`(pid_m, pid_k)` pair: load each x-tile once, dot it against the unified
|
| 277 |
+
phi covering all 3 streams in a single MFMA, and write the entire output
|
| 278 |
+
row in one store. Compared to the old per-stream layout this drops the 3x
|
| 279 |
+
redundant x re-read and lifts MFMA utilization (the pre/post partial
|
| 280 |
+
columns are now subsumed by the same dot as the res columns).
|
| 281 |
+
|
| 282 |
+
Writes all streams to unified contiguous tensor: (NUM_KSPLIT, M, N_total)
|
| 283 |
+
Memory layout: [pre_0..pre_{n-1}, post_0..post_{n-1}, res_0..res_{n_squared-1}]
|
| 284 |
+
|
| 285 |
+
Grid structure: (M_blocks, NUM_KSPLIT).
|
| 286 |
+
"""
|
| 287 |
+
pid_m = tl.program_id(0)
|
| 288 |
+
pid_k = tl.program_id(1)
|
| 289 |
+
|
| 290 |
+
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 291 |
+
rn = tl.arange(0, N_TOTAL_POW2)
|
| 292 |
+
|
| 293 |
+
split_k_start = pid_k * SPLITK_BLOCK_SIZE
|
| 294 |
+
split_k_end = tl.minimum(split_k_start + SPLITK_BLOCK_SIZE, K)
|
| 295 |
+
|
| 296 |
+
if split_k_start >= K:
|
| 297 |
+
return
|
| 298 |
+
|
| 299 |
+
acc = tl.zeros([BLOCK_M, N_TOTAL_POW2], dtype=tl.float32)
|
| 300 |
+
acc_sq = tl.zeros([BLOCK_M], dtype=tl.float32)
|
| 301 |
+
|
| 302 |
+
k_span = split_k_end - split_k_start
|
| 303 |
+
num_k_iter = tl.cdiv(k_span, BLOCK_K)
|
| 304 |
+
|
| 305 |
+
for k_idx in range(num_k_iter):
|
| 306 |
+
k = split_k_start + k_idx * BLOCK_K
|
| 307 |
+
rk = k + tl.arange(0, BLOCK_K)
|
| 308 |
+
|
| 309 |
+
x_tile = tl.load(
|
| 310 |
+
x_ptr + rm[:, None] * stride_xm + rk[None, :] * stride_xk,
|
| 311 |
+
mask=(rm[:, None] < M) & (rk[None, :] < split_k_end),
|
| 312 |
+
other=0.0,
|
| 313 |
+
)
|
| 314 |
+
phi_tile = tl.load(
|
| 315 |
+
phi_ptr + rk[:, None] * stride_phi_k + rn[None, :] * stride_phi_n,
|
| 316 |
+
mask=(rk[:, None] < split_k_end) & (rn[None, :] < N),
|
| 317 |
+
other=0.0,
|
| 318 |
+
)
|
| 319 |
+
|
| 320 |
+
acc = tl.dot(x_tile, phi_tile, acc=acc)
|
| 321 |
+
x_tile_f32 = x_tile.to(tl.float32)
|
| 322 |
+
acc_sq += tl.sum(x_tile_f32 * x_tile_f32, axis=1)
|
| 323 |
+
|
| 324 |
+
tl.store(
|
| 325 |
+
acc_ptr
|
| 326 |
+
+ pid_k * stride_acc_k
|
| 327 |
+
+ rm[:, None] * stride_acc_m
|
| 328 |
+
+ rn[None, :] * stride_acc_n,
|
| 329 |
+
acc,
|
| 330 |
+
mask=(rm[:, None] < M) & (rn[None, :] < N),
|
| 331 |
+
)
|
| 332 |
+
tl.store(
|
| 333 |
+
acc_sq_ptr + pid_k * stride_acc_sq_k + rm * stride_acc_sq_m,
|
| 334 |
+
acc_sq,
|
| 335 |
+
mask=rm < M,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
@triton.jit
|
| 340 |
+
def _mhc_reduce_apply_res_block(
|
| 341 |
+
acc_res, # (BLOCK_M, N_POW2_RES) fp32, already reduced over ks
|
| 342 |
+
rsigma, # (BLOCK_M,) fp32
|
| 343 |
+
rm,
|
| 344 |
+
rn_res_local,
|
| 345 |
+
rn_res_global,
|
| 346 |
+
alpha_res,
|
| 347 |
+
bias_ptr,
|
| 348 |
+
out_ptr,
|
| 349 |
+
M,
|
| 350 |
+
n: tl.constexpr,
|
| 351 |
+
n_squared: tl.constexpr,
|
| 352 |
+
N_POW2_RES: tl.constexpr,
|
| 353 |
+
stride_out_m,
|
| 354 |
+
stride_out_n,
|
| 355 |
+
BLOCK_M: tl.constexpr,
|
| 356 |
+
NUM_SINKHORN_ITERS: tl.constexpr,
|
| 357 |
+
):
|
| 358 |
+
"""Compute h_res = rsigma * alpha_res * acc_res + bias_res, optionally run
|
| 359 |
+
log-domain Sinkhorn-Knopp, and store to ``out[:, n:n+n_squared]``.
|
| 360 |
+
|
| 361 |
+
Shared between the merged-CTA path (`RES_PID_C == 0`, fused with post on the
|
| 362 |
+
same `for-ks` loop) and the split-CTA path (`RES_PID_C != 0`).
|
| 363 |
+
"""
|
| 364 |
+
bias_res = tl.load(
|
| 365 |
+
bias_ptr + rn_res_global,
|
| 366 |
+
mask=rn_res_local < n_squared,
|
| 367 |
+
other=0.0,
|
| 368 |
+
).to(tl.float32)
|
| 369 |
+
h_res = rsigma[:, None] * alpha_res * acc_res + bias_res[None, :]
|
| 370 |
+
|
| 371 |
+
if NUM_SINKHORN_ITERS > 0:
|
| 372 |
+
LOG2_E: tl.constexpr = 1.4426950408889634
|
| 373 |
+
|
| 374 |
+
log2_A = tl.reshape(h_res, (BLOCK_M, n, n)) * LOG2_E
|
| 375 |
+
log2_u = tl.zeros((BLOCK_M, n), dtype=tl.float32)
|
| 376 |
+
log2_v = tl.zeros((BLOCK_M, n), dtype=tl.float32)
|
| 377 |
+
|
| 378 |
+
for _ in range(NUM_SINKHORN_ITERS):
|
| 379 |
+
scaled_row = log2_A + log2_v[:, None, :]
|
| 380 |
+
row_max = tl.max(scaled_row, axis=2)
|
| 381 |
+
exp_shifted = tl.exp2(scaled_row - row_max[:, :, None])
|
| 382 |
+
row_sum_exp = tl.sum(exp_shifted, axis=2)
|
| 383 |
+
log2_row_sums = row_max + tl.log2(row_sum_exp)
|
| 384 |
+
log2_u = -log2_row_sums
|
| 385 |
+
|
| 386 |
+
scaled_col = log2_A + log2_u[:, :, None]
|
| 387 |
+
col_max = tl.max(scaled_col, axis=1)
|
| 388 |
+
exp_shifted = tl.exp2(scaled_col - col_max[:, None, :])
|
| 389 |
+
col_sum_exp = tl.sum(exp_shifted, axis=1)
|
| 390 |
+
log2_col_sums = col_max + tl.log2(col_sum_exp)
|
| 391 |
+
log2_v = -log2_col_sums
|
| 392 |
+
|
| 393 |
+
log2_P = log2_A + log2_u[:, :, None] + log2_v[:, None, :]
|
| 394 |
+
P = tl.exp2(log2_P)
|
| 395 |
+
out_res = tl.reshape(P, (BLOCK_M, n_squared))
|
| 396 |
+
else:
|
| 397 |
+
out_res = h_res
|
| 398 |
+
|
| 399 |
+
tl.store(
|
| 400 |
+
out_ptr
|
| 401 |
+
+ rm[:, None] * stride_out_m
|
| 402 |
+
+ (n + rn_res_local[None, :]) * stride_out_n,
|
| 403 |
+
out_res,
|
| 404 |
+
mask=(rm[:, None] < M) & (rn_res_local[None, :] < n_squared),
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
@triton.jit
|
| 409 |
+
def _mhc_reduce_apply_kernel(
|
| 410 |
+
acc_ptr, # Unified split-K partials: (NUM_KSPLIT, M, n + n + n_squared), layout [pre | post | res]
|
| 411 |
+
acc_sq_ptr, # Sum-of-squares partials: (NUM_KSPLIT, M)
|
| 412 |
+
alpha_pre,
|
| 413 |
+
alpha_post,
|
| 414 |
+
alpha_res,
|
| 415 |
+
bias_ptr, # (n + n + n_squared,) fp32
|
| 416 |
+
x_ptr, # (M, n*C)
|
| 417 |
+
out_ptr, # Unified output: (M, n + n_squared), layout [post | res]
|
| 418 |
+
layer_input_ptr, # (M, C) in x.dtype
|
| 419 |
+
M,
|
| 420 |
+
K: tl.constexpr,
|
| 421 |
+
n: tl.constexpr,
|
| 422 |
+
n_squared: tl.constexpr,
|
| 423 |
+
C: tl.constexpr,
|
| 424 |
+
eps: tl.constexpr,
|
| 425 |
+
hc_pre_eps: tl.constexpr,
|
| 426 |
+
hc_post_mult_value: tl.constexpr,
|
| 427 |
+
stride_acc_k,
|
| 428 |
+
stride_acc_m,
|
| 429 |
+
stride_acc_n,
|
| 430 |
+
stride_acc_sq_k,
|
| 431 |
+
stride_acc_sq_m,
|
| 432 |
+
stride_xm,
|
| 433 |
+
stride_xk,
|
| 434 |
+
stride_out_m,
|
| 435 |
+
stride_out_n,
|
| 436 |
+
stride_li_m,
|
| 437 |
+
stride_li_c,
|
| 438 |
+
BLOCK_M: tl.constexpr,
|
| 439 |
+
BLOCK_C: tl.constexpr,
|
| 440 |
+
N_POW2: tl.constexpr,
|
| 441 |
+
N_POW2_RES: tl.constexpr,
|
| 442 |
+
ACTUAL_KSPLIT: tl.constexpr,
|
| 443 |
+
NUM_SINKHORN_ITERS: tl.constexpr,
|
| 444 |
+
RES_PID_C: tl.constexpr,
|
| 445 |
+
):
|
| 446 |
+
"""
|
| 447 |
+
Reduce-and-apply kernel for the split-K mHC pipeline (Eq 15-19 + apply).
|
| 448 |
+
|
| 449 |
+
Grid: ``(cdiv(M, BLOCK_M), cdiv(C, BLOCK_C))``.
|
| 450 |
+
|
| 451 |
+
Each program reads its M-slice of split-K partials once and computes:
|
| 452 |
+
|
| 453 |
+
- All pids: pre stream (RMS + bias + alpha + sigmoid + hc_pre_eps) and
|
| 454 |
+
the apply step ``layer_input[m, c] = sum_i pre_mix[m, i] * x[m, i*C + c]``
|
| 455 |
+
restricted to this pid's BLOCK_C slice of the hidden dimension.
|
| 456 |
+
- ``pid_c == 0``: post stream (``hc_post_mult_value * sigmoid``), writes to
|
| 457 |
+
``out[:, :n]``.
|
| 458 |
+
- ``pid_c == RES_PID_C``: res stream (in-kernel log-domain Sinkhorn-Knopp
|
| 459 |
+
when ``NUM_SINKHORN_ITERS > 0``, else raw logits), writes to
|
| 460 |
+
``out[:, n:n+n_squared]``.
|
| 461 |
+
|
| 462 |
+
Sinkhorn requires ``n_squared is`` a power of two; the wrapper enforces
|
| 463 |
+
this when ``NUM_SINKHORN_ITERS > 0``.
|
| 464 |
+
"""
|
| 465 |
+
pid_m = tl.program_id(0)
|
| 466 |
+
pid_c = tl.program_id(1)
|
| 467 |
+
|
| 468 |
+
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 469 |
+
rc = pid_c * BLOCK_C + tl.arange(0, BLOCK_C)
|
| 470 |
+
rn_pre = tl.arange(0, N_POW2)
|
| 471 |
+
|
| 472 |
+
# --- 1) Reduce split-K partials: PRE columns + acc_sq ---
|
| 473 |
+
acc_pre = tl.zeros([BLOCK_M, N_POW2], dtype=tl.float32)
|
| 474 |
+
acc_sq = tl.zeros([BLOCK_M], dtype=tl.float32)
|
| 475 |
+
for ks in range(ACTUAL_KSPLIT):
|
| 476 |
+
acc_pre += tl.load(
|
| 477 |
+
acc_ptr
|
| 478 |
+
+ ks * stride_acc_k
|
| 479 |
+
+ rm[:, None] * stride_acc_m
|
| 480 |
+
+ rn_pre[None, :] * stride_acc_n,
|
| 481 |
+
mask=(rm[:, None] < M) & (rn_pre[None, :] < n),
|
| 482 |
+
other=0.0,
|
| 483 |
+
)
|
| 484 |
+
acc_sq += tl.load(
|
| 485 |
+
acc_sq_ptr + ks * stride_acc_sq_k + rm * stride_acc_sq_m,
|
| 486 |
+
mask=rm < M,
|
| 487 |
+
other=0.0,
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
# --- 2) RMS normalization (Eq 15) ---
|
| 491 |
+
rms = tl.sqrt(acc_sq / K + eps)
|
| 492 |
+
rsigma = 1.0 / rms
|
| 493 |
+
|
| 494 |
+
# --- 3) Pre stream: bias + alpha + sigmoid + hc_pre_eps (Eq 16-17) ---
|
| 495 |
+
bias_pre = tl.load(bias_ptr + rn_pre, mask=rn_pre < n, other=0.0).to(tl.float32)
|
| 496 |
+
h_pre = rsigma[:, None] * alpha_pre * acc_pre + bias_pre[None, :]
|
| 497 |
+
pre_mix_2d = tl.sigmoid(h_pre) + hc_pre_eps
|
| 498 |
+
|
| 499 |
+
# --- 4) Apply step for this pid's BLOCK_C slice ---
|
| 500 |
+
_mhc_apply_pre_mix_tile(
|
| 501 |
+
x_ptr,
|
| 502 |
+
layer_input_ptr,
|
| 503 |
+
pre_mix_2d,
|
| 504 |
+
rm,
|
| 505 |
+
rc,
|
| 506 |
+
rn_pre,
|
| 507 |
+
M,
|
| 508 |
+
C,
|
| 509 |
+
n,
|
| 510 |
+
stride_xm,
|
| 511 |
+
stride_xk,
|
| 512 |
+
stride_li_m,
|
| 513 |
+
stride_li_c,
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
# --- 5) Post stream on pid_c == 0; Res stream on pid_c == RES_PID_C ---
|
| 517 |
+
# Two compile-time layouts:
|
| 518 |
+
# RES_PID_C == 0 (single C-tile, shared CTA): one for-ks loop loads both
|
| 519 |
+
# post and res partials, then post and res are computed back-to-back.
|
| 520 |
+
# RES_PID_C != 0 (multi C-tile, separate CTAs): each CTA runs its own
|
| 521 |
+
# for-ks loop. The res body is factored into _mhc_reduce_apply_res_block
|
| 522 |
+
# to avoid duplication with the shared-CTA branch.
|
| 523 |
+
if RES_PID_C == 0:
|
| 524 |
+
if pid_c == 0:
|
| 525 |
+
rn_post_local = tl.arange(0, N_POW2)
|
| 526 |
+
rn_post_global = n + rn_post_local
|
| 527 |
+
rn_res_local = tl.arange(0, N_POW2_RES)
|
| 528 |
+
rn_res_global = 2 * n + rn_res_local
|
| 529 |
+
|
| 530 |
+
acc_post = tl.zeros([BLOCK_M, N_POW2], dtype=tl.float32)
|
| 531 |
+
acc_res = tl.zeros([BLOCK_M, N_POW2_RES], dtype=tl.float32)
|
| 532 |
+
for ks in range(ACTUAL_KSPLIT):
|
| 533 |
+
acc_post += tl.load(
|
| 534 |
+
acc_ptr
|
| 535 |
+
+ ks * stride_acc_k
|
| 536 |
+
+ rm[:, None] * stride_acc_m
|
| 537 |
+
+ rn_post_global[None, :] * stride_acc_n,
|
| 538 |
+
mask=(rm[:, None] < M) & (rn_post_local[None, :] < n),
|
| 539 |
+
other=0.0,
|
| 540 |
+
)
|
| 541 |
+
acc_res += tl.load(
|
| 542 |
+
acc_ptr
|
| 543 |
+
+ ks * stride_acc_k
|
| 544 |
+
+ rm[:, None] * stride_acc_m
|
| 545 |
+
+ rn_res_global[None, :] * stride_acc_n,
|
| 546 |
+
mask=(rm[:, None] < M) & (rn_res_local[None, :] < n_squared),
|
| 547 |
+
other=0.0,
|
| 548 |
+
)
|
| 549 |
+
|
| 550 |
+
bias_post = tl.load(
|
| 551 |
+
bias_ptr + rn_post_global,
|
| 552 |
+
mask=rn_post_local < n,
|
| 553 |
+
other=0.0,
|
| 554 |
+
).to(tl.float32)
|
| 555 |
+
h_post = rsigma[:, None] * alpha_post * acc_post + bias_post[None, :]
|
| 556 |
+
out_post = tl.sigmoid(h_post) * hc_post_mult_value
|
| 557 |
+
tl.store(
|
| 558 |
+
out_ptr
|
| 559 |
+
+ rm[:, None] * stride_out_m
|
| 560 |
+
+ rn_post_local[None, :] * stride_out_n,
|
| 561 |
+
out_post,
|
| 562 |
+
mask=(rm[:, None] < M) & (rn_post_local[None, :] < n),
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
+
_mhc_reduce_apply_res_block(
|
| 566 |
+
acc_res,
|
| 567 |
+
rsigma,
|
| 568 |
+
rm,
|
| 569 |
+
rn_res_local,
|
| 570 |
+
rn_res_global,
|
| 571 |
+
alpha_res,
|
| 572 |
+
bias_ptr,
|
| 573 |
+
out_ptr,
|
| 574 |
+
M,
|
| 575 |
+
n,
|
| 576 |
+
n_squared,
|
| 577 |
+
N_POW2_RES,
|
| 578 |
+
stride_out_m,
|
| 579 |
+
stride_out_n,
|
| 580 |
+
BLOCK_M,
|
| 581 |
+
NUM_SINKHORN_ITERS,
|
| 582 |
+
)
|
| 583 |
+
else:
|
| 584 |
+
if pid_c == 0:
|
| 585 |
+
rn_post_local = tl.arange(0, N_POW2)
|
| 586 |
+
rn_post_global = n + rn_post_local
|
| 587 |
+
acc_post = tl.zeros([BLOCK_M, N_POW2], dtype=tl.float32)
|
| 588 |
+
for ks in range(ACTUAL_KSPLIT):
|
| 589 |
+
acc_post += tl.load(
|
| 590 |
+
acc_ptr
|
| 591 |
+
+ ks * stride_acc_k
|
| 592 |
+
+ rm[:, None] * stride_acc_m
|
| 593 |
+
+ rn_post_global[None, :] * stride_acc_n,
|
| 594 |
+
mask=(rm[:, None] < M) & (rn_post_local[None, :] < n),
|
| 595 |
+
other=0.0,
|
| 596 |
+
)
|
| 597 |
+
bias_post = tl.load(
|
| 598 |
+
bias_ptr + rn_post_global,
|
| 599 |
+
mask=rn_post_local < n,
|
| 600 |
+
other=0.0,
|
| 601 |
+
).to(tl.float32)
|
| 602 |
+
h_post = rsigma[:, None] * alpha_post * acc_post + bias_post[None, :]
|
| 603 |
+
out_post = tl.sigmoid(h_post) * hc_post_mult_value
|
| 604 |
+
tl.store(
|
| 605 |
+
out_ptr
|
| 606 |
+
+ rm[:, None] * stride_out_m
|
| 607 |
+
+ rn_post_local[None, :] * stride_out_n,
|
| 608 |
+
out_post,
|
| 609 |
+
mask=(rm[:, None] < M) & (rn_post_local[None, :] < n),
|
| 610 |
+
)
|
| 611 |
+
|
| 612 |
+
if pid_c == RES_PID_C:
|
| 613 |
+
rn_res_local = tl.arange(0, N_POW2_RES)
|
| 614 |
+
rn_res_global = 2 * n + rn_res_local
|
| 615 |
+
acc_res = tl.zeros([BLOCK_M, N_POW2_RES], dtype=tl.float32)
|
| 616 |
+
for ks in range(ACTUAL_KSPLIT):
|
| 617 |
+
acc_res += tl.load(
|
| 618 |
+
acc_ptr
|
| 619 |
+
+ ks * stride_acc_k
|
| 620 |
+
+ rm[:, None] * stride_acc_m
|
| 621 |
+
+ rn_res_global[None, :] * stride_acc_n,
|
| 622 |
+
mask=(rm[:, None] < M) & (rn_res_local[None, :] < n_squared),
|
| 623 |
+
other=0.0,
|
| 624 |
+
)
|
| 625 |
+
_mhc_reduce_apply_res_block(
|
| 626 |
+
acc_res,
|
| 627 |
+
rsigma,
|
| 628 |
+
rm,
|
| 629 |
+
rn_res_local,
|
| 630 |
+
rn_res_global,
|
| 631 |
+
alpha_res,
|
| 632 |
+
bias_ptr,
|
| 633 |
+
out_ptr,
|
| 634 |
+
M,
|
| 635 |
+
n,
|
| 636 |
+
n_squared,
|
| 637 |
+
N_POW2_RES,
|
| 638 |
+
stride_out_m,
|
| 639 |
+
stride_out_n,
|
| 640 |
+
BLOCK_M,
|
| 641 |
+
NUM_SINKHORN_ITERS,
|
| 642 |
+
)
|
| 643 |
+
|
| 644 |
+
|
| 645 |
+
@triton.jit
|
| 646 |
+
def _mhc_post_kernel(
|
| 647 |
+
out_ptr, # (M, n, C) bf16 / fp16
|
| 648 |
+
x_ptr, # (M, C) bf16 / fp16 (layer_input from mhc())
|
| 649 |
+
residual_ptr, # (M, n, C) bf16 / fp16
|
| 650 |
+
post_mix_ptr, # (M, n) fp32 (mhc()'s h_post)
|
| 651 |
+
comb_mix_ptr, # (M, n, n) fp32 [src, dst] (mhc()'s h_res)
|
| 652 |
+
M,
|
| 653 |
+
C,
|
| 654 |
+
stride_x_m,
|
| 655 |
+
stride_x_c,
|
| 656 |
+
stride_res_m,
|
| 657 |
+
stride_res_n,
|
| 658 |
+
stride_res_c,
|
| 659 |
+
stride_out_m,
|
| 660 |
+
stride_out_n,
|
| 661 |
+
stride_out_c,
|
| 662 |
+
stride_post_m,
|
| 663 |
+
stride_post_n,
|
| 664 |
+
stride_comb_m,
|
| 665 |
+
stride_comb_src,
|
| 666 |
+
stride_comb_dst,
|
| 667 |
+
n: tl.constexpr,
|
| 668 |
+
BLOCK_M: tl.constexpr,
|
| 669 |
+
BLOCK_C: tl.constexpr,
|
| 670 |
+
):
|
| 671 |
+
"""Fused mhc_post kernel: compute one M-tile across all `n` output
|
| 672 |
+
streams and the full hidden dim.
|
| 673 |
+
|
| 674 |
+
out[m, j, c] = post_mix[m, j] * x[m, c]
|
| 675 |
+
+ sum_h comb_mix[m, h, j] * residual[m, h, c]
|
| 676 |
+
|
| 677 |
+
Grid: ``(cdiv(M, BLOCK_M),)``. Each program loads ``post_mix``
|
| 678 |
+
(BLOCK_M, n) and ``comb_mix`` (BLOCK_M, n, n) once and reuses them
|
| 679 |
+
across the persistent loop over ``BLOCK_C``-sized C-tiles. The
|
| 680 |
+
``n``-source-head contraction inside each C-tile is unrolled via
|
| 681 |
+
``tl.static_range``: each iteration loads a 2-D ``residual`` slice and
|
| 682 |
+
a 1-D ``comb_mix`` row and accumulates ``comb_h * res_h`` into
|
| 683 |
+
``out_tile``. This avoids materializing a (BLOCK_M, n, n, BLOCK_C)
|
| 684 |
+
outer-product intermediate. Requires ``n`` to be a power of 2 so
|
| 685 |
+
``tl.arange(0, n)`` compiles.
|
| 686 |
+
"""
|
| 687 |
+
pid_m = tl.program_id(0)
|
| 688 |
+
|
| 689 |
+
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 690 |
+
i_n = tl.arange(0, n)
|
| 691 |
+
|
| 692 |
+
m_mask = rm < M
|
| 693 |
+
|
| 694 |
+
post_mix_tile = tl.load(
|
| 695 |
+
post_mix_ptr + rm[:, None] * stride_post_m + i_n[None, :] * stride_post_n,
|
| 696 |
+
mask=m_mask[:, None],
|
| 697 |
+
other=0.0,
|
| 698 |
+
)
|
| 699 |
+
|
| 700 |
+
# Pre-load each row of the per-token (n_src, n_dst) comb_mix matrix into
|
| 701 |
+
# a tuple of 2-D tiles. Each ``comb_rows[h]`` has shape (BLOCK_M, n_dst)
|
| 702 |
+
# and lives in registers across the C-loop, eliminating per-C-tile
|
| 703 |
+
# reloads of the small per-token coefficients.
|
| 704 |
+
comb_rows = ()
|
| 705 |
+
for h in tl.static_range(n):
|
| 706 |
+
comb_rows = comb_rows + (
|
| 707 |
+
tl.load(
|
| 708 |
+
comb_mix_ptr
|
| 709 |
+
+ rm[:, None] * stride_comb_m
|
| 710 |
+
+ h * stride_comb_src
|
| 711 |
+
+ i_n[None, :] * stride_comb_dst,
|
| 712 |
+
mask=m_mask[:, None],
|
| 713 |
+
other=0.0,
|
| 714 |
+
),
|
| 715 |
+
)
|
| 716 |
+
|
| 717 |
+
for c_start in range(0, C, BLOCK_C):
|
| 718 |
+
rc = c_start + tl.arange(0, BLOCK_C)
|
| 719 |
+
c_mask = rc < C
|
| 720 |
+
|
| 721 |
+
# ``x`` and ``residual`` are bf16 / fp16 in production. Keeping them
|
| 722 |
+
# in their native dtype halves on-chip footprint vs an upfront fp32
|
| 723 |
+
# promotion; mixed-dtype multiply with fp32 ``post_mix`` / ``comb``
|
| 724 |
+
# is auto-promoted by Triton with an fp32 accumulator.
|
| 725 |
+
x_tile = tl.load(
|
| 726 |
+
x_ptr + rm[:, None] * stride_x_m + rc[None, :] * stride_x_c,
|
| 727 |
+
mask=m_mask[:, None] & c_mask[None, :],
|
| 728 |
+
other=0.0,
|
| 729 |
+
cache_modifier=".cg",
|
| 730 |
+
)
|
| 731 |
+
|
| 732 |
+
out_tile = post_mix_tile[:, :, None] * x_tile[:, None, :].to(tl.float32)
|
| 733 |
+
|
| 734 |
+
for h in tl.static_range(n):
|
| 735 |
+
res_h = tl.load(
|
| 736 |
+
residual_ptr
|
| 737 |
+
+ rm[:, None] * stride_res_m
|
| 738 |
+
+ h * stride_res_n
|
| 739 |
+
+ rc[None, :] * stride_res_c,
|
| 740 |
+
mask=m_mask[:, None] & c_mask[None, :],
|
| 741 |
+
other=0.0,
|
| 742 |
+
cache_modifier=".cg",
|
| 743 |
+
)
|
| 744 |
+
comb_h = comb_rows[h] # (BLOCK_M, n_dst), pre-loaded 2-D tile
|
| 745 |
+
out_tile += comb_h[:, :, None] * res_h[:, None, :].to(tl.float32)
|
| 746 |
+
|
| 747 |
+
tl.store(
|
| 748 |
+
out_ptr
|
| 749 |
+
+ rm[:, None, None] * stride_out_m
|
| 750 |
+
+ i_n[None, :, None] * stride_out_n
|
| 751 |
+
+ rc[None, None, :] * stride_out_c,
|
| 752 |
+
out_tile.to(out_ptr.dtype.element_ty),
|
| 753 |
+
mask=m_mask[:, None, None] & c_mask[None, None, :],
|
| 754 |
+
cache_modifier=".cs",
|
| 755 |
+
)
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
_mhc_post_pre_split_kernel_repr = make_kernel_repr(
|
| 759 |
+
"_mhc_post_pre_split_kernel",
|
| 760 |
+
[
|
| 761 |
+
"n",
|
| 762 |
+
"C",
|
| 763 |
+
"stride_phi_k",
|
| 764 |
+
"stride_phi_n",
|
| 765 |
+
"BLOCK_M",
|
| 766 |
+
"BLOCK_C",
|
| 767 |
+
"N_TOTAL_POW2",
|
| 768 |
+
],
|
| 769 |
+
)
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
@triton.jit(repr=_mhc_post_pre_split_kernel_repr)
|
| 773 |
+
def _mhc_post_pre_split_kernel(
|
| 774 |
+
# mhc_post inputs
|
| 775 |
+
layer_input_ptr, # (M, C) x.dtype - attn/ffn output
|
| 776 |
+
residual_in_ptr, # (M, n, C) x.dtype - prev-layer multi-stream residual
|
| 777 |
+
post_mix_ptr, # (M, n) fp32 - h_post from preceding mhc_pre
|
| 778 |
+
comb_mix_ptr, # (M, n, n) fp32 - h_res from preceding mhc_pre
|
| 779 |
+
# mhc_post output (also the next mhc_pre's flattened x input)
|
| 780 |
+
residual_out_ptr, # (M, n, C) x.dtype - new residual; consumed by next layer's hc_post
|
| 781 |
+
# next mhc_pre's split-K partials
|
| 782 |
+
phi_ptr, # (n*C, N=2n+n^2) x.dtype - projection matrix, cols [pre|post|res]
|
| 783 |
+
acc_ptr, # (NUM_KSPLIT=cdiv(C, BLOCK_C), M, N) fp32 - GEMM partials
|
| 784 |
+
acc_sq_ptr, # (NUM_KSPLIT, M) fp32 - sum-of-squares partials
|
| 785 |
+
M,
|
| 786 |
+
N: tl.constexpr, # = 2*n + n_squared
|
| 787 |
+
n: tl.constexpr,
|
| 788 |
+
C: tl.constexpr,
|
| 789 |
+
stride_x_m,
|
| 790 |
+
stride_x_c,
|
| 791 |
+
stride_resin_m,
|
| 792 |
+
stride_resin_n,
|
| 793 |
+
stride_resin_c,
|
| 794 |
+
stride_post_m,
|
| 795 |
+
stride_post_n,
|
| 796 |
+
stride_comb_m,
|
| 797 |
+
stride_comb_src,
|
| 798 |
+
stride_comb_dst,
|
| 799 |
+
stride_resout_m,
|
| 800 |
+
stride_resout_n,
|
| 801 |
+
stride_resout_c,
|
| 802 |
+
stride_phi_k: tl.constexpr,
|
| 803 |
+
stride_phi_n: tl.constexpr,
|
| 804 |
+
stride_acc_k,
|
| 805 |
+
stride_acc_m,
|
| 806 |
+
stride_acc_n,
|
| 807 |
+
stride_acc_sq_k,
|
| 808 |
+
stride_acc_sq_m,
|
| 809 |
+
BLOCK_M: tl.constexpr,
|
| 810 |
+
BLOCK_C: tl.constexpr,
|
| 811 |
+
N_TOTAL_POW2: tl.constexpr,
|
| 812 |
+
):
|
| 813 |
+
"""Fused mhc_post + (next) mhc_pre split-K kernel.
|
| 814 |
+
|
| 815 |
+
Per (M-tile, C-tile) — n streams unrolled via tl.static_range — this CTA:
|
| 816 |
+
1. Computes the new mHC residual stream (mhc_post step):
|
| 817 |
+
residual_out[m, j, c] = post_mix[m, j] * layer_input[m, c]
|
| 818 |
+
+ sum_h comb_mix[m, h, j] * residual_in[m, h, c]
|
| 819 |
+
2. With that residual tile still live in registers, contributes the next
|
| 820 |
+
mhc_pre's split-K GEMM partials over the same C-tile, treating the
|
| 821 |
+
residual as the next pre's flattened x = (M, n*C):
|
| 822 |
+
acc[pid_c, m, :N] += sum_j x_j(:, c_block:+BLOCK_C)
|
| 823 |
+
@ phi[j*C+c_block:+BLOCK_C, :N]
|
| 824 |
+
acc_sq[pid_c, m] += sum_j ||x_j(:, c_block:+BLOCK_C)||^2
|
| 825 |
+
where ``x_j`` is the j-th stream of residual_out (= the "h" index of
|
| 826 |
+
the flattened pre input ``x[m, h*C+c]``).
|
| 827 |
+
|
| 828 |
+
The C-tile axis IS the pre's split-K axis: each CTA owns one of
|
| 829 |
+
``cdiv(C, BLOCK_C)`` non-overlapping K-splits of the next-pre GEMM. The
|
| 830 |
+
remaining apply / RMS / Sinkhorn work is finished by a separate launch
|
| 831 |
+
of ``_mhc_reduce_apply_kernel``, which re-reads ``residual_out`` as its
|
| 832 |
+
``x`` operand for the apply-pre step.
|
| 833 |
+
|
| 834 |
+
The post output (``residual_out``) is still written to HBM because the
|
| 835 |
+
next layer's ``hc_post`` consumes it as its own ``residual_in``. The
|
| 836 |
+
HBM saving vs the unfused chain comes from not re-reading it as the
|
| 837 |
+
next-pre's GEMM operand.
|
| 838 |
+
|
| 839 |
+
Grid: ``(cdiv(M, BLOCK_M), cdiv(C, BLOCK_C))``.
|
| 840 |
+
"""
|
| 841 |
+
pid_m = tl.program_id(0)
|
| 842 |
+
pid_c = tl.program_id(1)
|
| 843 |
+
|
| 844 |
+
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 845 |
+
rc = pid_c * BLOCK_C + tl.arange(0, BLOCK_C)
|
| 846 |
+
rn = tl.arange(0, N_TOTAL_POW2)
|
| 847 |
+
i_n = tl.arange(0, n)
|
| 848 |
+
|
| 849 |
+
m_mask = rm < M
|
| 850 |
+
c_mask = rc < C
|
| 851 |
+
|
| 852 |
+
out_dtype = residual_out_ptr.dtype.element_ty
|
| 853 |
+
|
| 854 |
+
# --- 1) Load small post-step operands once. ---
|
| 855 |
+
post_mix_tile = tl.load(
|
| 856 |
+
post_mix_ptr + rm[:, None] * stride_post_m + i_n[None, :] * stride_post_n,
|
| 857 |
+
mask=m_mask[:, None],
|
| 858 |
+
other=0.0,
|
| 859 |
+
) # (BLOCK_M, n) fp32
|
| 860 |
+
|
| 861 |
+
# comb_mix as a single (BLOCK_M, n_src, n_dst) 3D tile.
|
| 862 |
+
comb_3d = tl.load(
|
| 863 |
+
comb_mix_ptr
|
| 864 |
+
+ rm[:, None, None] * stride_comb_m
|
| 865 |
+
+ i_n[None, :, None] * stride_comb_src
|
| 866 |
+
+ i_n[None, None, :] * stride_comb_dst,
|
| 867 |
+
mask=m_mask[:, None, None],
|
| 868 |
+
other=0.0,
|
| 869 |
+
) # (BLOCK_M, n_src, n_dst) fp32
|
| 870 |
+
|
| 871 |
+
x_tile = tl.load(
|
| 872 |
+
layer_input_ptr + rm[:, None] * stride_x_m + rc[None, :] * stride_x_c,
|
| 873 |
+
mask=m_mask[:, None] & c_mask[None, :],
|
| 874 |
+
other=0.0,
|
| 875 |
+
cache_modifier=".cg",
|
| 876 |
+
) # (BLOCK_M, BLOCK_C) x.dtype
|
| 877 |
+
|
| 878 |
+
# All n source streams of residual_in as one (BLOCK_M, n_src, BLOCK_C) tile.
|
| 879 |
+
res_3d = tl.load(
|
| 880 |
+
residual_in_ptr
|
| 881 |
+
+ rm[:, None, None] * stride_resin_m
|
| 882 |
+
+ i_n[None, :, None] * stride_resin_n
|
| 883 |
+
+ rc[None, None, :] * stride_resin_c,
|
| 884 |
+
mask=m_mask[:, None, None] & c_mask[None, None, :],
|
| 885 |
+
other=0.0,
|
| 886 |
+
cache_modifier=".cg",
|
| 887 |
+
) # (BLOCK_M, n_src, BLOCK_C) x.dtype
|
| 888 |
+
|
| 889 |
+
# --- 2) Post step: build (BLOCK_M, n_dst, BLOCK_C) residual_out via tl.sum.
|
| 890 |
+
# Conceptually: out[m, j, c] = post[m, j] * x[m, c]
|
| 891 |
+
# + sum_h_src comb[m, h_src, j] * res[m, h_src, c].
|
| 892 |
+
# The 4D outer product `comb[:, :, :, None] * res[:, :, None, :]` of shape
|
| 893 |
+
# (BLOCK_M, n_src, n_dst, BLOCK_C) is folded into tl.sum(axis=1); Triton
|
| 894 |
+
# fuses the broadcast + sum into a register-resident reduction without
|
| 895 |
+
# materializing the full 4D intermediate. Measured ~25-40% faster than the
|
| 896 |
+
# manual static_range(n) per-h load+accumulate at M ∈ {1..256}, hc=4,
|
| 897 |
+
# C=4096.
|
| 898 |
+
out_tile = post_mix_tile[:, :, None] * x_tile[:, None, :].to(tl.float32)
|
| 899 |
+
out_tile += tl.sum(
|
| 900 |
+
comb_3d[:, :, :, None] * res_3d[:, :, None, :].to(tl.float32),
|
| 901 |
+
axis=1,
|
| 902 |
+
)
|
| 903 |
+
|
| 904 |
+
# Store residual_out for next layer's hc_post (separate from the dot below).
|
| 905 |
+
tl.store(
|
| 906 |
+
residual_out_ptr
|
| 907 |
+
+ rm[:, None, None] * stride_resout_m
|
| 908 |
+
+ i_n[None, :, None] * stride_resout_n
|
| 909 |
+
+ rc[None, None, :] * stride_resout_c,
|
| 910 |
+
out_tile.to(out_dtype),
|
| 911 |
+
mask=m_mask[:, None, None] & c_mask[None, None, :],
|
| 912 |
+
cache_modifier=".cs",
|
| 913 |
+
)
|
| 914 |
+
|
| 915 |
+
# --- 3) Next-pre split-K partial GEMM + sqrsum, sharing out_tile in registers. ---
|
| 916 |
+
# Reshape (BLOCK_M, n, BLOCK_C) → (BLOCK_M, n*BLOCK_C) as the next pre's
|
| 917 |
+
# flattened x for this K-split. The matching phi rows are (n, BLOCK_C, N)
|
| 918 |
+
# tiled at offsets ``h*C + c`` for h in [0, n), c in [c_block, c_block+BLOCK_C);
|
| 919 |
+
# we 3D-load and reshape to (n*BLOCK_C, N_TOTAL_POW2) so a single tl.dot
|
| 920 |
+
# handles all n streams' contribution to this K-split.
|
| 921 |
+
out_flat = tl.reshape(out_tile, [BLOCK_M, n * BLOCK_C])
|
| 922 |
+
out_flat_cast = out_flat.to(out_dtype)
|
| 923 |
+
|
| 924 |
+
k_offset_2d = i_n[:, None] * C + rc[None, :] # (n, BLOCK_C)
|
| 925 |
+
phi_3d = tl.load(
|
| 926 |
+
phi_ptr
|
| 927 |
+
+ k_offset_2d[:, :, None] * stride_phi_k
|
| 928 |
+
+ rn[None, None, :] * stride_phi_n,
|
| 929 |
+
mask=c_mask[None, :, None] & (rn[None, None, :] < N),
|
| 930 |
+
other=0.0,
|
| 931 |
+
).to(
|
| 932 |
+
out_dtype
|
| 933 |
+
) # (n, BLOCK_C, N_TOTAL_POW2) phi.dtype
|
| 934 |
+
phi_flat = tl.reshape(phi_3d, [n * BLOCK_C, N_TOTAL_POW2])
|
| 935 |
+
|
| 936 |
+
acc_gemm = tl.dot(out_flat_cast, phi_flat)
|
| 937 |
+
acc_sq = tl.sum(out_flat * out_flat, axis=1)
|
| 938 |
+
|
| 939 |
+
# --- 4) Write split-K partials for the reduce-apply kernel. ---
|
| 940 |
+
tl.store(
|
| 941 |
+
acc_ptr
|
| 942 |
+
+ pid_c * stride_acc_k
|
| 943 |
+
+ rm[:, None] * stride_acc_m
|
| 944 |
+
+ rn[None, :] * stride_acc_n,
|
| 945 |
+
acc_gemm,
|
| 946 |
+
mask=m_mask[:, None] & (rn[None, :] < N),
|
| 947 |
+
)
|
| 948 |
+
tl.store(
|
| 949 |
+
acc_sq_ptr + pid_c * stride_acc_sq_k + rm * stride_acc_sq_m,
|
| 950 |
+
acc_sq,
|
| 951 |
+
mask=m_mask,
|
| 952 |
+
)
|
| 953 |
+
|
| 954 |
+
|
| 955 |
+
@triton.jit
|
| 956 |
+
def _mhc_post_pre_reduce_apply_res_block(
|
| 957 |
+
acc_res, # (BLOCK_M, N_POW2_RES) fp32, already reduced over ks
|
| 958 |
+
rsigma, # (BLOCK_M,) fp32
|
| 959 |
+
rm,
|
| 960 |
+
rn_res_local,
|
| 961 |
+
rn_res_global,
|
| 962 |
+
alpha_res,
|
| 963 |
+
bias_ptr,
|
| 964 |
+
h_res_ptr,
|
| 965 |
+
M,
|
| 966 |
+
n: tl.constexpr,
|
| 967 |
+
n_squared: tl.constexpr,
|
| 968 |
+
N_POW2_RES: tl.constexpr,
|
| 969 |
+
stride_hr_m,
|
| 970 |
+
stride_hr_n,
|
| 971 |
+
BLOCK_M: tl.constexpr,
|
| 972 |
+
NUM_SINKHORN_ITERS: tl.constexpr,
|
| 973 |
+
ASYMMETRIC_EXP_DOMAIN: tl.constexpr,
|
| 974 |
+
hc_sinkhorn_eps: tl.constexpr,
|
| 975 |
+
):
|
| 976 |
+
"""Compute h_res = rsigma * alpha_res * acc_res + bias_res, optionally run
|
| 977 |
+
Sinkhorn-Knopp, and store to ``h_res_ptr`` (flattened (M, n²)).
|
| 978 |
+
|
| 979 |
+
Called from the dedicated res-stream CTA in
|
| 980 |
+
``_mhc_post_pre_reduce_apply_kernel``.
|
| 981 |
+
|
| 982 |
+
Sinkhorn variant selected by ``ASYMMETRIC_EXP_DOMAIN``:
|
| 983 |
+
False (default) → canonical log-domain Sinkhorn-Knopp: symmetric row/col
|
| 984 |
+
normalization, no eps perturbation.
|
| 985 |
+
True → HIP-compatible exp-domain Sinkhorn
|
| 986 |
+
(``mhc_kernels.cu:493-507``): first iter is asymmetric
|
| 987 |
+
(softmax(row) + eps, then div(col + eps)); remaining
|
| 988 |
+
``NUM_SINKHORN_ITERS - 1`` iters are symmetric div(row + eps) /
|
| 989 |
+
div(col + eps). ``hc_sinkhorn_eps`` is unused when False.
|
| 990 |
+
"""
|
| 991 |
+
bias_res = tl.load(
|
| 992 |
+
bias_ptr + rn_res_global,
|
| 993 |
+
mask=rn_res_local < n_squared,
|
| 994 |
+
other=0.0,
|
| 995 |
+
).to(tl.float32)
|
| 996 |
+
h_res = rsigma[:, None] * alpha_res * acc_res + bias_res[None, :]
|
| 997 |
+
|
| 998 |
+
if NUM_SINKHORN_ITERS > 0:
|
| 999 |
+
if ASYMMETRIC_EXP_DOMAIN:
|
| 1000 |
+
# Asymmetric first iter + (NUM_SINKHORN_ITERS - 1) symmetric iters,
|
| 1001 |
+
# mirroring HIP exactly.
|
| 1002 |
+
A = tl.reshape(h_res, (BLOCK_M, n, n))
|
| 1003 |
+
row_max = tl.max(A, axis=2)
|
| 1004 |
+
P = tl.exp(A - row_max[:, :, None])
|
| 1005 |
+
row_sum = tl.sum(P, axis=2)
|
| 1006 |
+
P = P / row_sum[:, :, None] + hc_sinkhorn_eps
|
| 1007 |
+
col_sum = tl.sum(P, axis=1)
|
| 1008 |
+
P = P / (col_sum[:, None, :] + hc_sinkhorn_eps)
|
| 1009 |
+
for _ in range(NUM_SINKHORN_ITERS - 1):
|
| 1010 |
+
row_sum = tl.sum(P, axis=2)
|
| 1011 |
+
P = P / (row_sum[:, :, None] + hc_sinkhorn_eps)
|
| 1012 |
+
col_sum = tl.sum(P, axis=1)
|
| 1013 |
+
P = P / (col_sum[:, None, :] + hc_sinkhorn_eps)
|
| 1014 |
+
out_res = tl.reshape(P, (BLOCK_M, n_squared))
|
| 1015 |
+
else:
|
| 1016 |
+
LOG2_E: tl.constexpr = 1.4426950408889634
|
| 1017 |
+
|
| 1018 |
+
log2_A = tl.reshape(h_res, (BLOCK_M, n, n)) * LOG2_E
|
| 1019 |
+
log2_u = tl.zeros((BLOCK_M, n), dtype=tl.float32)
|
| 1020 |
+
log2_v = tl.zeros((BLOCK_M, n), dtype=tl.float32)
|
| 1021 |
+
|
| 1022 |
+
for _ in range(NUM_SINKHORN_ITERS):
|
| 1023 |
+
scaled_row = log2_A + log2_v[:, None, :]
|
| 1024 |
+
row_max = tl.max(scaled_row, axis=2)
|
| 1025 |
+
exp_shifted = tl.exp2(scaled_row - row_max[:, :, None])
|
| 1026 |
+
row_sum_exp = tl.sum(exp_shifted, axis=2)
|
| 1027 |
+
log2_row_sums = row_max + tl.log2(row_sum_exp)
|
| 1028 |
+
log2_u = -log2_row_sums
|
| 1029 |
+
|
| 1030 |
+
scaled_col = log2_A + log2_u[:, :, None]
|
| 1031 |
+
col_max = tl.max(scaled_col, axis=1)
|
| 1032 |
+
exp_shifted = tl.exp2(scaled_col - col_max[:, None, :])
|
| 1033 |
+
col_sum_exp = tl.sum(exp_shifted, axis=1)
|
| 1034 |
+
log2_col_sums = col_max + tl.log2(col_sum_exp)
|
| 1035 |
+
log2_v = -log2_col_sums
|
| 1036 |
+
|
| 1037 |
+
log2_P = log2_A + log2_u[:, :, None] + log2_v[:, None, :]
|
| 1038 |
+
P = tl.exp2(log2_P)
|
| 1039 |
+
out_res = tl.reshape(P, (BLOCK_M, n_squared))
|
| 1040 |
+
else:
|
| 1041 |
+
out_res = h_res
|
| 1042 |
+
|
| 1043 |
+
tl.store(
|
| 1044 |
+
h_res_ptr + rm[:, None] * stride_hr_m + rn_res_local[None, :] * stride_hr_n,
|
| 1045 |
+
out_res,
|
| 1046 |
+
mask=(rm[:, None] < M) & (rn_res_local[None, :] < n_squared),
|
| 1047 |
+
)
|
| 1048 |
+
|
| 1049 |
+
|
| 1050 |
+
@triton.jit
|
| 1051 |
+
def _mhc_post_pre_reduce_apply_kernel(
|
| 1052 |
+
acc_ptr, # Unified split-K partials: (NUM_KSPLIT, M, n + n + n_squared), layout [pre | post | res]
|
| 1053 |
+
acc_sq_ptr, # Sum-of-squares partials: (NUM_KSPLIT, M)
|
| 1054 |
+
alpha_ptr, # (3,) fp32 — [alpha_pre, alpha_post, alpha_res]
|
| 1055 |
+
bias_ptr, # (n + n + n_squared,) fp32
|
| 1056 |
+
x_ptr, # (M, n*C)
|
| 1057 |
+
h_post_ptr, # (M, n) — written by the post CTA
|
| 1058 |
+
h_res_ptr, # (M, n*n) — written by the res CTA (flat n_squared view)
|
| 1059 |
+
layer_input_ptr, # (M, C) in x.dtype
|
| 1060 |
+
M,
|
| 1061 |
+
K: tl.constexpr,
|
| 1062 |
+
n: tl.constexpr,
|
| 1063 |
+
n_squared: tl.constexpr,
|
| 1064 |
+
C: tl.constexpr,
|
| 1065 |
+
eps: tl.constexpr,
|
| 1066 |
+
hc_pre_eps: tl.constexpr,
|
| 1067 |
+
hc_post_mult_value: tl.constexpr,
|
| 1068 |
+
stride_acc_k,
|
| 1069 |
+
stride_acc_m,
|
| 1070 |
+
stride_acc_n,
|
| 1071 |
+
stride_acc_sq_k,
|
| 1072 |
+
stride_acc_sq_m,
|
| 1073 |
+
stride_xm,
|
| 1074 |
+
stride_xk,
|
| 1075 |
+
stride_hp_m,
|
| 1076 |
+
stride_hp_n,
|
| 1077 |
+
stride_hr_m,
|
| 1078 |
+
stride_hr_n,
|
| 1079 |
+
stride_li_m,
|
| 1080 |
+
stride_li_c,
|
| 1081 |
+
BLOCK_M: tl.constexpr,
|
| 1082 |
+
BLOCK_C: tl.constexpr,
|
| 1083 |
+
N_POW2: tl.constexpr,
|
| 1084 |
+
N_POW2_RES: tl.constexpr,
|
| 1085 |
+
ACTUAL_KSPLIT: tl.constexpr,
|
| 1086 |
+
KSPLIT_POW2: tl.constexpr,
|
| 1087 |
+
BLOCK_M_POST_RES: tl.constexpr,
|
| 1088 |
+
NUM_SINKHORN_ITERS: tl.constexpr,
|
| 1089 |
+
ASYMMETRIC_EXP_DOMAIN: tl.constexpr,
|
| 1090 |
+
hc_sinkhorn_eps: tl.constexpr,
|
| 1091 |
+
):
|
| 1092 |
+
"""
|
| 1093 |
+
Reduce-and-apply kernel for the split-K mHC pipeline (Eq 15-19 + apply).
|
| 1094 |
+
|
| 1095 |
+
Grid: ``(cdiv(M, BLOCK_M), cdiv(C, BLOCK_C) + 2)``. For each ``pid_m``:
|
| 1096 |
+
|
| 1097 |
+
- ``pid_c < NUM_C_BLOCKS`` : pre reduce + RMS + apply-pre on this BLOCK_C
|
| 1098 |
+
slice. Writes ``layer_input[:, rc]``.
|
| 1099 |
+
- ``pid_c == NUM_C_BLOCKS`` : post stream only (``hc_post_mult_value *
|
| 1100 |
+
sigmoid``). Writes ``out[:, :n]``.
|
| 1101 |
+
- ``pid_c == NUM_C_BLOCKS+1``: res stream + log-domain Sinkhorn (when
|
| 1102 |
+
NUM_SINKHORN_ITERS > 0). Writes
|
| 1103 |
+
``out[:, n:n+n_squared]``.
|
| 1104 |
+
|
| 1105 |
+
The three branches share only ``rsigma`` (which each CTA recomputes from
|
| 1106 |
+
``acc_sq``). Compared to the earlier layout where the post and res CTAs
|
| 1107 |
+
were piggybacked onto ``pid_c == 0`` / ``pid_c == RES_PID_C`` and did
|
| 1108 |
+
apply-pre work on top, these dedicated CTAs do **only** their stream's
|
| 1109 |
+
activation — so the 20-iter Sinkhorn on the res CTA runs in parallel with
|
| 1110 |
+
apply-pre on the other ``NUM_C_BLOCKS`` CTAs rather than serializing
|
| 1111 |
+
behind it.
|
| 1112 |
+
|
| 1113 |
+
Sinkhorn requires ``n_squared`` to be a power of two; the wrapper enforces
|
| 1114 |
+
this when ``NUM_SINKHORN_ITERS > 0``.
|
| 1115 |
+
"""
|
| 1116 |
+
# pid_m = tl.program_id(0)
|
| 1117 |
+
# pid_c = tl.program_id(1)
|
| 1118 |
+
pid = tl.program_id(0)
|
| 1119 |
+
|
| 1120 |
+
NUM_C_BLOCKS = tl.cdiv(C, BLOCK_C)
|
| 1121 |
+
NUM_M_BLOCKS = tl.cdiv(M, BLOCK_M)
|
| 1122 |
+
NUM_M_BLOCKS_POST_RES = tl.cdiv(M, BLOCK_M_POST_RES)
|
| 1123 |
+
|
| 1124 |
+
K_INV: tl.constexpr = 1.0 / K
|
| 1125 |
+
# POST_PID == NUM_C_BLOCKS, RES_PID == NUM_C_BLOCKS + 1 (inlined below to
|
| 1126 |
+
# sidestep Triton's constexpr-arithmetic restriction on `: tl.constexpr =`
|
| 1127 |
+
# binding sites).
|
| 1128 |
+
|
| 1129 |
+
ks_offs = tl.arange(0, KSPLIT_POW2)
|
| 1130 |
+
if KSPLIT_POW2 != ACTUAL_KSPLIT:
|
| 1131 |
+
ks_mask = ks_offs < ACTUAL_KSPLIT
|
| 1132 |
+
else:
|
| 1133 |
+
ks_mask = tl.full((1,), 1, dtype=tl.int1)
|
| 1134 |
+
|
| 1135 |
+
if pid < NUM_M_BLOCKS * NUM_C_BLOCKS:
|
| 1136 |
+
# ---- Apply-pre branch ----
|
| 1137 |
+
pid_m = pid // NUM_C_BLOCKS
|
| 1138 |
+
pid_c = pid % NUM_C_BLOCKS
|
| 1139 |
+
rc = pid_c * BLOCK_C + tl.arange(0, BLOCK_C)
|
| 1140 |
+
m_offs = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 1141 |
+
m_mask = m_offs < M
|
| 1142 |
+
acc_sq = tl.load(
|
| 1143 |
+
acc_sq_ptr
|
| 1144 |
+
+ ks_offs[:, None] * stride_acc_sq_k
|
| 1145 |
+
+ m_offs[None, :] * stride_acc_sq_m,
|
| 1146 |
+
mask=ks_mask[:, None] & m_mask[None, :],
|
| 1147 |
+
other=0.0,
|
| 1148 |
+
)
|
| 1149 |
+
acc_sq = tl.sum(acc_sq, 0)
|
| 1150 |
+
rsigma = tl.math.rsqrt((acc_sq * K_INV) + eps)
|
| 1151 |
+
|
| 1152 |
+
rn_pre = tl.arange(0, N_POW2)
|
| 1153 |
+
|
| 1154 |
+
acc_pre = tl.load(
|
| 1155 |
+
acc_ptr
|
| 1156 |
+
+ ks_offs[:, None, None] * stride_acc_k
|
| 1157 |
+
+ m_offs[None, :, None] * stride_acc_m
|
| 1158 |
+
+ rn_pre[None, None, :] * stride_acc_n,
|
| 1159 |
+
mask=ks_mask[:, None, None]
|
| 1160 |
+
& m_mask[None, :, None]
|
| 1161 |
+
& (rn_pre[None, None, :] < n),
|
| 1162 |
+
other=0.0,
|
| 1163 |
+
)
|
| 1164 |
+
acc_pre = tl.sum(acc_pre, 0)
|
| 1165 |
+
|
| 1166 |
+
alpha_pre = tl.load(alpha_ptr + 0)
|
| 1167 |
+
bias_pre = tl.load(bias_ptr + rn_pre, mask=rn_pre < n, other=0.0).to(tl.float32)
|
| 1168 |
+
h_pre = rsigma[:, None] * alpha_pre * acc_pre + bias_pre[None, :]
|
| 1169 |
+
pre_mix_2d = tl.sigmoid(h_pre) + hc_pre_eps
|
| 1170 |
+
|
| 1171 |
+
_mhc_apply_pre_mix_tile(
|
| 1172 |
+
x_ptr,
|
| 1173 |
+
layer_input_ptr,
|
| 1174 |
+
pre_mix_2d,
|
| 1175 |
+
m_offs,
|
| 1176 |
+
rc,
|
| 1177 |
+
rn_pre,
|
| 1178 |
+
M,
|
| 1179 |
+
C,
|
| 1180 |
+
n,
|
| 1181 |
+
stride_xm,
|
| 1182 |
+
stride_xk,
|
| 1183 |
+
stride_li_m,
|
| 1184 |
+
stride_li_c,
|
| 1185 |
+
)
|
| 1186 |
+
elif pid < NUM_M_BLOCKS * NUM_C_BLOCKS + NUM_M_BLOCKS_POST_RES:
|
| 1187 |
+
pid = pid - NUM_M_BLOCKS * NUM_C_BLOCKS
|
| 1188 |
+
m_offs_post_res = pid * BLOCK_M_POST_RES + tl.arange(0, BLOCK_M_POST_RES)
|
| 1189 |
+
m_mask_post_res = m_offs_post_res < M
|
| 1190 |
+
acc_sq_post_res = tl.load(
|
| 1191 |
+
acc_sq_ptr
|
| 1192 |
+
+ ks_offs[:, None] * stride_acc_sq_k
|
| 1193 |
+
+ m_offs_post_res[None, :] * stride_acc_sq_m,
|
| 1194 |
+
mask=ks_mask[:, None] & m_mask_post_res[None, :],
|
| 1195 |
+
other=0.0,
|
| 1196 |
+
)
|
| 1197 |
+
acc_sq_post_res = tl.sum(acc_sq_post_res, 0)
|
| 1198 |
+
rsigma_post_res = tl.math.rsqrt((acc_sq_post_res * K_INV) + eps)
|
| 1199 |
+
# ---- Post stream branch (pid_c == NUM_C_BLOCKS) ----
|
| 1200 |
+
rn_post_local = tl.arange(0, N_POW2)
|
| 1201 |
+
rn_post_global = n + rn_post_local
|
| 1202 |
+
|
| 1203 |
+
acc_post = tl.load(
|
| 1204 |
+
acc_ptr
|
| 1205 |
+
+ ks_offs[:, None, None] * stride_acc_k
|
| 1206 |
+
+ m_offs_post_res[None, :, None] * stride_acc_m
|
| 1207 |
+
+ rn_post_global[None, None, :] * stride_acc_n,
|
| 1208 |
+
mask=ks_mask[:, None, None]
|
| 1209 |
+
& m_mask_post_res[None, :, None]
|
| 1210 |
+
& (rn_post_local[None, None, :] < n),
|
| 1211 |
+
other=0.0,
|
| 1212 |
+
)
|
| 1213 |
+
acc_post = tl.sum(acc_post, 0)
|
| 1214 |
+
|
| 1215 |
+
alpha_post = tl.load(alpha_ptr + 1)
|
| 1216 |
+
bias_post = tl.load(
|
| 1217 |
+
bias_ptr + rn_post_global, mask=rn_post_local < n, other=0.0
|
| 1218 |
+
).to(tl.float32)
|
| 1219 |
+
h_post = rsigma_post_res[:, None] * alpha_post * acc_post + bias_post[None, :]
|
| 1220 |
+
out_post = tl.sigmoid(h_post) * hc_post_mult_value
|
| 1221 |
+
tl.store(
|
| 1222 |
+
h_post_ptr
|
| 1223 |
+
+ m_offs_post_res[:, None] * stride_hp_m
|
| 1224 |
+
+ rn_post_local[None, :] * stride_hp_n,
|
| 1225 |
+
out_post,
|
| 1226 |
+
mask=m_mask_post_res[:, None] & (rn_post_local[None, :] < n),
|
| 1227 |
+
)
|
| 1228 |
+
else:
|
| 1229 |
+
pid = pid - NUM_M_BLOCKS * NUM_C_BLOCKS - NUM_M_BLOCKS_POST_RES
|
| 1230 |
+
m_offs_post_res = pid * BLOCK_M_POST_RES + tl.arange(0, BLOCK_M_POST_RES)
|
| 1231 |
+
m_mask_post_res = m_offs_post_res < M
|
| 1232 |
+
acc_sq_post_res = tl.load(
|
| 1233 |
+
acc_sq_ptr
|
| 1234 |
+
+ ks_offs[:, None] * stride_acc_sq_k
|
| 1235 |
+
+ m_offs_post_res[None, :] * stride_acc_sq_m,
|
| 1236 |
+
mask=ks_mask[:, None] & m_mask_post_res[None, :],
|
| 1237 |
+
other=0.0,
|
| 1238 |
+
)
|
| 1239 |
+
acc_sq_post_res = tl.sum(acc_sq_post_res, 0)
|
| 1240 |
+
rsigma_post_res = tl.math.rsqrt((acc_sq_post_res * K_INV) + eps)
|
| 1241 |
+
# ---- Res stream + Sinkhorn branch (pid_c == NUM_C_BLOCKS + 1) ----
|
| 1242 |
+
rn_res_local = tl.arange(0, N_POW2_RES)
|
| 1243 |
+
rn_res_global = 2 * n + rn_res_local
|
| 1244 |
+
|
| 1245 |
+
acc_res = tl.load(
|
| 1246 |
+
acc_ptr
|
| 1247 |
+
+ ks_offs[:, None, None] * stride_acc_k
|
| 1248 |
+
+ m_offs_post_res[None, :, None] * stride_acc_m
|
| 1249 |
+
+ rn_res_global[None, None, :] * stride_acc_n,
|
| 1250 |
+
mask=ks_mask[:, None, None]
|
| 1251 |
+
& m_mask_post_res[None, :, None]
|
| 1252 |
+
& (rn_res_local[None, None, :] < n_squared),
|
| 1253 |
+
other=0.0,
|
| 1254 |
+
)
|
| 1255 |
+
acc_res = tl.sum(acc_res, 0)
|
| 1256 |
+
alpha_res = tl.load(alpha_ptr + 2)
|
| 1257 |
+
|
| 1258 |
+
_mhc_post_pre_reduce_apply_res_block(
|
| 1259 |
+
acc_res,
|
| 1260 |
+
rsigma_post_res,
|
| 1261 |
+
m_offs_post_res,
|
| 1262 |
+
rn_res_local,
|
| 1263 |
+
rn_res_global,
|
| 1264 |
+
alpha_res,
|
| 1265 |
+
bias_ptr,
|
| 1266 |
+
h_res_ptr,
|
| 1267 |
+
M,
|
| 1268 |
+
n,
|
| 1269 |
+
n_squared,
|
| 1270 |
+
N_POW2_RES,
|
| 1271 |
+
stride_hr_m,
|
| 1272 |
+
stride_hr_n,
|
| 1273 |
+
BLOCK_M_POST_RES,
|
| 1274 |
+
NUM_SINKHORN_ITERS,
|
| 1275 |
+
ASYMMETRIC_EXP_DOMAIN,
|
| 1276 |
+
hc_sinkhorn_eps,
|
| 1277 |
+
)
|
build/torch-rocm/_triton_kernels/gated_delta_rule/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Gated Delta Rule Operations (Forward Only).
|
| 7 |
+
|
| 8 |
+
This module provides optimized Triton kernels for gated delta rule computations.
|
| 9 |
+
|
| 10 |
+
Available operations:
|
| 11 |
+
- Fused recurrent forward: _fused_recurrent_gated_delta_rule_fwd_kernel
|
| 12 |
+
- Chunk-based forward: chunk_gated_delta_rule_fwd
|
| 13 |
+
- Hidden state computation: chunk_gated_delta_rule_fwd_h
|
| 14 |
+
- Output computation: chunk_fwd_o
|
| 15 |
+
|
| 16 |
+
Note: Only forward pass is implemented. Backward pass is not supported in aiter.
|
| 17 |
+
For training with gradients, please use the flash-linear-attention library.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from .decode.fused_recurrent import _fused_recurrent_gated_delta_rule_fwd_kernel
|
| 21 |
+
from .prefill.chunk import (
|
| 22 |
+
chunk_gated_delta_rule_fwd,
|
| 23 |
+
chunk_gated_delta_rule_fwd_opt,
|
| 24 |
+
chunk_gated_delta_rule_fwd_opt_vk,
|
| 25 |
+
)
|
| 26 |
+
from .prefill.chunk_delta_h import (
|
| 27 |
+
chunk_gated_delta_rule_fwd_h,
|
| 28 |
+
chunk_gated_delta_rule_fwd_h_opt,
|
| 29 |
+
chunk_gated_delta_rule_fwd_h_opt_vk,
|
| 30 |
+
)
|
| 31 |
+
from .prefill.chunk_o import chunk_fwd_o, chunk_fwd_o_opt, chunk_fwd_o_opt_vk
|
| 32 |
+
from .prefill.fused_cumsum_kkt import fused_chunk_local_cumsum_scaled_dot_kkt_fwd
|
| 33 |
+
from .prefill.fused_solve_tril_recompute import fused_solve_tril_recompute_w_u
|
| 34 |
+
from . import gated_delta_rule_utils
|
| 35 |
+
|
| 36 |
+
__all__ = [
|
| 37 |
+
"_fused_recurrent_gated_delta_rule_fwd_kernel",
|
| 38 |
+
"chunk_gated_delta_rule_fwd",
|
| 39 |
+
"chunk_gated_delta_rule_fwd_opt",
|
| 40 |
+
"chunk_gated_delta_rule_fwd_opt_vk",
|
| 41 |
+
"chunk_gated_delta_rule_fwd_h",
|
| 42 |
+
"chunk_gated_delta_rule_fwd_h_opt",
|
| 43 |
+
"chunk_gated_delta_rule_fwd_h_opt_vk",
|
| 44 |
+
"chunk_fwd_o",
|
| 45 |
+
"chunk_fwd_o_opt",
|
| 46 |
+
"chunk_fwd_o_opt_vk",
|
| 47 |
+
"fused_chunk_local_cumsum_scaled_dot_kkt_fwd",
|
| 48 |
+
"fused_solve_tril_recompute_w_u",
|
| 49 |
+
"gated_delta_rule_utils",
|
| 50 |
+
]
|
build/torch-rocm/_triton_kernels/gated_delta_rule/decode/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Gated Delta Rule Decode Operations (Forward Only).
|
| 7 |
+
|
| 8 |
+
This module provides optimized Triton kernels for decode/inference operations.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from .fused_rearrange_sigmoid_gdr import (
|
| 12 |
+
fused_rearrange_sigmoid_gated_delta_rule_update_kernel,
|
| 13 |
+
)
|
| 14 |
+
from .fused_recurrent import _fused_recurrent_gated_delta_rule_fwd_kernel
|
| 15 |
+
from .fused_sigmoid_gating_recurrent import fused_sigmoid_gating_delta_rule_update
|
| 16 |
+
|
| 17 |
+
__all__ = [
|
| 18 |
+
"_fused_recurrent_gated_delta_rule_fwd_kernel",
|
| 19 |
+
"fused_rearrange_sigmoid_gated_delta_rule_update_kernel",
|
| 20 |
+
"fused_sigmoid_gating_delta_rule_update",
|
| 21 |
+
]
|
build/torch-rocm/_triton_kernels/gated_delta_rule/decode/causal_conv1d_split_qkv.py
ADDED
|
@@ -0,0 +1,1098 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Optimized causal_conv1d_update: directly output split q/k/v."""
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.experimental.gluon.language as gl
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
from triton.experimental import gluon
|
| 8 |
+
|
| 9 |
+
PAD_SLOT_ID = -1
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@triton.jit()
|
| 13 |
+
def _causal_conv1d_update_split_qkv_kernel(
|
| 14 |
+
# Pointers to matrices
|
| 15 |
+
x_ptr, # (batch, dim, seqlen) where dim = 2*key_dim + value_dim
|
| 16 |
+
w_ptr, # (dim, width)
|
| 17 |
+
bias_ptr,
|
| 18 |
+
conv_state_ptr,
|
| 19 |
+
conv_state_indices_ptr,
|
| 20 |
+
q_ptr,
|
| 21 |
+
k_ptr,
|
| 22 |
+
v_ptr,
|
| 23 |
+
key_dim: tl.constexpr,
|
| 24 |
+
value_dim: tl.constexpr,
|
| 25 |
+
# Matrix dimensions
|
| 26 |
+
batch: int,
|
| 27 |
+
dim: tl.constexpr,
|
| 28 |
+
seqlen: tl.constexpr,
|
| 29 |
+
state_len: tl.constexpr,
|
| 30 |
+
num_cache_lines: tl.constexpr,
|
| 31 |
+
# Strides
|
| 32 |
+
stride_x_seq: tl.constexpr,
|
| 33 |
+
stride_x_dim: tl.constexpr,
|
| 34 |
+
stride_x_token: tl.constexpr,
|
| 35 |
+
stride_w_dim: tl.constexpr,
|
| 36 |
+
stride_w_width: tl.constexpr,
|
| 37 |
+
stride_conv_state_seq: tl.constexpr,
|
| 38 |
+
stride_conv_state_dim: tl.constexpr,
|
| 39 |
+
stride_conv_state_tok: tl.constexpr,
|
| 40 |
+
stride_state_indices: tl.constexpr,
|
| 41 |
+
stride_q_seq: tl.constexpr,
|
| 42 |
+
stride_q_dim: tl.constexpr,
|
| 43 |
+
stride_q_token: tl.constexpr,
|
| 44 |
+
stride_k_seq: tl.constexpr,
|
| 45 |
+
stride_k_dim: tl.constexpr,
|
| 46 |
+
stride_k_token: tl.constexpr,
|
| 47 |
+
stride_v_seq: tl.constexpr,
|
| 48 |
+
stride_v_dim: tl.constexpr,
|
| 49 |
+
stride_v_token: tl.constexpr,
|
| 50 |
+
# others
|
| 51 |
+
pad_slot_id: tl.constexpr,
|
| 52 |
+
# Meta-parameters
|
| 53 |
+
HAS_BIAS: tl.constexpr,
|
| 54 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 55 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 56 |
+
IS_CONTINUOUS_BATCHING: tl.constexpr,
|
| 57 |
+
NP2_STATELEN: tl.constexpr,
|
| 58 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 59 |
+
BLOCK_N: tl.constexpr,
|
| 60 |
+
):
|
| 61 |
+
idx_seq = tl.program_id(0)
|
| 62 |
+
if idx_seq >= batch:
|
| 63 |
+
return
|
| 64 |
+
|
| 65 |
+
# [BLOCK_N,] elements along the feature-dimension (channel)
|
| 66 |
+
idx_feats = tl.program_id(1) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 67 |
+
|
| 68 |
+
if IS_CONTINUOUS_BATCHING:
|
| 69 |
+
conv_state_batch_coord = tl.load(
|
| 70 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices
|
| 71 |
+
).to(tl.int64)
|
| 72 |
+
else:
|
| 73 |
+
conv_state_batch_coord = idx_seq
|
| 74 |
+
|
| 75 |
+
if USE_PAD_SLOT:
|
| 76 |
+
if conv_state_batch_coord == pad_slot_id:
|
| 77 |
+
return
|
| 78 |
+
|
| 79 |
+
# STEP 1: READ init_state data
|
| 80 |
+
conv_states_base = (
|
| 81 |
+
conv_state_ptr
|
| 82 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 83 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 84 |
+
)
|
| 85 |
+
mask_w = idx_feats < dim
|
| 86 |
+
|
| 87 |
+
prior_tokens = conv_states_base
|
| 88 |
+
if KERNEL_WIDTH >= 2:
|
| 89 |
+
conv_states_ptrs = prior_tokens
|
| 90 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 91 |
+
if KERNEL_WIDTH >= 3:
|
| 92 |
+
conv_states_ptrs = prior_tokens + 1 * stride_conv_state_tok
|
| 93 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 94 |
+
if KERNEL_WIDTH >= 4:
|
| 95 |
+
conv_states_ptrs = prior_tokens + 2 * stride_conv_state_tok
|
| 96 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 97 |
+
|
| 98 |
+
# STEP 2: Update conv state (same as original)
|
| 99 |
+
idx_tokens = tl.arange(0, NP2_STATELEN)
|
| 100 |
+
|
| 101 |
+
conv_state_ptrs_source = (
|
| 102 |
+
conv_state_ptr
|
| 103 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 104 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 105 |
+
+ ((idx_tokens + seqlen) * stride_conv_state_tok)[:, None]
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
mask = (
|
| 109 |
+
(conv_state_batch_coord < num_cache_lines)
|
| 110 |
+
& ((idx_tokens + seqlen) < state_len)[:, None]
|
| 111 |
+
& (idx_feats < dim)[None, :]
|
| 112 |
+
)
|
| 113 |
+
conv_state = tl.load(conv_state_ptrs_source, mask, other=0.0)
|
| 114 |
+
|
| 115 |
+
VAL = state_len - seqlen
|
| 116 |
+
x_base = x_ptr + (idx_seq * stride_x_seq) + (idx_feats * stride_x_dim)
|
| 117 |
+
x_ptrs = x_base[None, :] + ((idx_tokens - VAL) * stride_x_token)[:, None]
|
| 118 |
+
|
| 119 |
+
mask_x = (
|
| 120 |
+
(idx_tokens - VAL >= 0)[:, None]
|
| 121 |
+
& (idx_tokens - VAL < seqlen)[:, None]
|
| 122 |
+
& (idx_feats < dim)[None, :]
|
| 123 |
+
)
|
| 124 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 125 |
+
tl.debug_barrier()
|
| 126 |
+
|
| 127 |
+
new_conv_state = tl.where(mask, conv_state, loaded_x)
|
| 128 |
+
|
| 129 |
+
conv_state_base = (
|
| 130 |
+
conv_state_ptr
|
| 131 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 132 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 133 |
+
)
|
| 134 |
+
conv_state_ptrs_target = (
|
| 135 |
+
conv_state_base + (idx_tokens * stride_conv_state_tok)[:, None]
|
| 136 |
+
)
|
| 137 |
+
mask_store = (idx_tokens < state_len)[:, None] & (idx_feats < dim)[None, :]
|
| 138 |
+
tl.store(conv_state_ptrs_target, new_conv_state, mask_store)
|
| 139 |
+
|
| 140 |
+
# STEP 3: init accumulator
|
| 141 |
+
if HAS_BIAS:
|
| 142 |
+
bias = bias_ptr + idx_feats
|
| 143 |
+
mask_bias = idx_feats < dim
|
| 144 |
+
acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(tl.float32)
|
| 145 |
+
else:
|
| 146 |
+
acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 147 |
+
|
| 148 |
+
# STEP 4: PRE-LOAD WEIGHTS
|
| 149 |
+
w_base = w_ptr + (idx_feats * stride_w_dim)
|
| 150 |
+
mask_w = idx_feats < dim
|
| 151 |
+
if KERNEL_WIDTH >= 2:
|
| 152 |
+
w_ptrs = w_base + (0 * stride_w_width)
|
| 153 |
+
w_col0 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 154 |
+
w_ptrs = w_base + (1 * stride_w_width)
|
| 155 |
+
w_col1 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 156 |
+
if KERNEL_WIDTH >= 3:
|
| 157 |
+
w_ptrs = w_base + (2 * stride_w_width)
|
| 158 |
+
w_col2 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 159 |
+
if KERNEL_WIDTH >= 4:
|
| 160 |
+
w_ptrs = w_base + (3 * stride_w_width)
|
| 161 |
+
w_col3 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 162 |
+
|
| 163 |
+
x_base_1d = x_base
|
| 164 |
+
mask_x_1d = idx_feats < dim
|
| 165 |
+
|
| 166 |
+
# STEP 5: compute each token and write to split buffers
|
| 167 |
+
for idx_token in tl.static_range(seqlen):
|
| 168 |
+
acc = acc_preload
|
| 169 |
+
|
| 170 |
+
matrix_w = w_col0
|
| 171 |
+
matrix_x = col0
|
| 172 |
+
for j in tl.static_range(KERNEL_WIDTH):
|
| 173 |
+
if KERNEL_WIDTH == 2:
|
| 174 |
+
if j == 1:
|
| 175 |
+
matrix_w = w_col1
|
| 176 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 177 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 178 |
+
elif KERNEL_WIDTH == 3:
|
| 179 |
+
if j == 1:
|
| 180 |
+
matrix_w = w_col1
|
| 181 |
+
matrix_x = col1
|
| 182 |
+
elif j == 2:
|
| 183 |
+
matrix_w = w_col2
|
| 184 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 185 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 186 |
+
elif KERNEL_WIDTH == 4:
|
| 187 |
+
if j == 1:
|
| 188 |
+
matrix_w = w_col1
|
| 189 |
+
matrix_x = col1
|
| 190 |
+
elif j == 2:
|
| 191 |
+
matrix_w = w_col2
|
| 192 |
+
matrix_x = col2
|
| 193 |
+
elif j == 3:
|
| 194 |
+
matrix_w = w_col3
|
| 195 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 196 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 197 |
+
|
| 198 |
+
acc += matrix_x * matrix_w
|
| 199 |
+
|
| 200 |
+
# Update sliding window
|
| 201 |
+
if KERNEL_WIDTH == 2:
|
| 202 |
+
col0 = matrix_x
|
| 203 |
+
elif KERNEL_WIDTH == 3:
|
| 204 |
+
col0 = col1
|
| 205 |
+
col1 = matrix_x
|
| 206 |
+
elif KERNEL_WIDTH == 4:
|
| 207 |
+
col0 = col1
|
| 208 |
+
col1 = col2
|
| 209 |
+
col2 = matrix_x
|
| 210 |
+
|
| 211 |
+
# Apply activation
|
| 212 |
+
if SILU_ACTIVATION:
|
| 213 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 214 |
+
|
| 215 |
+
mask_feat = (idx_token < seqlen) & (idx_feats < dim)
|
| 216 |
+
|
| 217 |
+
# Query: idx_feats in [0, key_dim)
|
| 218 |
+
is_query = idx_feats < key_dim
|
| 219 |
+
q_feat_idx = idx_feats # 0-based index within query
|
| 220 |
+
q_ptrs = (
|
| 221 |
+
q_ptr
|
| 222 |
+
+ idx_seq * stride_q_seq
|
| 223 |
+
+ idx_token * stride_q_token
|
| 224 |
+
+ q_feat_idx * stride_q_dim
|
| 225 |
+
)
|
| 226 |
+
tl.store(q_ptrs, acc, mask=mask_feat & is_query)
|
| 227 |
+
|
| 228 |
+
# Key: idx_feats in [key_dim, 2*key_dim)
|
| 229 |
+
is_key = (idx_feats >= key_dim) & (idx_feats < 2 * key_dim)
|
| 230 |
+
k_feat_idx = idx_feats - key_dim
|
| 231 |
+
k_ptrs = (
|
| 232 |
+
k_ptr
|
| 233 |
+
+ idx_seq * stride_k_seq
|
| 234 |
+
+ idx_token * stride_k_token
|
| 235 |
+
+ k_feat_idx * stride_k_dim
|
| 236 |
+
)
|
| 237 |
+
tl.store(k_ptrs, acc, mask=mask_feat & is_key)
|
| 238 |
+
|
| 239 |
+
# Value: idx_feats in [2*key_dim, 2*key_dim+value_dim)
|
| 240 |
+
is_value = (idx_feats >= 2 * key_dim) & (idx_feats < 2 * key_dim + value_dim)
|
| 241 |
+
v_feat_idx = idx_feats - 2 * key_dim
|
| 242 |
+
v_ptrs = (
|
| 243 |
+
v_ptr
|
| 244 |
+
+ idx_seq * stride_v_seq
|
| 245 |
+
+ idx_token * stride_v_token
|
| 246 |
+
+ v_feat_idx * stride_v_dim
|
| 247 |
+
)
|
| 248 |
+
tl.store(v_ptrs, acc, mask=mask_feat & is_value)
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
@tl.core.builtin
|
| 252 |
+
def load_conv_weights(
|
| 253 |
+
w_base,
|
| 254 |
+
feats,
|
| 255 |
+
stride_w_dim,
|
| 256 |
+
stride_w_width,
|
| 257 |
+
conv_width: int,
|
| 258 |
+
mask=None,
|
| 259 |
+
other=0.0,
|
| 260 |
+
_semantic=None,
|
| 261 |
+
) -> tl.tuple:
|
| 262 |
+
weights = [
|
| 263 |
+
tl.load(w_base + i * stride_w_width, mask=mask, other=other)
|
| 264 |
+
for i in range(conv_width)
|
| 265 |
+
]
|
| 266 |
+
return tl.tuple(weights)
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
@tl.core.builtin
|
| 270 |
+
def load_conv_states(
|
| 271 |
+
conv_state_ptr,
|
| 272 |
+
feats,
|
| 273 |
+
stride_conv_state_dim,
|
| 274 |
+
stride_conv_state_tok,
|
| 275 |
+
conv_width: int,
|
| 276 |
+
mask=None,
|
| 277 |
+
other=0.0,
|
| 278 |
+
_semantic=None,
|
| 279 |
+
) -> tl.tuple:
|
| 280 |
+
states = [
|
| 281 |
+
tl.load(conv_state_ptr + i * stride_conv_state_tok, mask=mask, other=other)
|
| 282 |
+
for i in range(conv_width)
|
| 283 |
+
]
|
| 284 |
+
return tl.tuple(states)
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
@triton.jit()
|
| 288 |
+
def _causal_conv1d_update_split_qkv_kernel_v2(
|
| 289 |
+
# Pointers to matrices
|
| 290 |
+
x_ptr, # (batch, dim, seqlen) where dim = 2*key_dim + value_dim
|
| 291 |
+
w_ptr, # (dim, width)
|
| 292 |
+
bias_ptr,
|
| 293 |
+
conv_state_ptr,
|
| 294 |
+
conv_state_indices_ptr,
|
| 295 |
+
q_ptr,
|
| 296 |
+
k_ptr,
|
| 297 |
+
v_ptr,
|
| 298 |
+
key_dim: tl.constexpr,
|
| 299 |
+
value_dim: tl.constexpr,
|
| 300 |
+
# Matrix dimensions
|
| 301 |
+
batch: int,
|
| 302 |
+
dim: tl.constexpr,
|
| 303 |
+
seqlen: tl.constexpr,
|
| 304 |
+
state_len: tl.constexpr,
|
| 305 |
+
num_cache_lines: tl.constexpr,
|
| 306 |
+
# Strides
|
| 307 |
+
stride_x_seq: tl.constexpr,
|
| 308 |
+
stride_x_dim: tl.constexpr,
|
| 309 |
+
stride_x_token: tl.constexpr,
|
| 310 |
+
stride_w_dim: tl.constexpr,
|
| 311 |
+
stride_w_width: tl.constexpr,
|
| 312 |
+
stride_conv_state_seq: tl.constexpr,
|
| 313 |
+
stride_conv_state_dim: tl.constexpr,
|
| 314 |
+
stride_conv_state_tok: tl.constexpr,
|
| 315 |
+
stride_state_indices: tl.constexpr,
|
| 316 |
+
stride_q_seq: tl.constexpr,
|
| 317 |
+
stride_q_dim: tl.constexpr,
|
| 318 |
+
stride_q_token: tl.constexpr,
|
| 319 |
+
stride_k_seq: tl.constexpr,
|
| 320 |
+
stride_k_dim: tl.constexpr,
|
| 321 |
+
stride_k_token: tl.constexpr,
|
| 322 |
+
stride_v_seq: tl.constexpr,
|
| 323 |
+
stride_v_dim: tl.constexpr,
|
| 324 |
+
stride_v_token: tl.constexpr,
|
| 325 |
+
# others
|
| 326 |
+
pad_slot_id: tl.constexpr,
|
| 327 |
+
# Meta-parameters
|
| 328 |
+
HAS_BIAS: tl.constexpr,
|
| 329 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 330 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 331 |
+
IS_CONTINUOUS_BATCHING: tl.constexpr,
|
| 332 |
+
NP2_STATELEN: tl.constexpr,
|
| 333 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 334 |
+
BLOCK_N: tl.constexpr,
|
| 335 |
+
):
|
| 336 |
+
cu_idx = tl.program_id(0)
|
| 337 |
+
dim_idx = cu_idx % (dim // BLOCK_N)
|
| 338 |
+
batch_idx = cu_idx // (dim // BLOCK_N)
|
| 339 |
+
num_cus = 80
|
| 340 |
+
batch_cus = num_cus // (dim // BLOCK_N)
|
| 341 |
+
per_cu_batchs = batch // batch_cus
|
| 342 |
+
cu_mores = batch % batch_cus
|
| 343 |
+
|
| 344 |
+
# ??:???????1,????-1;??????0 -> ??+1????1/0
|
| 345 |
+
# x = x - thresh
|
| 346 |
+
# t = (x >> (x.bit_length() - 1)) ^ 1
|
| 347 |
+
# r = val0 + (val1 - val0) * t
|
| 348 |
+
cu_tasks = (per_cu_batchs + 1) if batch_idx < cu_mores else per_cu_batchs
|
| 349 |
+
cu_offs = 0 if batch_idx < cu_mores else cu_mores
|
| 350 |
+
|
| 351 |
+
idx_feats = dim_idx * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 352 |
+
|
| 353 |
+
w_base = w_ptr + (idx_feats * stride_w_dim)
|
| 354 |
+
mask_w = idx_feats < dim
|
| 355 |
+
if KERNEL_WIDTH >= 2:
|
| 356 |
+
w_ptrs = w_base + (0 * stride_w_width)
|
| 357 |
+
w_col0 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 358 |
+
w_ptrs = w_base + (1 * stride_w_width)
|
| 359 |
+
w_col1 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 360 |
+
if KERNEL_WIDTH >= 3:
|
| 361 |
+
w_ptrs = w_base + (2 * stride_w_width)
|
| 362 |
+
w_col2 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 363 |
+
if KERNEL_WIDTH >= 4:
|
| 364 |
+
w_ptrs = w_base + (3 * stride_w_width)
|
| 365 |
+
w_col3 = tl.load(w_ptrs, mask_w, other=0.0)
|
| 366 |
+
|
| 367 |
+
for task_idx in range(cu_tasks):
|
| 368 |
+
idx_seq = batch_idx * cu_tasks + task_idx + cu_offs
|
| 369 |
+
|
| 370 |
+
if IS_CONTINUOUS_BATCHING:
|
| 371 |
+
conv_state_batch_coord = tl.load(
|
| 372 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices
|
| 373 |
+
).to(tl.int64)
|
| 374 |
+
else:
|
| 375 |
+
conv_state_batch_coord = idx_seq
|
| 376 |
+
|
| 377 |
+
if USE_PAD_SLOT:
|
| 378 |
+
if conv_state_batch_coord != pad_slot_id:
|
| 379 |
+
|
| 380 |
+
# STEP 1: READ init_state data
|
| 381 |
+
conv_states_base = (
|
| 382 |
+
conv_state_ptr
|
| 383 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 384 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 385 |
+
)
|
| 386 |
+
mask_w = idx_feats < dim
|
| 387 |
+
|
| 388 |
+
prior_tokens = conv_states_base
|
| 389 |
+
if KERNEL_WIDTH >= 2:
|
| 390 |
+
conv_states_ptrs = prior_tokens
|
| 391 |
+
col0 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 392 |
+
if KERNEL_WIDTH >= 3:
|
| 393 |
+
conv_states_ptrs = prior_tokens + 1 * stride_conv_state_tok
|
| 394 |
+
col1 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 395 |
+
if KERNEL_WIDTH >= 4:
|
| 396 |
+
conv_states_ptrs = prior_tokens + 2 * stride_conv_state_tok
|
| 397 |
+
col2 = tl.load(conv_states_ptrs, mask_w, 0.0)
|
| 398 |
+
|
| 399 |
+
# STEP 2: Update conv state (same as original)
|
| 400 |
+
idx_tokens = tl.arange(0, NP2_STATELEN)
|
| 401 |
+
|
| 402 |
+
conv_state_ptrs_source = (
|
| 403 |
+
conv_state_ptr
|
| 404 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 405 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 406 |
+
+ ((idx_tokens + seqlen) * stride_conv_state_tok)[:, None]
|
| 407 |
+
)
|
| 408 |
+
|
| 409 |
+
mask = (
|
| 410 |
+
(conv_state_batch_coord < num_cache_lines)
|
| 411 |
+
& ((idx_tokens + seqlen) < state_len)[:, None]
|
| 412 |
+
& (idx_feats < dim)[None, :]
|
| 413 |
+
)
|
| 414 |
+
conv_state = tl.load(conv_state_ptrs_source, mask, other=0.0)
|
| 415 |
+
|
| 416 |
+
VAL = state_len - seqlen
|
| 417 |
+
x_base = x_ptr + (idx_seq * stride_x_seq) + (idx_feats * stride_x_dim)
|
| 418 |
+
x_ptrs = (
|
| 419 |
+
x_base[None, :] + ((idx_tokens - VAL) * stride_x_token)[:, None]
|
| 420 |
+
)
|
| 421 |
+
|
| 422 |
+
mask_x = (
|
| 423 |
+
(idx_tokens - VAL >= 0)[:, None]
|
| 424 |
+
& (idx_tokens - VAL < seqlen)[:, None]
|
| 425 |
+
& (idx_feats < dim)[None, :]
|
| 426 |
+
)
|
| 427 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 428 |
+
tl.debug_barrier()
|
| 429 |
+
|
| 430 |
+
new_conv_state = tl.where(mask, conv_state, loaded_x)
|
| 431 |
+
|
| 432 |
+
conv_state_base = (
|
| 433 |
+
conv_state_ptr
|
| 434 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 435 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 436 |
+
)
|
| 437 |
+
conv_state_ptrs_target = (
|
| 438 |
+
conv_state_base + (idx_tokens * stride_conv_state_tok)[:, None]
|
| 439 |
+
)
|
| 440 |
+
mask_store = (idx_tokens < state_len)[:, None] & (idx_feats < dim)[
|
| 441 |
+
None, :
|
| 442 |
+
]
|
| 443 |
+
tl.store(conv_state_ptrs_target, new_conv_state, mask_store)
|
| 444 |
+
|
| 445 |
+
# STEP 3: init accumulator
|
| 446 |
+
if HAS_BIAS:
|
| 447 |
+
bias = bias_ptr + idx_feats
|
| 448 |
+
mask_bias = idx_feats < dim
|
| 449 |
+
acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(
|
| 450 |
+
tl.float32
|
| 451 |
+
)
|
| 452 |
+
else:
|
| 453 |
+
acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 454 |
+
|
| 455 |
+
# STEP 4: PRE-LOAD WEIGHTS
|
| 456 |
+
x_base_1d = x_base
|
| 457 |
+
mask_x_1d = idx_feats < dim
|
| 458 |
+
|
| 459 |
+
# STEP 5: compute each token and write to split buffers
|
| 460 |
+
for idx_token in tl.static_range(seqlen):
|
| 461 |
+
acc = acc_preload
|
| 462 |
+
|
| 463 |
+
matrix_w = w_col0
|
| 464 |
+
matrix_x = col0
|
| 465 |
+
for j in tl.static_range(KERNEL_WIDTH):
|
| 466 |
+
if KERNEL_WIDTH == 2:
|
| 467 |
+
if j == 1:
|
| 468 |
+
matrix_w = w_col1
|
| 469 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 470 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 471 |
+
elif KERNEL_WIDTH == 3:
|
| 472 |
+
if j == 1:
|
| 473 |
+
matrix_w = w_col1
|
| 474 |
+
matrix_x = col1
|
| 475 |
+
elif j == 2:
|
| 476 |
+
matrix_w = w_col2
|
| 477 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 478 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 479 |
+
elif KERNEL_WIDTH == 4:
|
| 480 |
+
if j == 1:
|
| 481 |
+
matrix_w = w_col1
|
| 482 |
+
matrix_x = col1
|
| 483 |
+
elif j == 2:
|
| 484 |
+
matrix_w = w_col2
|
| 485 |
+
matrix_x = col2
|
| 486 |
+
elif j == 3:
|
| 487 |
+
matrix_w = w_col3
|
| 488 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 489 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 490 |
+
|
| 491 |
+
acc += matrix_x * matrix_w
|
| 492 |
+
|
| 493 |
+
# Update sliding window
|
| 494 |
+
if KERNEL_WIDTH == 2:
|
| 495 |
+
col0 = matrix_x
|
| 496 |
+
elif KERNEL_WIDTH == 3:
|
| 497 |
+
col0 = col1
|
| 498 |
+
col1 = matrix_x
|
| 499 |
+
elif KERNEL_WIDTH == 4:
|
| 500 |
+
col0 = col1
|
| 501 |
+
col1 = col2
|
| 502 |
+
col2 = matrix_x
|
| 503 |
+
|
| 504 |
+
# Apply activation
|
| 505 |
+
if SILU_ACTIVATION:
|
| 506 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 507 |
+
|
| 508 |
+
mask_feat = (idx_token < seqlen) & (idx_feats < dim)
|
| 509 |
+
|
| 510 |
+
# Query: idx_feats in [0, key_dim)
|
| 511 |
+
is_query = idx_feats < key_dim
|
| 512 |
+
q_feat_idx = idx_feats # 0-based index within query
|
| 513 |
+
q_ptrs = (
|
| 514 |
+
q_ptr
|
| 515 |
+
+ idx_seq * stride_q_seq
|
| 516 |
+
+ idx_token * stride_q_token
|
| 517 |
+
+ q_feat_idx * stride_q_dim
|
| 518 |
+
)
|
| 519 |
+
tl.store(q_ptrs, acc, mask=mask_feat & is_query)
|
| 520 |
+
|
| 521 |
+
# Key: idx_feats in [key_dim, 2*key_dim)
|
| 522 |
+
is_key = (idx_feats >= key_dim) & (idx_feats < 2 * key_dim)
|
| 523 |
+
k_feat_idx = idx_feats - key_dim
|
| 524 |
+
k_ptrs = (
|
| 525 |
+
k_ptr
|
| 526 |
+
+ idx_seq * stride_k_seq
|
| 527 |
+
+ idx_token * stride_k_token
|
| 528 |
+
+ k_feat_idx * stride_k_dim
|
| 529 |
+
)
|
| 530 |
+
tl.store(k_ptrs, acc, mask=mask_feat & is_key)
|
| 531 |
+
|
| 532 |
+
# Value: idx_feats in [2*key_dim, 2*key_dim+value_dim)
|
| 533 |
+
is_value = (idx_feats >= 2 * key_dim) & (
|
| 534 |
+
idx_feats < 2 * key_dim + value_dim
|
| 535 |
+
)
|
| 536 |
+
v_feat_idx = idx_feats - 2 * key_dim
|
| 537 |
+
v_ptrs = (
|
| 538 |
+
v_ptr
|
| 539 |
+
+ idx_seq * stride_v_seq
|
| 540 |
+
+ idx_token * stride_v_token
|
| 541 |
+
+ v_feat_idx * stride_v_dim
|
| 542 |
+
)
|
| 543 |
+
tl.store(v_ptrs, acc, mask=mask_feat & is_value)
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
@tl.core.builtin
|
| 547 |
+
def tuple_combine(a: gl.tuple, b: gl.tensor, _semantic=None) -> gl.tuple:
|
| 548 |
+
return tl.tuple([*a.values, b])
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
@gluon.jit()
|
| 552 |
+
def gluon_causal_conv1d_update_split_qkv_kernel(
|
| 553 |
+
# Pointers to matrices
|
| 554 |
+
x_ptr, # (batch, dim, seqlen) where dim = 2*key_dim + value_dim
|
| 555 |
+
w_ptr, # (dim, width)
|
| 556 |
+
bias_ptr,
|
| 557 |
+
conv_state_ptr,
|
| 558 |
+
conv_state_indices_ptr,
|
| 559 |
+
q_ptr,
|
| 560 |
+
k_ptr,
|
| 561 |
+
v_ptr,
|
| 562 |
+
key_dim: gl.constexpr,
|
| 563 |
+
value_dim: gl.constexpr,
|
| 564 |
+
# Matrix dimensions
|
| 565 |
+
batch: int,
|
| 566 |
+
dim: gl.constexpr,
|
| 567 |
+
seqlen: gl.constexpr,
|
| 568 |
+
state_len: gl.constexpr,
|
| 569 |
+
num_cache_lines: gl.constexpr,
|
| 570 |
+
# Strides
|
| 571 |
+
stride_x_seq: gl.constexpr,
|
| 572 |
+
stride_x_dim: gl.constexpr,
|
| 573 |
+
stride_x_token: gl.constexpr,
|
| 574 |
+
stride_w_dim: gl.constexpr,
|
| 575 |
+
stride_w_width: gl.constexpr,
|
| 576 |
+
stride_conv_state_seq: gl.constexpr,
|
| 577 |
+
stride_conv_state_dim: gl.constexpr,
|
| 578 |
+
stride_conv_state_tok: gl.constexpr,
|
| 579 |
+
stride_state_indices: gl.constexpr,
|
| 580 |
+
stride_q_seq: gl.constexpr,
|
| 581 |
+
stride_q_dim: gl.constexpr,
|
| 582 |
+
stride_q_token: gl.constexpr,
|
| 583 |
+
stride_k_seq: gl.constexpr,
|
| 584 |
+
stride_k_dim: gl.constexpr,
|
| 585 |
+
stride_k_token: gl.constexpr,
|
| 586 |
+
stride_v_seq: gl.constexpr,
|
| 587 |
+
stride_v_dim: gl.constexpr,
|
| 588 |
+
stride_v_token: gl.constexpr,
|
| 589 |
+
# others
|
| 590 |
+
pad_slot_id: gl.constexpr,
|
| 591 |
+
# Meta-parameters
|
| 592 |
+
HAS_BIAS: gl.constexpr,
|
| 593 |
+
KERNEL_WIDTH: gl.constexpr,
|
| 594 |
+
SILU_ACTIVATION: gl.constexpr,
|
| 595 |
+
IS_CONTINUOUS_BATCHING: gl.constexpr,
|
| 596 |
+
NP2_STATELEN: gl.constexpr,
|
| 597 |
+
USE_PAD_SLOT: gl.constexpr,
|
| 598 |
+
BLOCK_N: gl.constexpr,
|
| 599 |
+
):
|
| 600 |
+
"""Gluon version of causal_conv1d_update_split_qkv kernel (original)."""
|
| 601 |
+
|
| 602 |
+
blocked: gl.constexpr = gl.BlockedLayout(
|
| 603 |
+
size_per_thread=[2],
|
| 604 |
+
threads_per_warp=[64],
|
| 605 |
+
warps_per_cta=[2],
|
| 606 |
+
order=[0],
|
| 607 |
+
)
|
| 608 |
+
|
| 609 |
+
idx_seq = gl.program_id(0)
|
| 610 |
+
if idx_seq >= batch:
|
| 611 |
+
return
|
| 612 |
+
|
| 613 |
+
# [BLOCK_N,] elements along the feature-dimension (channel)
|
| 614 |
+
idx_feats = gl.program_id(1) * BLOCK_N + gl.arange(0, BLOCK_N, layout=blocked)
|
| 615 |
+
|
| 616 |
+
if IS_CONTINUOUS_BATCHING:
|
| 617 |
+
conv_state_batch_coord = gl.load(
|
| 618 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices
|
| 619 |
+
).to(gl.int64)
|
| 620 |
+
else:
|
| 621 |
+
conv_state_batch_coord = idx_seq
|
| 622 |
+
|
| 623 |
+
if USE_PAD_SLOT:
|
| 624 |
+
if conv_state_batch_coord == pad_slot_id:
|
| 625 |
+
return
|
| 626 |
+
|
| 627 |
+
# STEP 1: READ initial conv_state data
|
| 628 |
+
conv_states_base = (
|
| 629 |
+
conv_state_ptr
|
| 630 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 631 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 632 |
+
)
|
| 633 |
+
mask_w = idx_feats < dim
|
| 634 |
+
|
| 635 |
+
prior_tokens = conv_states_base
|
| 636 |
+
conv_state_vecs = ()
|
| 637 |
+
for j in gl.static_range(KERNEL_WIDTH - 1):
|
| 638 |
+
conv_states_ptrs = prior_tokens + j * stride_conv_state_tok
|
| 639 |
+
col = gl.load(conv_states_ptrs, mask_w, 0.0)
|
| 640 |
+
conv_state_vecs = tuple_combine(conv_state_vecs, col)
|
| 641 |
+
|
| 642 |
+
conv_state_base = (
|
| 643 |
+
conv_state_ptr
|
| 644 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 645 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 646 |
+
)
|
| 647 |
+
|
| 648 |
+
# STEP 3: init accumulator
|
| 649 |
+
if HAS_BIAS:
|
| 650 |
+
bias = bias_ptr + idx_feats
|
| 651 |
+
mask_bias = idx_feats < dim
|
| 652 |
+
acc_preload = gl.load(bias, mask=mask_bias, other=0.0).to(gl.float32)
|
| 653 |
+
else:
|
| 654 |
+
acc_preload = gl.zeros((BLOCK_N,), dtype=gl.float32, layout=blocked)
|
| 655 |
+
|
| 656 |
+
# STEP 4: PRE-LOAD WEIGHTS
|
| 657 |
+
w_base = w_ptr + (idx_feats * stride_w_dim)
|
| 658 |
+
mask_w = idx_feats < dim
|
| 659 |
+
w_vecs = ()
|
| 660 |
+
for j in gl.static_range(KERNEL_WIDTH):
|
| 661 |
+
w_ptrs = w_base + (j * stride_w_width)
|
| 662 |
+
w_col = gl.load(w_ptrs, mask_w, other=0.0)
|
| 663 |
+
w_vecs = tuple_combine(w_vecs, w_col)
|
| 664 |
+
|
| 665 |
+
x_base_1d = x_ptr + (idx_seq * stride_x_seq) + (idx_feats * stride_x_dim)
|
| 666 |
+
mask_x_1d = idx_feats < dim
|
| 667 |
+
|
| 668 |
+
# STEP 5: compute each token and split to q/k/v
|
| 669 |
+
for idx_token in gl.static_range(seqlen):
|
| 670 |
+
acc = acc_preload
|
| 671 |
+
|
| 672 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 673 |
+
x_vec = gl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 674 |
+
conv_state_vecs = tuple_combine(conv_state_vecs, x_vec)
|
| 675 |
+
|
| 676 |
+
for j in gl.static_range(KERNEL_WIDTH):
|
| 677 |
+
matrix_w = w_vecs[j]
|
| 678 |
+
matrix_x = conv_state_vecs[j]
|
| 679 |
+
acc += matrix_x * matrix_w
|
| 680 |
+
|
| 681 |
+
conv_state_vecs = conv_state_vecs[1:]
|
| 682 |
+
|
| 683 |
+
# Apply activation
|
| 684 |
+
if SILU_ACTIVATION:
|
| 685 |
+
acc = acc / (1 + gl.exp(-acc))
|
| 686 |
+
|
| 687 |
+
mask_feat = (idx_token < seqlen) & (idx_feats < dim)
|
| 688 |
+
|
| 689 |
+
# Split and store to q, k, v
|
| 690 |
+
# Query: idx_feats in [0, key_dim)
|
| 691 |
+
is_query = idx_feats < key_dim
|
| 692 |
+
q_feat_idx = idx_feats
|
| 693 |
+
q_ptrs = (
|
| 694 |
+
q_ptr
|
| 695 |
+
+ idx_seq * stride_q_seq
|
| 696 |
+
+ idx_token * stride_q_token
|
| 697 |
+
+ q_feat_idx * stride_q_dim
|
| 698 |
+
)
|
| 699 |
+
gl.store(q_ptrs, acc, mask=mask_feat & is_query)
|
| 700 |
+
|
| 701 |
+
# Key: idx_feats in [key_dim, 2*key_dim)
|
| 702 |
+
is_key = (idx_feats >= key_dim) & (idx_feats < 2 * key_dim)
|
| 703 |
+
k_feat_idx = idx_feats - key_dim
|
| 704 |
+
k_ptrs = (
|
| 705 |
+
k_ptr
|
| 706 |
+
+ idx_seq * stride_k_seq
|
| 707 |
+
+ idx_token * stride_k_token
|
| 708 |
+
+ k_feat_idx * stride_k_dim
|
| 709 |
+
)
|
| 710 |
+
gl.store(k_ptrs, acc, mask=mask_feat & is_key)
|
| 711 |
+
|
| 712 |
+
# Value: idx_feats in [2*key_dim, 2*key_dim+value_dim)
|
| 713 |
+
is_value = (idx_feats >= 2 * key_dim) & (idx_feats < 2 * key_dim + value_dim)
|
| 714 |
+
v_feat_idx = idx_feats - 2 * key_dim
|
| 715 |
+
v_ptrs = (
|
| 716 |
+
v_ptr
|
| 717 |
+
+ idx_seq * stride_v_seq
|
| 718 |
+
+ idx_token * stride_v_token
|
| 719 |
+
+ v_feat_idx * stride_v_dim
|
| 720 |
+
)
|
| 721 |
+
gl.store(v_ptrs, acc, mask=mask_feat & is_value)
|
| 722 |
+
|
| 723 |
+
# Store final conv_state
|
| 724 |
+
for idx in gl.static_range(state_len):
|
| 725 |
+
gl.store(
|
| 726 |
+
conv_state_base + idx * stride_conv_state_tok,
|
| 727 |
+
conv_state_vecs[idx],
|
| 728 |
+
idx_feats < dim,
|
| 729 |
+
)
|
| 730 |
+
|
| 731 |
+
|
| 732 |
+
@gluon.jit()
|
| 733 |
+
def gluon_causal_conv1d_update_split_qkv_kernel_v2(
|
| 734 |
+
# Pointers to matrices
|
| 735 |
+
x_ptr, # (batch, dim, seqlen) where dim = 2*key_dim + value_dim
|
| 736 |
+
w_ptr, # (dim, width)
|
| 737 |
+
bias_ptr,
|
| 738 |
+
conv_state_ptr,
|
| 739 |
+
conv_state_indices_ptr,
|
| 740 |
+
q_ptr,
|
| 741 |
+
k_ptr,
|
| 742 |
+
v_ptr,
|
| 743 |
+
key_dim: gl.constexpr,
|
| 744 |
+
value_dim: gl.constexpr,
|
| 745 |
+
# Matrix dimensions
|
| 746 |
+
batch: int,
|
| 747 |
+
dim: gl.constexpr,
|
| 748 |
+
seqlen: gl.constexpr,
|
| 749 |
+
state_len: gl.constexpr,
|
| 750 |
+
num_cache_lines: gl.constexpr,
|
| 751 |
+
# Strides
|
| 752 |
+
stride_x_seq: gl.constexpr,
|
| 753 |
+
stride_x_dim: gl.constexpr,
|
| 754 |
+
stride_x_token: gl.constexpr,
|
| 755 |
+
stride_w_dim: gl.constexpr,
|
| 756 |
+
stride_w_width: gl.constexpr,
|
| 757 |
+
stride_conv_state_seq: gl.constexpr,
|
| 758 |
+
stride_conv_state_dim: gl.constexpr,
|
| 759 |
+
stride_conv_state_tok: gl.constexpr,
|
| 760 |
+
stride_state_indices: gl.constexpr,
|
| 761 |
+
stride_q_seq: gl.constexpr,
|
| 762 |
+
stride_q_dim: gl.constexpr,
|
| 763 |
+
stride_q_token: gl.constexpr,
|
| 764 |
+
stride_k_seq: gl.constexpr,
|
| 765 |
+
stride_k_dim: gl.constexpr,
|
| 766 |
+
stride_k_token: gl.constexpr,
|
| 767 |
+
stride_v_seq: gl.constexpr,
|
| 768 |
+
stride_v_dim: gl.constexpr,
|
| 769 |
+
stride_v_token: gl.constexpr,
|
| 770 |
+
# others
|
| 771 |
+
pad_slot_id: gl.constexpr,
|
| 772 |
+
# Meta-parameters
|
| 773 |
+
HAS_BIAS: gl.constexpr,
|
| 774 |
+
KERNEL_WIDTH: gl.constexpr,
|
| 775 |
+
SILU_ACTIVATION: gl.constexpr,
|
| 776 |
+
IS_CONTINUOUS_BATCHING: gl.constexpr,
|
| 777 |
+
NP2_STATELEN: gl.constexpr,
|
| 778 |
+
USE_PAD_SLOT: gl.constexpr,
|
| 779 |
+
BLOCK_N: gl.constexpr,
|
| 780 |
+
):
|
| 781 |
+
"""Gluon version of causal_conv1d_update_split_qkv kernel (optimized v2).
|
| 782 |
+
|
| 783 |
+
Key optimizations:
|
| 784 |
+
- Eliminates tuple operations in hot loop (tuple_combine, tuple slicing)
|
| 785 |
+
- Uses explicit variables like Triton version for better register allocation
|
| 786 |
+
- Reduces memory allocation overhead in inner loop
|
| 787 |
+
"""
|
| 788 |
+
|
| 789 |
+
blocked: gl.constexpr = gl.BlockedLayout(
|
| 790 |
+
size_per_thread=[2],
|
| 791 |
+
threads_per_warp=[64],
|
| 792 |
+
warps_per_cta=[2],
|
| 793 |
+
order=[0],
|
| 794 |
+
)
|
| 795 |
+
|
| 796 |
+
idx_seq = gl.program_id(0)
|
| 797 |
+
if idx_seq >= batch:
|
| 798 |
+
return
|
| 799 |
+
|
| 800 |
+
# [BLOCK_N,] elements along the feature-dimension (channel)
|
| 801 |
+
idx_feats = gl.program_id(1) * BLOCK_N + gl.arange(0, BLOCK_N, layout=blocked)
|
| 802 |
+
|
| 803 |
+
if IS_CONTINUOUS_BATCHING:
|
| 804 |
+
conv_state_batch_coord = gl.load(
|
| 805 |
+
conv_state_indices_ptr + idx_seq * stride_state_indices
|
| 806 |
+
).to(gl.int64)
|
| 807 |
+
else:
|
| 808 |
+
conv_state_batch_coord = idx_seq
|
| 809 |
+
|
| 810 |
+
if USE_PAD_SLOT:
|
| 811 |
+
if conv_state_batch_coord == pad_slot_id:
|
| 812 |
+
return
|
| 813 |
+
|
| 814 |
+
# STEP 1: READ initial conv_state data (use explicit variables instead of tuple)
|
| 815 |
+
conv_states_base = (
|
| 816 |
+
conv_state_ptr
|
| 817 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 818 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 819 |
+
)
|
| 820 |
+
mask_w = idx_feats < dim
|
| 821 |
+
|
| 822 |
+
prior_tokens = conv_states_base
|
| 823 |
+
# Use explicit variables like Triton version - avoid tuple operations
|
| 824 |
+
if KERNEL_WIDTH >= 2:
|
| 825 |
+
conv_states_ptrs = prior_tokens
|
| 826 |
+
col0 = gl.load(conv_states_ptrs, mask_w, 0.0)
|
| 827 |
+
if KERNEL_WIDTH >= 3:
|
| 828 |
+
conv_states_ptrs = prior_tokens + 1 * stride_conv_state_tok
|
| 829 |
+
col1 = gl.load(conv_states_ptrs, mask_w, 0.0)
|
| 830 |
+
if KERNEL_WIDTH >= 4:
|
| 831 |
+
conv_states_ptrs = prior_tokens + 2 * stride_conv_state_tok
|
| 832 |
+
col2 = gl.load(conv_states_ptrs, mask_w, 0.0)
|
| 833 |
+
|
| 834 |
+
conv_state_base = (
|
| 835 |
+
conv_state_ptr
|
| 836 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 837 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 838 |
+
)
|
| 839 |
+
|
| 840 |
+
# STEP 3: init accumulator
|
| 841 |
+
if HAS_BIAS:
|
| 842 |
+
bias = bias_ptr + idx_feats
|
| 843 |
+
mask_bias = idx_feats < dim
|
| 844 |
+
acc_preload = gl.load(bias, mask=mask_bias, other=0.0).to(gl.float32)
|
| 845 |
+
else:
|
| 846 |
+
acc_preload = gl.zeros((BLOCK_N,), dtype=gl.float32, layout=blocked)
|
| 847 |
+
|
| 848 |
+
# STEP 4: PRE-LOAD WEIGHTS (use explicit variables)
|
| 849 |
+
w_base = w_ptr + (idx_feats * stride_w_dim)
|
| 850 |
+
mask_w = idx_feats < dim
|
| 851 |
+
if KERNEL_WIDTH >= 2:
|
| 852 |
+
w_ptrs = w_base + (0 * stride_w_width)
|
| 853 |
+
w_col0 = gl.load(w_ptrs, mask_w, other=0.0)
|
| 854 |
+
w_ptrs = w_base + (1 * stride_w_width)
|
| 855 |
+
w_col1 = gl.load(w_ptrs, mask_w, other=0.0)
|
| 856 |
+
if KERNEL_WIDTH >= 3:
|
| 857 |
+
w_ptrs = w_base + (2 * stride_w_width)
|
| 858 |
+
w_col2 = gl.load(w_ptrs, mask_w, other=0.0)
|
| 859 |
+
if KERNEL_WIDTH >= 4:
|
| 860 |
+
w_ptrs = w_base + (3 * stride_w_width)
|
| 861 |
+
w_col3 = gl.load(w_ptrs, mask_w, other=0.0)
|
| 862 |
+
|
| 863 |
+
x_base_1d = x_ptr + (idx_seq * stride_x_seq) + (idx_feats * stride_x_dim)
|
| 864 |
+
mask_x_1d = idx_feats < dim
|
| 865 |
+
|
| 866 |
+
# STEP 5: compute each token and split to q/k/v
|
| 867 |
+
# Use explicit variable updates like Triton version - NO tuple operations in loop!
|
| 868 |
+
for idx_token in gl.static_range(seqlen):
|
| 869 |
+
acc = acc_preload
|
| 870 |
+
|
| 871 |
+
# Initialize matrix_w and matrix_x for first iteration
|
| 872 |
+
matrix_w = w_col0
|
| 873 |
+
matrix_x = col0
|
| 874 |
+
|
| 875 |
+
# Compute convolution using explicit conditionals (like Triton)
|
| 876 |
+
for j in gl.static_range(KERNEL_WIDTH):
|
| 877 |
+
if KERNEL_WIDTH == 2:
|
| 878 |
+
if j == 1:
|
| 879 |
+
matrix_w = w_col1
|
| 880 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 881 |
+
matrix_x = gl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 882 |
+
elif KERNEL_WIDTH == 3:
|
| 883 |
+
if j == 1:
|
| 884 |
+
matrix_w = w_col1
|
| 885 |
+
matrix_x = col1
|
| 886 |
+
elif j == 2:
|
| 887 |
+
matrix_w = w_col2
|
| 888 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 889 |
+
matrix_x = gl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 890 |
+
elif KERNEL_WIDTH == 4:
|
| 891 |
+
if j == 1:
|
| 892 |
+
matrix_w = w_col1
|
| 893 |
+
matrix_x = col1
|
| 894 |
+
elif j == 2:
|
| 895 |
+
matrix_w = w_col2
|
| 896 |
+
matrix_x = col2
|
| 897 |
+
elif j == 3:
|
| 898 |
+
matrix_w = w_col3
|
| 899 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 900 |
+
matrix_x = gl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 901 |
+
|
| 902 |
+
acc += matrix_x * matrix_w
|
| 903 |
+
|
| 904 |
+
# Update sliding window with simple variable assignments (like Triton)
|
| 905 |
+
if KERNEL_WIDTH == 2:
|
| 906 |
+
col0 = matrix_x
|
| 907 |
+
elif KERNEL_WIDTH == 3:
|
| 908 |
+
col0 = col1
|
| 909 |
+
col1 = matrix_x
|
| 910 |
+
elif KERNEL_WIDTH == 4:
|
| 911 |
+
col0 = col1
|
| 912 |
+
col1 = col2
|
| 913 |
+
col2 = matrix_x
|
| 914 |
+
|
| 915 |
+
# Apply activation
|
| 916 |
+
if SILU_ACTIVATION:
|
| 917 |
+
acc = acc / (1 + gl.exp(-acc))
|
| 918 |
+
|
| 919 |
+
mask_feat = (idx_token < seqlen) & (idx_feats < dim)
|
| 920 |
+
|
| 921 |
+
# Split and store to q, k, v
|
| 922 |
+
# Query: idx_feats in [0, key_dim)
|
| 923 |
+
is_query = idx_feats < key_dim
|
| 924 |
+
q_feat_idx = idx_feats
|
| 925 |
+
q_ptrs = (
|
| 926 |
+
q_ptr
|
| 927 |
+
+ idx_seq * stride_q_seq
|
| 928 |
+
+ idx_token * stride_q_token
|
| 929 |
+
+ q_feat_idx * stride_q_dim
|
| 930 |
+
)
|
| 931 |
+
gl.store(q_ptrs, acc, mask=mask_feat & is_query)
|
| 932 |
+
|
| 933 |
+
# Key: idx_feats in [key_dim, 2*key_dim)
|
| 934 |
+
is_key = (idx_feats >= key_dim) & (idx_feats < 2 * key_dim)
|
| 935 |
+
k_feat_idx = idx_feats - key_dim
|
| 936 |
+
k_ptrs = (
|
| 937 |
+
k_ptr
|
| 938 |
+
+ idx_seq * stride_k_seq
|
| 939 |
+
+ idx_token * stride_k_token
|
| 940 |
+
+ k_feat_idx * stride_k_dim
|
| 941 |
+
)
|
| 942 |
+
gl.store(k_ptrs, acc, mask=mask_feat & is_key)
|
| 943 |
+
|
| 944 |
+
# Value: idx_feats in [2*key_dim, 2*key_dim+value_dim)
|
| 945 |
+
is_value = (idx_feats >= 2 * key_dim) & (idx_feats < 2 * key_dim + value_dim)
|
| 946 |
+
v_feat_idx = idx_feats - 2 * key_dim
|
| 947 |
+
v_ptrs = (
|
| 948 |
+
v_ptr
|
| 949 |
+
+ idx_seq * stride_v_seq
|
| 950 |
+
+ idx_token * stride_v_token
|
| 951 |
+
+ v_feat_idx * stride_v_dim
|
| 952 |
+
)
|
| 953 |
+
gl.store(v_ptrs, acc, mask=mask_feat & is_value)
|
| 954 |
+
|
| 955 |
+
# Store final conv_state using explicit variables
|
| 956 |
+
if KERNEL_WIDTH >= 2:
|
| 957 |
+
gl.store(conv_state_base + 0 * stride_conv_state_tok, col0, idx_feats < dim)
|
| 958 |
+
if KERNEL_WIDTH >= 3:
|
| 959 |
+
gl.store(conv_state_base + 1 * stride_conv_state_tok, col1, idx_feats < dim)
|
| 960 |
+
if KERNEL_WIDTH >= 4:
|
| 961 |
+
gl.store(conv_state_base + 2 * stride_conv_state_tok, col2, idx_feats < dim)
|
| 962 |
+
|
| 963 |
+
|
| 964 |
+
def causal_conv1d_update_split_qkv(
|
| 965 |
+
x: torch.Tensor,
|
| 966 |
+
conv_state: torch.Tensor,
|
| 967 |
+
weight: torch.Tensor,
|
| 968 |
+
key_dim: int,
|
| 969 |
+
value_dim: int,
|
| 970 |
+
bias: torch.Tensor | None = None,
|
| 971 |
+
activation: bool | str | None = "silu",
|
| 972 |
+
conv_state_indices: torch.Tensor | None = None,
|
| 973 |
+
pad_slot_id: int = PAD_SLOT_ID,
|
| 974 |
+
use_gluon: bool = True,
|
| 975 |
+
use_gluon_v2: bool = False,
|
| 976 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 977 |
+
"""Optimized causal_conv1d_update that directly outputs split q, k, v.
|
| 978 |
+
|
| 979 |
+
Args:
|
| 980 |
+
x: Input tensor (batch, dim, seqlen) where dim = 2*key_dim + value_dim
|
| 981 |
+
conv_state: Convolution state (num_cache_lines, dim, state_len)
|
| 982 |
+
weight: Convolution weights (dim, width)
|
| 983 |
+
key_dim: Dimension of query and key
|
| 984 |
+
value_dim: Dimension of value
|
| 985 |
+
bias: Optional bias (dim,)
|
| 986 |
+
activation: Activation function ("silu", "swish", or None)
|
| 987 |
+
conv_state_indices: Optional batch indices for continuous batching
|
| 988 |
+
pad_slot_id: ID for padded slots
|
| 989 |
+
use_gluon: Whether to use Gluon kernel (default: True)
|
| 990 |
+
use_gluon_v2: Whether to use optimized Gluon v2 kernel (default: False)
|
| 991 |
+
This version eliminates tuple operations for better performance
|
| 992 |
+
|
| 993 |
+
Returns:
|
| 994 |
+
Tuple of (query, key, value) tensors
|
| 995 |
+
"""
|
| 996 |
+
# Validate and prepare
|
| 997 |
+
if isinstance(activation, bool):
|
| 998 |
+
activation = "silu" if activation is True else None
|
| 999 |
+
elif activation is not None:
|
| 1000 |
+
assert activation in ["silu", "swish"]
|
| 1001 |
+
|
| 1002 |
+
unsqueeze = x.dim() == 2
|
| 1003 |
+
if unsqueeze:
|
| 1004 |
+
x = x.unsqueeze(-1)
|
| 1005 |
+
|
| 1006 |
+
batch, dim, seqlen = x.shape
|
| 1007 |
+
assert dim == 2 * key_dim + value_dim, f"dim {dim} != 2*{key_dim} + {value_dim}"
|
| 1008 |
+
|
| 1009 |
+
_, width = weight.shape
|
| 1010 |
+
num_cache_lines, _, state_len = conv_state.size()
|
| 1011 |
+
|
| 1012 |
+
query = torch.empty(
|
| 1013 |
+
(batch, key_dim, seqlen),
|
| 1014 |
+
dtype=x.dtype,
|
| 1015 |
+
device=x.device,
|
| 1016 |
+
)
|
| 1017 |
+
key = torch.empty(
|
| 1018 |
+
(batch, key_dim, seqlen),
|
| 1019 |
+
dtype=x.dtype,
|
| 1020 |
+
device=x.device,
|
| 1021 |
+
)
|
| 1022 |
+
value = torch.empty(
|
| 1023 |
+
(batch, value_dim, seqlen),
|
| 1024 |
+
dtype=x.dtype,
|
| 1025 |
+
device=x.device,
|
| 1026 |
+
)
|
| 1027 |
+
|
| 1028 |
+
# Kernel launch
|
| 1029 |
+
stride_state_indices = (
|
| 1030 |
+
conv_state_indices.stride(0) if conv_state_indices is not None else 0
|
| 1031 |
+
)
|
| 1032 |
+
state_len = width - 1
|
| 1033 |
+
np2_statelen = triton.next_power_of_2(state_len)
|
| 1034 |
+
|
| 1035 |
+
BLOCK_N = 256
|
| 1036 |
+
grid = (batch, triton.cdiv(dim, BLOCK_N))
|
| 1037 |
+
|
| 1038 |
+
# Select kernel based on flags
|
| 1039 |
+
if use_gluon:
|
| 1040 |
+
kernel_fn = (
|
| 1041 |
+
gluon_causal_conv1d_update_split_qkv_kernel_v2
|
| 1042 |
+
if use_gluon_v2
|
| 1043 |
+
else gluon_causal_conv1d_update_split_qkv_kernel
|
| 1044 |
+
)
|
| 1045 |
+
else:
|
| 1046 |
+
kernel_fn = _causal_conv1d_update_split_qkv_kernel
|
| 1047 |
+
|
| 1048 |
+
kernel_fn[grid](
|
| 1049 |
+
x_ptr=x,
|
| 1050 |
+
w_ptr=weight,
|
| 1051 |
+
bias_ptr=bias,
|
| 1052 |
+
conv_state_ptr=conv_state,
|
| 1053 |
+
conv_state_indices_ptr=conv_state_indices,
|
| 1054 |
+
q_ptr=query,
|
| 1055 |
+
k_ptr=key,
|
| 1056 |
+
v_ptr=value,
|
| 1057 |
+
key_dim=key_dim,
|
| 1058 |
+
value_dim=value_dim,
|
| 1059 |
+
batch=batch,
|
| 1060 |
+
dim=dim,
|
| 1061 |
+
seqlen=seqlen,
|
| 1062 |
+
state_len=state_len,
|
| 1063 |
+
num_cache_lines=num_cache_lines,
|
| 1064 |
+
stride_x_seq=x.stride(0),
|
| 1065 |
+
stride_x_dim=x.stride(1),
|
| 1066 |
+
stride_x_token=x.stride(2),
|
| 1067 |
+
stride_w_dim=weight.stride(0),
|
| 1068 |
+
stride_w_width=weight.stride(1),
|
| 1069 |
+
stride_conv_state_seq=conv_state.stride(0),
|
| 1070 |
+
stride_conv_state_dim=conv_state.stride(1),
|
| 1071 |
+
stride_conv_state_tok=conv_state.stride(2),
|
| 1072 |
+
stride_state_indices=stride_state_indices,
|
| 1073 |
+
stride_q_seq=query.stride(0),
|
| 1074 |
+
stride_q_dim=query.stride(1),
|
| 1075 |
+
stride_q_token=query.stride(2),
|
| 1076 |
+
stride_k_seq=key.stride(0),
|
| 1077 |
+
stride_k_dim=key.stride(1),
|
| 1078 |
+
stride_k_token=key.stride(2),
|
| 1079 |
+
stride_v_seq=value.stride(0),
|
| 1080 |
+
stride_v_dim=value.stride(1),
|
| 1081 |
+
stride_v_token=value.stride(2),
|
| 1082 |
+
pad_slot_id=pad_slot_id,
|
| 1083 |
+
HAS_BIAS=bias is not None,
|
| 1084 |
+
KERNEL_WIDTH=width,
|
| 1085 |
+
SILU_ACTIVATION=activation in ["silu", "swish"],
|
| 1086 |
+
IS_CONTINUOUS_BATCHING=conv_state_indices is not None,
|
| 1087 |
+
NP2_STATELEN=np2_statelen,
|
| 1088 |
+
USE_PAD_SLOT=pad_slot_id is not None,
|
| 1089 |
+
BLOCK_N=BLOCK_N,
|
| 1090 |
+
num_warps=2 if use_gluon else 4,
|
| 1091 |
+
)
|
| 1092 |
+
|
| 1093 |
+
if unsqueeze:
|
| 1094 |
+
query = query.squeeze(-1)
|
| 1095 |
+
key = key.squeeze(-1)
|
| 1096 |
+
value = value.squeeze(-1)
|
| 1097 |
+
|
| 1098 |
+
return query, key, value
|
build/torch-rocm/_triton_kernels/gated_delta_rule/decode/fused_rearrange_sigmoid_gdr.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
| 3 |
+
# SPDX-FileCopyrightText: Songlin Yang, Yu Zhang
|
| 4 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 5 |
+
#
|
| 6 |
+
# This file contains code copied from the flash-linear-attention project.
|
| 7 |
+
# The original source code was licensed under the MIT license and included
|
| 8 |
+
# the following copyright notice:
|
| 9 |
+
# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 10 |
+
|
| 11 |
+
import triton
|
| 12 |
+
import triton.language as tl
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@triton.heuristics(
|
| 16 |
+
{
|
| 17 |
+
"USE_INITIAL_STATE": lambda args: args["h0"] is not None,
|
| 18 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 19 |
+
"IS_CONTINUOUS_BATCHING": lambda args: args["ssm_state_indices"] is not None,
|
| 20 |
+
"IS_SPEC_DECODING": lambda args: args["num_accepted_tokens"] is not None,
|
| 21 |
+
}
|
| 22 |
+
)
|
| 23 |
+
@triton.jit(do_not_specialize=["N", "T"])
|
| 24 |
+
def fused_rearrange_sigmoid_gated_delta_rule_update_kernel(
|
| 25 |
+
A_log,
|
| 26 |
+
a,
|
| 27 |
+
b,
|
| 28 |
+
dt_bias,
|
| 29 |
+
beta,
|
| 30 |
+
threshold,
|
| 31 |
+
qkv,
|
| 32 |
+
o,
|
| 33 |
+
h0,
|
| 34 |
+
ht,
|
| 35 |
+
cu_seqlens,
|
| 36 |
+
ssm_state_indices,
|
| 37 |
+
num_accepted_tokens,
|
| 38 |
+
scale,
|
| 39 |
+
N: tl.int64, # num of sequences
|
| 40 |
+
T: tl.int64, # num of tokens
|
| 41 |
+
B: tl.constexpr,
|
| 42 |
+
H: tl.constexpr,
|
| 43 |
+
HV: tl.constexpr,
|
| 44 |
+
K: tl.constexpr,
|
| 45 |
+
V: tl.constexpr,
|
| 46 |
+
BK: tl.constexpr,
|
| 47 |
+
BV: tl.constexpr,
|
| 48 |
+
stride_qkv_l: tl.constexpr,
|
| 49 |
+
stride_qkv_hd: tl.constexpr,
|
| 50 |
+
stride_init_state_token: tl.constexpr,
|
| 51 |
+
stride_final_state_token: tl.constexpr,
|
| 52 |
+
stride_indices_seq: tl.constexpr,
|
| 53 |
+
stride_indices_tok: tl.constexpr,
|
| 54 |
+
USE_INITIAL_STATE: tl.constexpr, # whether to use initial state
|
| 55 |
+
INPLACE_FINAL_STATE: tl.constexpr, # whether to store final state inplace
|
| 56 |
+
USE_QK_L2NORM_IN_KERNEL: tl.constexpr,
|
| 57 |
+
IS_VARLEN: tl.constexpr,
|
| 58 |
+
IS_CONTINUOUS_BATCHING: tl.constexpr,
|
| 59 |
+
IS_SPEC_DECODING: tl.constexpr,
|
| 60 |
+
IS_KDA: tl.constexpr,
|
| 61 |
+
):
|
| 62 |
+
i_k, i_v, i_nh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 63 |
+
i_n, i_hv = i_nh // HV, i_nh % HV
|
| 64 |
+
i_h = i_hv // (HV // H)
|
| 65 |
+
if IS_VARLEN:
|
| 66 |
+
bos, eos = (
|
| 67 |
+
tl.load(cu_seqlens + i_n).to(tl.int64),
|
| 68 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int64),
|
| 69 |
+
)
|
| 70 |
+
all = T
|
| 71 |
+
T = eos - bos
|
| 72 |
+
else:
|
| 73 |
+
bos, eos = i_n * T, i_n * T + T
|
| 74 |
+
all = B * T
|
| 75 |
+
|
| 76 |
+
if T == 0:
|
| 77 |
+
return
|
| 78 |
+
|
| 79 |
+
o_k = i_k * BK + tl.arange(0, BK)
|
| 80 |
+
o_v = i_v * BV + tl.arange(0, BV)
|
| 81 |
+
|
| 82 |
+
p_q = qkv + bos * stride_qkv_l + ((i_h * K) + o_k) * stride_qkv_hd
|
| 83 |
+
p_k = qkv + bos * stride_qkv_l + (H * K + (i_h * K) + o_k) * stride_qkv_hd
|
| 84 |
+
p_v = qkv + bos * stride_qkv_l + (2 * H * K + (i_hv * V) + o_v) * stride_qkv_hd
|
| 85 |
+
|
| 86 |
+
p_A_log = A_log + i_hv
|
| 87 |
+
if not IS_KDA:
|
| 88 |
+
p_a = a + bos * HV + i_hv
|
| 89 |
+
p_dt_bias = dt_bias + i_hv
|
| 90 |
+
else:
|
| 91 |
+
p_a = a + (bos * HV + i_hv) * K + o_k
|
| 92 |
+
p_dt_bias = dt_bias + i_hv * K + o_k
|
| 93 |
+
|
| 94 |
+
p_b = b + bos * HV + i_hv
|
| 95 |
+
p_o = o + ((i_k * all + bos) * HV + i_hv) * V + o_v
|
| 96 |
+
|
| 97 |
+
mask_k = o_k < K
|
| 98 |
+
mask_v = o_v < V
|
| 99 |
+
mask_h = mask_v[:, None] & mask_k[None, :]
|
| 100 |
+
|
| 101 |
+
b_h = tl.zeros([BV, BK], dtype=tl.float32)
|
| 102 |
+
if USE_INITIAL_STATE:
|
| 103 |
+
if IS_CONTINUOUS_BATCHING:
|
| 104 |
+
if IS_SPEC_DECODING:
|
| 105 |
+
i_t = tl.load(num_accepted_tokens + i_n).to(tl.int64) - 1
|
| 106 |
+
else:
|
| 107 |
+
i_t = 0
|
| 108 |
+
state_idx = tl.load(
|
| 109 |
+
ssm_state_indices + i_n * stride_indices_seq + i_t * stride_indices_tok
|
| 110 |
+
).to(tl.int64)
|
| 111 |
+
if state_idx < 0:
|
| 112 |
+
return
|
| 113 |
+
p_h0 = h0 + state_idx * stride_init_state_token
|
| 114 |
+
else:
|
| 115 |
+
p_h0 = h0 + bos * HV * V * K
|
| 116 |
+
p_h0 = p_h0 + i_hv * V * K + o_v[:, None] * K + o_k[None, :]
|
| 117 |
+
b_h += tl.load(p_h0, mask=mask_h, other=0).to(tl.float32)
|
| 118 |
+
|
| 119 |
+
for i_t in range(0, T):
|
| 120 |
+
b_q = tl.load(p_q, mask=mask_k, other=0).to(tl.float32)
|
| 121 |
+
b_k = tl.load(p_k, mask=mask_k, other=0).to(tl.float32)
|
| 122 |
+
b_v = tl.load(p_v, mask=mask_v, other=0).to(tl.float32)
|
| 123 |
+
b_b = tl.load(p_b).to(tl.float32)
|
| 124 |
+
|
| 125 |
+
x = tl.load(p_a).to(tl.float32) + tl.load(p_dt_bias).to(tl.float32)
|
| 126 |
+
softplus_x = tl.where(
|
| 127 |
+
beta * x <= threshold, (1 / beta) * tl.log(1 + tl.exp(beta * x)), x
|
| 128 |
+
)
|
| 129 |
+
b_g = -tl.exp(tl.load(p_A_log).to(tl.float32)) * softplus_x
|
| 130 |
+
|
| 131 |
+
b_beta = tl.sigmoid(b_b.to(tl.float32))
|
| 132 |
+
|
| 133 |
+
if USE_QK_L2NORM_IN_KERNEL:
|
| 134 |
+
b_q = b_q * tl.rsqrt(tl.sum(b_q * b_q) + 1e-6)
|
| 135 |
+
b_k = b_k * tl.rsqrt(tl.sum(b_k * b_k) + 1e-6)
|
| 136 |
+
b_q = b_q * scale
|
| 137 |
+
if not IS_KDA:
|
| 138 |
+
b_h *= tl.exp(b_g)
|
| 139 |
+
else:
|
| 140 |
+
b_h *= tl.exp(b_g[None, :])
|
| 141 |
+
b_v -= tl.sum(b_h * b_k[None, :], 1)
|
| 142 |
+
b_v *= b_beta
|
| 143 |
+
b_h += b_v[:, None] * b_k[None, :]
|
| 144 |
+
b_o = tl.sum(b_h * b_q[None, :], 1)
|
| 145 |
+
tl.store(p_o, b_o.to(p_o.dtype.element_ty), mask=mask_v)
|
| 146 |
+
|
| 147 |
+
if INPLACE_FINAL_STATE:
|
| 148 |
+
final_state_idx = tl.load(
|
| 149 |
+
ssm_state_indices + i_n * stride_indices_seq + i_t * stride_indices_tok
|
| 150 |
+
).to(tl.int64)
|
| 151 |
+
if final_state_idx >= 0:
|
| 152 |
+
p_ht = ht + final_state_idx * stride_final_state_token
|
| 153 |
+
p_ht = p_ht + i_hv * V * K + o_v[:, None] * K + o_k[None, :]
|
| 154 |
+
tl.store(p_ht, b_h.to(p_ht.dtype.element_ty), mask=mask_h)
|
| 155 |
+
else:
|
| 156 |
+
p_ht = ht + (bos + i_t) * stride_final_state_token
|
| 157 |
+
p_ht = p_ht + i_hv * V * K + o_v[:, None] * K + o_k[None, :]
|
| 158 |
+
tl.store(p_ht, b_h.to(p_ht.dtype.element_ty), mask=mask_h)
|
| 159 |
+
|
| 160 |
+
p_q += stride_qkv_l
|
| 161 |
+
p_k += stride_qkv_l
|
| 162 |
+
p_v += stride_qkv_l
|
| 163 |
+
p_o += HV * V
|
| 164 |
+
p_b += HV
|
| 165 |
+
p_a += HV
|
build/torch-rocm/_triton_kernels/gated_delta_rule/decode/fused_recurrent.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Fused recurrent gated delta rule forward kernel (Forward only).
|
| 7 |
+
|
| 8 |
+
This module provides an optimized fused recurrent implementation of the gated delta rule.
|
| 9 |
+
Note: Only forward pass is implemented. Backward pass is not supported in aiter.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import triton
|
| 13 |
+
import triton.language as tl
|
| 14 |
+
from ....utils._triton.kernel_repr import make_kernel_repr
|
| 15 |
+
|
| 16 |
+
_fused_recurrent_gated_delta_rule_fwd_kernel_repr = make_kernel_repr(
|
| 17 |
+
"_fused_recurrent_gated_delta_rule_fwd_kernel",
|
| 18 |
+
[
|
| 19 |
+
"BK",
|
| 20 |
+
"BV",
|
| 21 |
+
"USE_G",
|
| 22 |
+
"USE_GK",
|
| 23 |
+
"USE_GV",
|
| 24 |
+
"USE_QK_L2NORM_IN_KERNEL",
|
| 25 |
+
"IS_BETA_HEADWISE",
|
| 26 |
+
"USE_INITIAL_STATE",
|
| 27 |
+
"STORE_FINAL_STATE",
|
| 28 |
+
"IS_VARLEN",
|
| 29 |
+
],
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@triton.heuristics(
|
| 34 |
+
{
|
| 35 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 36 |
+
"USE_GK": lambda args: args["gk"] is not None,
|
| 37 |
+
"USE_GV": lambda args: args["gv"] is not None,
|
| 38 |
+
"USE_INITIAL_STATE": lambda args: args["h0"] is not None,
|
| 39 |
+
"STORE_FINAL_STATE": lambda args: args["ht"] is not None,
|
| 40 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 41 |
+
}
|
| 42 |
+
)
|
| 43 |
+
@triton.jit(
|
| 44 |
+
repr=_fused_recurrent_gated_delta_rule_fwd_kernel_repr, do_not_specialize=["T"]
|
| 45 |
+
)
|
| 46 |
+
def _fused_recurrent_gated_delta_rule_fwd_kernel(
|
| 47 |
+
q,
|
| 48 |
+
k,
|
| 49 |
+
v,
|
| 50 |
+
g,
|
| 51 |
+
gk,
|
| 52 |
+
gv,
|
| 53 |
+
beta,
|
| 54 |
+
o,
|
| 55 |
+
h0,
|
| 56 |
+
ht,
|
| 57 |
+
cu_seqlens,
|
| 58 |
+
scale,
|
| 59 |
+
T,
|
| 60 |
+
B: tl.constexpr,
|
| 61 |
+
H: tl.constexpr,
|
| 62 |
+
HV: tl.constexpr,
|
| 63 |
+
K: tl.constexpr,
|
| 64 |
+
V: tl.constexpr,
|
| 65 |
+
BK: tl.constexpr,
|
| 66 |
+
BV: tl.constexpr,
|
| 67 |
+
USE_G: tl.constexpr,
|
| 68 |
+
USE_GK: tl.constexpr,
|
| 69 |
+
USE_GV: tl.constexpr,
|
| 70 |
+
USE_QK_L2NORM_IN_KERNEL: tl.constexpr,
|
| 71 |
+
IS_BETA_HEADWISE: tl.constexpr,
|
| 72 |
+
USE_INITIAL_STATE: tl.constexpr,
|
| 73 |
+
STORE_FINAL_STATE: tl.constexpr,
|
| 74 |
+
IS_VARLEN: tl.constexpr,
|
| 75 |
+
):
|
| 76 |
+
"""
|
| 77 |
+
Fused recurrent gated delta rule forward kernel.
|
| 78 |
+
|
| 79 |
+
This kernel implements a recurrent computation with gating mechanisms
|
| 80 |
+
for sequence modeling tasks.
|
| 81 |
+
|
| 82 |
+
Args:
|
| 83 |
+
q: Query tensor pointer
|
| 84 |
+
k: Key tensor pointer
|
| 85 |
+
v: Value tensor pointer
|
| 86 |
+
g: Global gate tensor pointer (optional)
|
| 87 |
+
gk: Key gate tensor pointer (optional)
|
| 88 |
+
gv: Value gate tensor pointer (optional)
|
| 89 |
+
beta: Beta parameter tensor pointer
|
| 90 |
+
o: Output tensor pointer
|
| 91 |
+
h0: Initial hidden state pointer (optional)
|
| 92 |
+
ht: Final hidden state pointer (optional)
|
| 93 |
+
cu_seqlens: Cumulative sequence lengths for variable-length inputs (optional)
|
| 94 |
+
scale: Scaling factor for queries
|
| 95 |
+
T: Sequence length
|
| 96 |
+
B, H, HV, K, V: Batch, head dimensions
|
| 97 |
+
BK, BV: Block sizes
|
| 98 |
+
USE_G, USE_GK, USE_GV: Flags for using gates
|
| 99 |
+
USE_QK_L2NORM_IN_KERNEL: Flag for L2 normalization
|
| 100 |
+
IS_BETA_HEADWISE: Flag for beta dimensionality
|
| 101 |
+
USE_INITIAL_STATE: Flag for using initial state
|
| 102 |
+
STORE_FINAL_STATE: Flag for storing final state
|
| 103 |
+
IS_VARLEN: Flag for variable-length sequences
|
| 104 |
+
"""
|
| 105 |
+
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
| 106 |
+
i_n, i_hv = i_nh // HV, i_nh % HV
|
| 107 |
+
i_h = i_hv // (HV // H)
|
| 108 |
+
|
| 109 |
+
if IS_VARLEN:
|
| 110 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int64), tl.load(
|
| 111 |
+
cu_seqlens + i_n + 1
|
| 112 |
+
).to(tl.int64)
|
| 113 |
+
T = eos - bos
|
| 114 |
+
else:
|
| 115 |
+
bos, eos = i_n * T, i_n * T + T
|
| 116 |
+
o_k = tl.arange(0, BK)
|
| 117 |
+
o_v = i_v * BV + tl.arange(0, BV)
|
| 118 |
+
|
| 119 |
+
p_q = q + (bos * H + i_h) * K + o_k
|
| 120 |
+
p_k = k + (bos * H + i_h) * K + o_k
|
| 121 |
+
p_v = v + (bos * HV + i_hv) * V + o_v
|
| 122 |
+
if USE_G:
|
| 123 |
+
p_g = g + bos * HV + i_hv
|
| 124 |
+
if USE_GK:
|
| 125 |
+
p_gk = gk + (bos * HV + i_hv) * K + o_k
|
| 126 |
+
if USE_GV:
|
| 127 |
+
p_gv = gv + (bos * HV + i_hv) * V + o_v
|
| 128 |
+
if IS_BETA_HEADWISE:
|
| 129 |
+
p_beta = beta + bos * HV + i_hv
|
| 130 |
+
else:
|
| 131 |
+
p_beta = beta + (bos * HV + i_hv) * V + o_v
|
| 132 |
+
|
| 133 |
+
p_o = o + (bos * HV + i_hv) * V + o_v
|
| 134 |
+
|
| 135 |
+
mask_k = o_k < K
|
| 136 |
+
mask_v = o_v < V
|
| 137 |
+
mask_h = mask_k[:, None] & mask_v[None, :]
|
| 138 |
+
|
| 139 |
+
b_h = tl.zeros([BK, BV], dtype=tl.float32)
|
| 140 |
+
if USE_INITIAL_STATE:
|
| 141 |
+
p_h0 = h0 + i_nh * K * V + o_k[:, None] * V + o_v[None, :]
|
| 142 |
+
b_h += tl.load(p_h0, mask=mask_h, other=0).to(tl.float32)
|
| 143 |
+
|
| 144 |
+
for _ in range(0, T):
|
| 145 |
+
b_q = tl.load(p_q, mask=mask_k, other=0).to(tl.float32)
|
| 146 |
+
b_k = tl.load(p_k, mask=mask_k, other=0).to(tl.float32)
|
| 147 |
+
b_v = tl.load(p_v, mask=mask_v, other=0).to(tl.float32)
|
| 148 |
+
if USE_QK_L2NORM_IN_KERNEL:
|
| 149 |
+
b_q = b_q / tl.sqrt(tl.sum(b_q * b_q) + 1e-6)
|
| 150 |
+
b_k = b_k / tl.sqrt(tl.sum(b_k * b_k) + 1e-6)
|
| 151 |
+
b_q = b_q * scale
|
| 152 |
+
if IS_BETA_HEADWISE:
|
| 153 |
+
b_beta = tl.load(p_beta).to(tl.float32)
|
| 154 |
+
else:
|
| 155 |
+
b_beta = tl.load(p_beta, mask=mask_v, other=0).to(tl.float32)
|
| 156 |
+
|
| 157 |
+
# [BK, BV]
|
| 158 |
+
if USE_G:
|
| 159 |
+
b_g = tl.load(p_g).to(tl.float32)
|
| 160 |
+
b_h *= tl.exp(b_g)
|
| 161 |
+
|
| 162 |
+
if USE_GK:
|
| 163 |
+
b_gk = tl.load(p_gk).to(tl.float32)
|
| 164 |
+
b_h *= tl.exp(b_gk[:, None])
|
| 165 |
+
|
| 166 |
+
if USE_GV:
|
| 167 |
+
b_gv = tl.load(p_gv).to(tl.float32)
|
| 168 |
+
b_h *= tl.exp(b_gv[None, :])
|
| 169 |
+
|
| 170 |
+
b_v = b_beta * (b_v - tl.sum(b_h * b_k[:, None], 0))
|
| 171 |
+
b_h += b_k[:, None] * b_v
|
| 172 |
+
|
| 173 |
+
# [BV]
|
| 174 |
+
b_o = tl.sum(b_h * b_q[:, None], 0)
|
| 175 |
+
tl.store(p_o, b_o.to(p_o.dtype.element_ty), mask=mask_v)
|
| 176 |
+
|
| 177 |
+
p_q += H * K
|
| 178 |
+
p_k += H * K
|
| 179 |
+
p_v += HV * V
|
| 180 |
+
if USE_G:
|
| 181 |
+
p_g += HV
|
| 182 |
+
if USE_GK:
|
| 183 |
+
p_gk += HV * K
|
| 184 |
+
if USE_GV:
|
| 185 |
+
p_gv += HV * V
|
| 186 |
+
p_beta += HV * (1 if IS_BETA_HEADWISE else V)
|
| 187 |
+
p_o += HV * V
|
| 188 |
+
|
| 189 |
+
if STORE_FINAL_STATE:
|
| 190 |
+
p_ht = ht + i_nh * K * V + o_k[:, None] * V + o_v[None, :]
|
| 191 |
+
tl.store(p_ht, b_h.to(p_ht.dtype.element_ty), mask=mask_h)
|
build/torch-rocm/_triton_kernels/gated_delta_rule/decode/fused_sigmoid_gating_recurrent.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from ..gated_delta_rule_utils import (
|
| 8 |
+
autotune_cache_kwargs,
|
| 9 |
+
gated_delta_rule_autotune_configs,
|
| 10 |
+
input_guard,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def is_cuda():
|
| 15 |
+
try:
|
| 16 |
+
return triton.runtime.driver.active.get_current_target().backend == "cuda"
|
| 17 |
+
except Exception:
|
| 18 |
+
# No active Triton driver (e.g. no-GPU CI sandbox at import time).
|
| 19 |
+
# Default to False so the HIP autotune config is selected.
|
| 20 |
+
return False
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def get_cuda_autotune_config():
|
| 24 |
+
return [
|
| 25 |
+
triton.Config({"BV": 8}, num_stages=3, num_warps=1),
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def get_hip_autotune_config():
|
| 30 |
+
return [
|
| 31 |
+
triton.Config({"BV": 64}, num_stages=1, num_warps=4),
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def get_autotune_config():
|
| 36 |
+
if is_cuda():
|
| 37 |
+
return get_cuda_autotune_config()
|
| 38 |
+
else:
|
| 39 |
+
return get_hip_autotune_config()
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@triton.heuristics(
|
| 43 |
+
{
|
| 44 |
+
"USE_INITIAL_STATE": lambda args: args["h0_source"] is not None,
|
| 45 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 46 |
+
}
|
| 47 |
+
)
|
| 48 |
+
@triton.autotune(
|
| 49 |
+
configs=gated_delta_rule_autotune_configs(get_autotune_config()),
|
| 50 |
+
key=["K", "V"],
|
| 51 |
+
**autotune_cache_kwargs,
|
| 52 |
+
)
|
| 53 |
+
@triton.jit(do_not_specialize=["T"])
|
| 54 |
+
def fused_sigmoid_gating_delta_rule_update_kernel(
|
| 55 |
+
A_log,
|
| 56 |
+
a,
|
| 57 |
+
dt_bias,
|
| 58 |
+
softplus_beta,
|
| 59 |
+
softplus_threshold,
|
| 60 |
+
q,
|
| 61 |
+
k,
|
| 62 |
+
v,
|
| 63 |
+
b,
|
| 64 |
+
o,
|
| 65 |
+
h0_source,
|
| 66 |
+
h0_indices,
|
| 67 |
+
cu_seqlens,
|
| 68 |
+
scale,
|
| 69 |
+
T,
|
| 70 |
+
B: tl.constexpr,
|
| 71 |
+
H: tl.constexpr,
|
| 72 |
+
HV: tl.constexpr,
|
| 73 |
+
K: tl.constexpr,
|
| 74 |
+
V: tl.constexpr,
|
| 75 |
+
BK: tl.constexpr,
|
| 76 |
+
BV: tl.constexpr,
|
| 77 |
+
USE_INITIAL_STATE: tl.constexpr,
|
| 78 |
+
USE_QK_L2NORM_IN_KERNEL: tl.constexpr,
|
| 79 |
+
IS_VARLEN: tl.constexpr,
|
| 80 |
+
):
|
| 81 |
+
"""
|
| 82 |
+
Fused kernel that combines sigmoid gating computation with recurrent delta rule update.
|
| 83 |
+
"""
|
| 84 |
+
i_k, i_v, i_nh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 85 |
+
i_n, i_hv = i_nh // HV, i_nh % HV
|
| 86 |
+
i_h = i_hv // (HV // H)
|
| 87 |
+
|
| 88 |
+
if IS_VARLEN:
|
| 89 |
+
bos, eos = (
|
| 90 |
+
tl.load(cu_seqlens + i_n).to(tl.int64),
|
| 91 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int64),
|
| 92 |
+
)
|
| 93 |
+
all = T
|
| 94 |
+
T = eos - bos
|
| 95 |
+
else:
|
| 96 |
+
bos, eos = i_n * T, i_n * T + T
|
| 97 |
+
all = B * T
|
| 98 |
+
|
| 99 |
+
o_k = i_k * BK + tl.arange(0, BK)
|
| 100 |
+
o_v = i_v * BV + tl.arange(0, BV)
|
| 101 |
+
|
| 102 |
+
p_q = q + (bos * H + i_h) * K + o_k
|
| 103 |
+
p_k = k + (bos * H + i_h) * K + o_k
|
| 104 |
+
p_v = v + (bos * HV + i_hv) * V + o_v
|
| 105 |
+
p_b = b + bos * HV + i_hv
|
| 106 |
+
p_o = o + ((i_k * all + bos) * HV + i_hv) * V + o_v
|
| 107 |
+
|
| 108 |
+
# Gating computation pointers
|
| 109 |
+
p_A_log = A_log + i_hv
|
| 110 |
+
p_a = a + bos * HV + i_hv
|
| 111 |
+
p_dt_bias = dt_bias + i_hv
|
| 112 |
+
|
| 113 |
+
mask_k = o_k < K
|
| 114 |
+
mask_v = o_v < V
|
| 115 |
+
mask_h = mask_k[:, None] & mask_v[None, :]
|
| 116 |
+
|
| 117 |
+
b_h = tl.zeros([BK, BV], dtype=tl.float32)
|
| 118 |
+
if USE_INITIAL_STATE:
|
| 119 |
+
idx = tl.load(h0_indices + i_n)
|
| 120 |
+
if idx >= 0:
|
| 121 |
+
p_h0 = (
|
| 122 |
+
h0_source
|
| 123 |
+
+ idx * HV * K * V
|
| 124 |
+
+ i_hv * K * V
|
| 125 |
+
+ o_k[:, None] * V
|
| 126 |
+
+ o_v[None, :]
|
| 127 |
+
)
|
| 128 |
+
b_h += tl.load(p_h0, mask=mask_h, other=0).to(tl.float32)
|
| 129 |
+
|
| 130 |
+
for _ in range(0, T):
|
| 131 |
+
# Load inputs
|
| 132 |
+
b_q = tl.load(p_q, mask=mask_k, other=0).to(tl.float32)
|
| 133 |
+
b_k = tl.load(p_k, mask=mask_k, other=0).to(tl.float32)
|
| 134 |
+
b_v = tl.load(p_v, mask=mask_v, other=0).to(tl.float32)
|
| 135 |
+
b_b = tl.load(p_b).to(tl.float32)
|
| 136 |
+
|
| 137 |
+
# Compute sigmoid gating
|
| 138 |
+
# Load gating parameters
|
| 139 |
+
b_A_log = tl.load(p_A_log).to(tl.float32)
|
| 140 |
+
b_a = tl.load(p_a).to(tl.float32)
|
| 141 |
+
b_dt_bias = tl.load(p_dt_bias).to(tl.float32)
|
| 142 |
+
|
| 143 |
+
# Compute g = -exp(A_log) * softplus(a + dt_bias)
|
| 144 |
+
x = b_a + b_dt_bias
|
| 145 |
+
beta_x = softplus_beta * x
|
| 146 |
+
# Apply softplus with numerical stability
|
| 147 |
+
softplus_x = tl.where(
|
| 148 |
+
beta_x <= softplus_threshold,
|
| 149 |
+
(1.0 / softplus_beta) * tl.log(1.0 + tl.exp(beta_x)),
|
| 150 |
+
x,
|
| 151 |
+
)
|
| 152 |
+
b_g = -tl.exp(b_A_log) * softplus_x
|
| 153 |
+
|
| 154 |
+
# Compute beta = sigmoid(b)
|
| 155 |
+
b_beta = 1.0 / (1.0 + tl.exp(-b_b))
|
| 156 |
+
|
| 157 |
+
# Apply L2 normalization if enabled
|
| 158 |
+
if USE_QK_L2NORM_IN_KERNEL:
|
| 159 |
+
b_q = b_q / (tl.sqrt(tl.sum(b_q * b_q) + 1e-6))
|
| 160 |
+
b_k = b_k / (tl.sqrt(tl.sum(b_k * b_k) + 1e-6))
|
| 161 |
+
|
| 162 |
+
b_q = b_q * scale
|
| 163 |
+
|
| 164 |
+
# Apply gating to hidden state: h *= exp(g)
|
| 165 |
+
b_h *= tl.exp(b_g)
|
| 166 |
+
|
| 167 |
+
# Delta rule: v -= sum(h * k, dim=0)
|
| 168 |
+
b_v -= tl.sum(b_h * b_k[:, None], 0)
|
| 169 |
+
|
| 170 |
+
# Apply beta gating: v *= beta
|
| 171 |
+
b_v *= b_beta
|
| 172 |
+
|
| 173 |
+
# Update hidden state: h += k[:, None] * v[None, :]
|
| 174 |
+
b_h += b_k[:, None] * b_v[None, :]
|
| 175 |
+
|
| 176 |
+
# Compute output: o = sum(h * q, dim=0)
|
| 177 |
+
b_o = tl.sum(b_h * b_q[:, None], 0)
|
| 178 |
+
tl.store(p_o, b_o.to(p_o.dtype.element_ty), mask=mask_v)
|
| 179 |
+
|
| 180 |
+
# Update pointers for next timestep
|
| 181 |
+
p_q += H * K
|
| 182 |
+
p_k += H * K
|
| 183 |
+
p_o += HV * V
|
| 184 |
+
p_v += HV * V
|
| 185 |
+
p_b += HV
|
| 186 |
+
p_a += HV
|
| 187 |
+
|
| 188 |
+
# Store final state back to h0_source with bounds checking
|
| 189 |
+
if USE_INITIAL_STATE:
|
| 190 |
+
idx = tl.load(h0_indices + i_n)
|
| 191 |
+
if idx >= 0:
|
| 192 |
+
p_h0 = (
|
| 193 |
+
h0_source
|
| 194 |
+
+ idx * HV * K * V
|
| 195 |
+
+ i_hv * K * V
|
| 196 |
+
+ o_k[:, None] * V
|
| 197 |
+
+ o_v[None, :]
|
| 198 |
+
)
|
| 199 |
+
tl.store(p_h0, b_h.to(p_h0.dtype.element_ty), mask=mask_h)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
@input_guard
|
| 203 |
+
def fused_sigmoid_gating_delta_rule_update(
|
| 204 |
+
A_log: torch.Tensor,
|
| 205 |
+
a: torch.Tensor,
|
| 206 |
+
dt_bias: torch.Tensor,
|
| 207 |
+
softplus_beta: float,
|
| 208 |
+
softplus_threshold: float,
|
| 209 |
+
q: torch.Tensor,
|
| 210 |
+
k: torch.Tensor,
|
| 211 |
+
v: torch.Tensor,
|
| 212 |
+
b: torch.Tensor,
|
| 213 |
+
initial_state_source: torch.Tensor,
|
| 214 |
+
initial_state_indices: torch.Tensor,
|
| 215 |
+
scale: Optional[float] = None,
|
| 216 |
+
use_qk_l2norm_in_kernel: bool = False,
|
| 217 |
+
cu_seqlens: Optional[torch.Tensor] = None,
|
| 218 |
+
):
|
| 219 |
+
"""
|
| 220 |
+
Fused triton implementation of sigmoid gating delta rule update.
|
| 221 |
+
This function uses a single fused kernel that combines both sigmoid gating computation
|
| 222 |
+
and the recurrent delta rule update for better performance.
|
| 223 |
+
"""
|
| 224 |
+
B, T, H, K, V = *k.shape, v.shape[-1]
|
| 225 |
+
HV = v.shape[2]
|
| 226 |
+
N = B if cu_seqlens is None else len(cu_seqlens) - 1
|
| 227 |
+
BK = triton.next_power_of_2(K)
|
| 228 |
+
NK = triton.cdiv(K, BK)
|
| 229 |
+
assert NK == 1, "NK > 1 is not supported yet"
|
| 230 |
+
|
| 231 |
+
if scale is None:
|
| 232 |
+
scale = k.shape[-1] ** -0.5
|
| 233 |
+
else:
|
| 234 |
+
assert scale > 0, "scale must be positive"
|
| 235 |
+
|
| 236 |
+
o = q.new_empty(NK, *v.shape)
|
| 237 |
+
|
| 238 |
+
def grid(META):
|
| 239 |
+
return (NK, triton.cdiv(V, META["BV"]), N * HV)
|
| 240 |
+
|
| 241 |
+
fused_sigmoid_gating_delta_rule_update_kernel[grid](
|
| 242 |
+
A_log=A_log,
|
| 243 |
+
a=a,
|
| 244 |
+
dt_bias=dt_bias,
|
| 245 |
+
softplus_beta=softplus_beta,
|
| 246 |
+
softplus_threshold=softplus_threshold,
|
| 247 |
+
q=q,
|
| 248 |
+
k=k,
|
| 249 |
+
v=v,
|
| 250 |
+
b=b,
|
| 251 |
+
o=o,
|
| 252 |
+
h0_source=initial_state_source,
|
| 253 |
+
h0_indices=initial_state_indices,
|
| 254 |
+
cu_seqlens=cu_seqlens,
|
| 255 |
+
scale=scale,
|
| 256 |
+
T=T,
|
| 257 |
+
B=B,
|
| 258 |
+
H=H,
|
| 259 |
+
HV=HV,
|
| 260 |
+
K=K,
|
| 261 |
+
V=V,
|
| 262 |
+
BK=BK,
|
| 263 |
+
USE_QK_L2NORM_IN_KERNEL=use_qk_l2norm_in_kernel,
|
| 264 |
+
)
|
| 265 |
+
o = o.squeeze(0)
|
| 266 |
+
return o
|
build/torch-rocm/_triton_kernels/gated_delta_rule/fused_qkvzba_split.py
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Fused kernels for QKVZBA split, reshape and concatenation."""
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@triton.jit
|
| 9 |
+
def _fused_qkvzba_split_reshape_cat_decode_kernel(
|
| 10 |
+
mixed_qkv,
|
| 11 |
+
z,
|
| 12 |
+
b,
|
| 13 |
+
a,
|
| 14 |
+
mixed_qkvz,
|
| 15 |
+
mixed_ba,
|
| 16 |
+
NUM_HEADS_QK: tl.constexpr,
|
| 17 |
+
NUM_HEADS_V: tl.constexpr,
|
| 18 |
+
HEAD_QK: tl.constexpr,
|
| 19 |
+
HEAD_V: tl.constexpr,
|
| 20 |
+
):
|
| 21 |
+
"""Decode stage fused kernel."""
|
| 22 |
+
i_bs, i_qk = tl.program_id(0), tl.program_id(1)
|
| 23 |
+
QKVZ_DIM_T: tl.constexpr = HEAD_QK * 2 + NUM_HEADS_V // NUM_HEADS_QK * HEAD_V * 2
|
| 24 |
+
BA_DIM_T: tl.constexpr = NUM_HEADS_V // NUM_HEADS_QK * 2
|
| 25 |
+
QKV_DIM_T: tl.constexpr = HEAD_QK * 2 + NUM_HEADS_V // NUM_HEADS_QK * HEAD_V
|
| 26 |
+
q_end: tl.constexpr = HEAD_QK
|
| 27 |
+
blk_q_ptr = (
|
| 28 |
+
mixed_qkvz
|
| 29 |
+
+ i_bs * NUM_HEADS_QK * QKVZ_DIM_T
|
| 30 |
+
+ i_qk * QKVZ_DIM_T
|
| 31 |
+
+ tl.arange(0, q_end)
|
| 32 |
+
)
|
| 33 |
+
k_end: tl.constexpr = q_end + HEAD_QK
|
| 34 |
+
blk_k_ptr = (
|
| 35 |
+
mixed_qkvz
|
| 36 |
+
+ i_bs * NUM_HEADS_QK * QKVZ_DIM_T
|
| 37 |
+
+ i_qk * QKVZ_DIM_T
|
| 38 |
+
+ tl.arange(q_end, k_end)
|
| 39 |
+
)
|
| 40 |
+
v_end: tl.constexpr = k_end + NUM_HEADS_V // NUM_HEADS_QK * HEAD_V
|
| 41 |
+
blk_v_ptr = (
|
| 42 |
+
mixed_qkvz
|
| 43 |
+
+ i_bs * NUM_HEADS_QK * QKVZ_DIM_T
|
| 44 |
+
+ i_qk * QKVZ_DIM_T
|
| 45 |
+
+ tl.arange(k_end, v_end)
|
| 46 |
+
)
|
| 47 |
+
z_end: tl.constexpr = v_end + NUM_HEADS_V // NUM_HEADS_QK * HEAD_V
|
| 48 |
+
blk_z_ptr = (
|
| 49 |
+
mixed_qkvz
|
| 50 |
+
+ i_bs * NUM_HEADS_QK * QKVZ_DIM_T
|
| 51 |
+
+ i_qk * QKVZ_DIM_T
|
| 52 |
+
+ tl.arange(v_end, z_end)
|
| 53 |
+
)
|
| 54 |
+
blk_q_st_ptr = (
|
| 55 |
+
mixed_qkv
|
| 56 |
+
+ i_bs * NUM_HEADS_QK * QKV_DIM_T
|
| 57 |
+
+ i_qk * HEAD_QK
|
| 58 |
+
+ tl.arange(0, HEAD_QK)
|
| 59 |
+
)
|
| 60 |
+
blk_k_st_ptr = (
|
| 61 |
+
mixed_qkv
|
| 62 |
+
+ i_bs * NUM_HEADS_QK * QKV_DIM_T
|
| 63 |
+
+ NUM_HEADS_QK * HEAD_QK
|
| 64 |
+
+ i_qk * HEAD_QK
|
| 65 |
+
+ tl.arange(0, HEAD_QK)
|
| 66 |
+
)
|
| 67 |
+
blk_v_st_ptr = (
|
| 68 |
+
mixed_qkv
|
| 69 |
+
+ i_bs * NUM_HEADS_QK * QKV_DIM_T
|
| 70 |
+
+ NUM_HEADS_QK * HEAD_QK * 2
|
| 71 |
+
+ i_qk * HEAD_V * NUM_HEADS_V // NUM_HEADS_QK
|
| 72 |
+
+ tl.arange(0, HEAD_V * NUM_HEADS_V // NUM_HEADS_QK)
|
| 73 |
+
)
|
| 74 |
+
blk_z_st_ptr = (
|
| 75 |
+
z
|
| 76 |
+
+ i_bs * NUM_HEADS_V * HEAD_V
|
| 77 |
+
+ i_qk * HEAD_V * NUM_HEADS_V // NUM_HEADS_QK
|
| 78 |
+
+ tl.arange(0, HEAD_V * NUM_HEADS_V // NUM_HEADS_QK)
|
| 79 |
+
)
|
| 80 |
+
tl.store(blk_q_st_ptr, tl.load(blk_q_ptr))
|
| 81 |
+
tl.store(blk_k_st_ptr, tl.load(blk_k_ptr))
|
| 82 |
+
tl.store(blk_v_st_ptr, tl.load(blk_v_ptr))
|
| 83 |
+
tl.store(blk_z_st_ptr, tl.load(blk_z_ptr))
|
| 84 |
+
b_end: tl.constexpr = NUM_HEADS_V // NUM_HEADS_QK
|
| 85 |
+
a_end: tl.constexpr = b_end + NUM_HEADS_V // NUM_HEADS_QK
|
| 86 |
+
for i in tl.static_range(b_end):
|
| 87 |
+
blk_b_ptr = mixed_ba + i_bs * NUM_HEADS_QK * BA_DIM_T + i_qk * BA_DIM_T + i
|
| 88 |
+
blk_b_st_ptr = b + i_bs * NUM_HEADS_V + i_qk * NUM_HEADS_V // NUM_HEADS_QK + i
|
| 89 |
+
tl.store(blk_b_st_ptr, tl.load(blk_b_ptr))
|
| 90 |
+
for i in tl.static_range(b_end, a_end):
|
| 91 |
+
blk_a_ptr = mixed_ba + i_bs * NUM_HEADS_QK * BA_DIM_T + i_qk * BA_DIM_T + i
|
| 92 |
+
blk_a_st_ptr = (
|
| 93 |
+
a + i_bs * NUM_HEADS_V + i_qk * NUM_HEADS_V // NUM_HEADS_QK + (i - b_end)
|
| 94 |
+
)
|
| 95 |
+
tl.store(blk_a_st_ptr, tl.load(blk_a_ptr))
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def fused_qkvzba_split_reshape_cat_decode(
|
| 99 |
+
mixed_qkvz: torch.Tensor,
|
| 100 |
+
mixed_ba: torch.Tensor,
|
| 101 |
+
num_heads_qk: int,
|
| 102 |
+
num_heads_v: int,
|
| 103 |
+
head_qk: int,
|
| 104 |
+
head_v: int,
|
| 105 |
+
):
|
| 106 |
+
"""Decode stage fused function."""
|
| 107 |
+
batch, seq_len = mixed_qkvz.shape[0], 1
|
| 108 |
+
qkv_dim_t = num_heads_qk * head_qk * 2 + num_heads_v * head_v
|
| 109 |
+
mixed_qkv = torch.empty(
|
| 110 |
+
[batch * seq_len, qkv_dim_t],
|
| 111 |
+
dtype=mixed_qkvz.dtype,
|
| 112 |
+
device=mixed_qkvz.device,
|
| 113 |
+
)
|
| 114 |
+
z = torch.empty(
|
| 115 |
+
[batch * seq_len, num_heads_v, head_v],
|
| 116 |
+
dtype=mixed_qkvz.dtype,
|
| 117 |
+
device=mixed_qkvz.device,
|
| 118 |
+
)
|
| 119 |
+
b = torch.empty(
|
| 120 |
+
[batch * seq_len, num_heads_v],
|
| 121 |
+
dtype=mixed_ba.dtype,
|
| 122 |
+
device=mixed_ba.device,
|
| 123 |
+
)
|
| 124 |
+
a = torch.empty_like(b)
|
| 125 |
+
grid = (batch * seq_len, num_heads_qk)
|
| 126 |
+
_fused_qkvzba_split_reshape_cat_decode_kernel[grid](
|
| 127 |
+
mixed_qkv,
|
| 128 |
+
z,
|
| 129 |
+
b,
|
| 130 |
+
a,
|
| 131 |
+
mixed_qkvz,
|
| 132 |
+
mixed_ba,
|
| 133 |
+
num_heads_qk,
|
| 134 |
+
num_heads_v,
|
| 135 |
+
head_qk,
|
| 136 |
+
head_v,
|
| 137 |
+
num_warps=1,
|
| 138 |
+
num_stages=3,
|
| 139 |
+
)
|
| 140 |
+
return mixed_qkv, z, b, a
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
@triton.jit
|
| 144 |
+
def _fused_qkvzba_prefill_kernel_nqk1(
|
| 145 |
+
mixed_qkv,
|
| 146 |
+
z,
|
| 147 |
+
b_out,
|
| 148 |
+
a_out,
|
| 149 |
+
mixed_qkvz,
|
| 150 |
+
mixed_ba,
|
| 151 |
+
NUM_HEADS_V: tl.constexpr,
|
| 152 |
+
HEAD_QK: tl.constexpr,
|
| 153 |
+
HEAD_V: tl.constexpr,
|
| 154 |
+
):
|
| 155 |
+
"""Prefill kernel specialized for NUM_HEADS_QK=1."""
|
| 156 |
+
i_seq = tl.program_id(0)
|
| 157 |
+
V_PER_QK: tl.constexpr = NUM_HEADS_V
|
| 158 |
+
QKVZ_DIM_T: tl.constexpr = HEAD_QK * 2 + V_PER_QK * HEAD_V * 2
|
| 159 |
+
BA_DIM_T: tl.constexpr = V_PER_QK * 2
|
| 160 |
+
QKV_DIM_T: tl.constexpr = HEAD_QK * 2 + NUM_HEADS_V * HEAD_V
|
| 161 |
+
|
| 162 |
+
base_in = mixed_qkvz + i_seq * QKVZ_DIM_T
|
| 163 |
+
base_out = mixed_qkv + i_seq * QKV_DIM_T
|
| 164 |
+
|
| 165 |
+
# Load and store q
|
| 166 |
+
blk_q = tl.load(base_in + tl.arange(0, HEAD_QK))
|
| 167 |
+
tl.store(base_out + tl.arange(0, HEAD_QK), blk_q)
|
| 168 |
+
|
| 169 |
+
# Load and store k
|
| 170 |
+
blk_k = tl.load(base_in + HEAD_QK + tl.arange(0, HEAD_QK))
|
| 171 |
+
tl.store(base_out + HEAD_QK + tl.arange(0, HEAD_QK), blk_k)
|
| 172 |
+
|
| 173 |
+
# Load and store v
|
| 174 |
+
blk_v = tl.load(base_in + HEAD_QK * 2 + tl.arange(0, V_PER_QK * HEAD_V))
|
| 175 |
+
tl.store(base_out + HEAD_QK * 2 + tl.arange(0, V_PER_QK * HEAD_V), blk_v)
|
| 176 |
+
|
| 177 |
+
# Load and store z
|
| 178 |
+
blk_z = tl.load(
|
| 179 |
+
base_in + HEAD_QK * 2 + V_PER_QK * HEAD_V + tl.arange(0, V_PER_QK * HEAD_V)
|
| 180 |
+
)
|
| 181 |
+
tl.store(z + i_seq * NUM_HEADS_V * HEAD_V + tl.arange(0, V_PER_QK * HEAD_V), blk_z)
|
| 182 |
+
|
| 183 |
+
# Load and store b, a
|
| 184 |
+
base_ba = mixed_ba + i_seq * BA_DIM_T
|
| 185 |
+
for i in tl.static_range(V_PER_QK):
|
| 186 |
+
tl.store(b_out + i_seq * NUM_HEADS_V + i, tl.load(base_ba + i))
|
| 187 |
+
tl.store(a_out + i_seq * NUM_HEADS_V + i, tl.load(base_ba + V_PER_QK + i))
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
@triton.jit
|
| 191 |
+
def _fused_qkvzba_prefill_kernel_nqk2(
|
| 192 |
+
mixed_qkv,
|
| 193 |
+
z,
|
| 194 |
+
b_out,
|
| 195 |
+
a_out,
|
| 196 |
+
mixed_qkvz,
|
| 197 |
+
mixed_ba,
|
| 198 |
+
NUM_HEADS_V: tl.constexpr,
|
| 199 |
+
HEAD_QK: tl.constexpr,
|
| 200 |
+
HEAD_V: tl.constexpr,
|
| 201 |
+
):
|
| 202 |
+
"""Prefill kernel specialized for NUM_HEADS_QK=2, single block processes entire row."""
|
| 203 |
+
i_seq = tl.program_id(0)
|
| 204 |
+
NUM_HEADS_QK: tl.constexpr = 2
|
| 205 |
+
V_PER_QK: tl.constexpr = NUM_HEADS_V // NUM_HEADS_QK
|
| 206 |
+
QKVZ_DIM_T: tl.constexpr = HEAD_QK * 2 + V_PER_QK * HEAD_V * 2
|
| 207 |
+
BA_DIM_T: tl.constexpr = V_PER_QK * 2
|
| 208 |
+
QKV_DIM_T: tl.constexpr = NUM_HEADS_QK * HEAD_QK * 2 + NUM_HEADS_V * HEAD_V
|
| 209 |
+
|
| 210 |
+
base_out = mixed_qkv + i_seq * QKV_DIM_T
|
| 211 |
+
|
| 212 |
+
# Process all heads sequentially to ensure contiguous writes
|
| 213 |
+
for i_qk in tl.static_range(NUM_HEADS_QK):
|
| 214 |
+
base_in = mixed_qkvz + i_seq * NUM_HEADS_QK * QKVZ_DIM_T + i_qk * QKVZ_DIM_T
|
| 215 |
+
|
| 216 |
+
# Load q, k, v, z
|
| 217 |
+
blk_q = tl.load(base_in + tl.arange(0, HEAD_QK))
|
| 218 |
+
blk_k = tl.load(base_in + HEAD_QK + tl.arange(0, HEAD_QK))
|
| 219 |
+
blk_v = tl.load(base_in + HEAD_QK * 2 + tl.arange(0, V_PER_QK * HEAD_V))
|
| 220 |
+
blk_z = tl.load(
|
| 221 |
+
base_in + HEAD_QK * 2 + V_PER_QK * HEAD_V + tl.arange(0, V_PER_QK * HEAD_V)
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
# Store q (all q contiguous)
|
| 225 |
+
tl.store(base_out + i_qk * HEAD_QK + tl.arange(0, HEAD_QK), blk_q)
|
| 226 |
+
# Store k (all k contiguous, after q)
|
| 227 |
+
tl.store(
|
| 228 |
+
base_out + NUM_HEADS_QK * HEAD_QK + i_qk * HEAD_QK + tl.arange(0, HEAD_QK),
|
| 229 |
+
blk_k,
|
| 230 |
+
)
|
| 231 |
+
# Store v (all v contiguous, after k)
|
| 232 |
+
tl.store(
|
| 233 |
+
base_out
|
| 234 |
+
+ NUM_HEADS_QK * HEAD_QK * 2
|
| 235 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 236 |
+
+ tl.arange(0, V_PER_QK * HEAD_V),
|
| 237 |
+
blk_v,
|
| 238 |
+
)
|
| 239 |
+
# Store z
|
| 240 |
+
tl.store(
|
| 241 |
+
z
|
| 242 |
+
+ i_seq * NUM_HEADS_V * HEAD_V
|
| 243 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 244 |
+
+ tl.arange(0, V_PER_QK * HEAD_V),
|
| 245 |
+
blk_z,
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
# Store b, a
|
| 249 |
+
base_ba = mixed_ba + i_seq * NUM_HEADS_QK * BA_DIM_T + i_qk * BA_DIM_T
|
| 250 |
+
for i in tl.static_range(V_PER_QK):
|
| 251 |
+
tl.store(
|
| 252 |
+
b_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i, tl.load(base_ba + i)
|
| 253 |
+
)
|
| 254 |
+
tl.store(
|
| 255 |
+
a_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i,
|
| 256 |
+
tl.load(base_ba + V_PER_QK + i),
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
@triton.jit
|
| 261 |
+
def _fused_qkvzba_prefill_kernel_nqk4(
|
| 262 |
+
mixed_qkv,
|
| 263 |
+
z,
|
| 264 |
+
b_out,
|
| 265 |
+
a_out,
|
| 266 |
+
mixed_qkvz,
|
| 267 |
+
mixed_ba,
|
| 268 |
+
NUM_HEADS_V: tl.constexpr,
|
| 269 |
+
HEAD_QK: tl.constexpr,
|
| 270 |
+
HEAD_V: tl.constexpr,
|
| 271 |
+
):
|
| 272 |
+
"""Prefill kernel specialized for NUM_HEADS_QK=4, single block processes entire row."""
|
| 273 |
+
i_seq = tl.program_id(0)
|
| 274 |
+
NUM_HEADS_QK: tl.constexpr = 4
|
| 275 |
+
V_PER_QK: tl.constexpr = NUM_HEADS_V // NUM_HEADS_QK
|
| 276 |
+
QKVZ_DIM_T: tl.constexpr = HEAD_QK * 2 + V_PER_QK * HEAD_V * 2
|
| 277 |
+
BA_DIM_T: tl.constexpr = V_PER_QK * 2
|
| 278 |
+
QKV_DIM_T: tl.constexpr = NUM_HEADS_QK * HEAD_QK * 2 + NUM_HEADS_V * HEAD_V
|
| 279 |
+
|
| 280 |
+
base_out = mixed_qkv + i_seq * QKV_DIM_T
|
| 281 |
+
|
| 282 |
+
for i_qk in tl.static_range(NUM_HEADS_QK):
|
| 283 |
+
base_in = mixed_qkvz + i_seq * NUM_HEADS_QK * QKVZ_DIM_T + i_qk * QKVZ_DIM_T
|
| 284 |
+
|
| 285 |
+
blk_q = tl.load(base_in + tl.arange(0, HEAD_QK))
|
| 286 |
+
blk_k = tl.load(base_in + HEAD_QK + tl.arange(0, HEAD_QK))
|
| 287 |
+
blk_v = tl.load(base_in + HEAD_QK * 2 + tl.arange(0, V_PER_QK * HEAD_V))
|
| 288 |
+
blk_z = tl.load(
|
| 289 |
+
base_in + HEAD_QK * 2 + V_PER_QK * HEAD_V + tl.arange(0, V_PER_QK * HEAD_V)
|
| 290 |
+
)
|
| 291 |
+
|
| 292 |
+
tl.store(base_out + i_qk * HEAD_QK + tl.arange(0, HEAD_QK), blk_q)
|
| 293 |
+
tl.store(
|
| 294 |
+
base_out + NUM_HEADS_QK * HEAD_QK + i_qk * HEAD_QK + tl.arange(0, HEAD_QK),
|
| 295 |
+
blk_k,
|
| 296 |
+
)
|
| 297 |
+
tl.store(
|
| 298 |
+
base_out
|
| 299 |
+
+ NUM_HEADS_QK * HEAD_QK * 2
|
| 300 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 301 |
+
+ tl.arange(0, V_PER_QK * HEAD_V),
|
| 302 |
+
blk_v,
|
| 303 |
+
)
|
| 304 |
+
tl.store(
|
| 305 |
+
z
|
| 306 |
+
+ i_seq * NUM_HEADS_V * HEAD_V
|
| 307 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 308 |
+
+ tl.arange(0, V_PER_QK * HEAD_V),
|
| 309 |
+
blk_z,
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
base_ba = mixed_ba + i_seq * NUM_HEADS_QK * BA_DIM_T + i_qk * BA_DIM_T
|
| 313 |
+
for i in tl.static_range(V_PER_QK):
|
| 314 |
+
tl.store(
|
| 315 |
+
b_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i, tl.load(base_ba + i)
|
| 316 |
+
)
|
| 317 |
+
tl.store(
|
| 318 |
+
a_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i,
|
| 319 |
+
tl.load(base_ba + V_PER_QK + i),
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
@triton.jit
|
| 324 |
+
def _fused_qkvzba_prefill_kernel_nqk8(
|
| 325 |
+
mixed_qkv,
|
| 326 |
+
z,
|
| 327 |
+
b_out,
|
| 328 |
+
a_out,
|
| 329 |
+
mixed_qkvz,
|
| 330 |
+
mixed_ba,
|
| 331 |
+
NUM_HEADS_V: tl.constexpr,
|
| 332 |
+
HEAD_QK: tl.constexpr,
|
| 333 |
+
HEAD_V: tl.constexpr,
|
| 334 |
+
):
|
| 335 |
+
"""Prefill kernel specialized for NUM_HEADS_QK=8, single block processes entire row."""
|
| 336 |
+
i_seq = tl.program_id(0)
|
| 337 |
+
NUM_HEADS_QK: tl.constexpr = 8
|
| 338 |
+
V_PER_QK: tl.constexpr = NUM_HEADS_V // NUM_HEADS_QK
|
| 339 |
+
QKVZ_DIM_T: tl.constexpr = HEAD_QK * 2 + V_PER_QK * HEAD_V * 2
|
| 340 |
+
BA_DIM_T: tl.constexpr = V_PER_QK * 2
|
| 341 |
+
QKV_DIM_T: tl.constexpr = NUM_HEADS_QK * HEAD_QK * 2 + NUM_HEADS_V * HEAD_V
|
| 342 |
+
|
| 343 |
+
base_out = mixed_qkv + i_seq * QKV_DIM_T
|
| 344 |
+
|
| 345 |
+
for i_qk in tl.static_range(NUM_HEADS_QK):
|
| 346 |
+
base_in = mixed_qkvz + i_seq * NUM_HEADS_QK * QKVZ_DIM_T + i_qk * QKVZ_DIM_T
|
| 347 |
+
|
| 348 |
+
blk_q = tl.load(base_in + tl.arange(0, HEAD_QK))
|
| 349 |
+
blk_k = tl.load(base_in + HEAD_QK + tl.arange(0, HEAD_QK))
|
| 350 |
+
blk_v = tl.load(base_in + HEAD_QK * 2 + tl.arange(0, V_PER_QK * HEAD_V))
|
| 351 |
+
blk_z = tl.load(
|
| 352 |
+
base_in + HEAD_QK * 2 + V_PER_QK * HEAD_V + tl.arange(0, V_PER_QK * HEAD_V)
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
+
tl.store(base_out + i_qk * HEAD_QK + tl.arange(0, HEAD_QK), blk_q)
|
| 356 |
+
tl.store(
|
| 357 |
+
base_out + NUM_HEADS_QK * HEAD_QK + i_qk * HEAD_QK + tl.arange(0, HEAD_QK),
|
| 358 |
+
blk_k,
|
| 359 |
+
)
|
| 360 |
+
tl.store(
|
| 361 |
+
base_out
|
| 362 |
+
+ NUM_HEADS_QK * HEAD_QK * 2
|
| 363 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 364 |
+
+ tl.arange(0, V_PER_QK * HEAD_V),
|
| 365 |
+
blk_v,
|
| 366 |
+
)
|
| 367 |
+
tl.store(
|
| 368 |
+
z
|
| 369 |
+
+ i_seq * NUM_HEADS_V * HEAD_V
|
| 370 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 371 |
+
+ tl.arange(0, V_PER_QK * HEAD_V),
|
| 372 |
+
blk_z,
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
base_ba = mixed_ba + i_seq * NUM_HEADS_QK * BA_DIM_T + i_qk * BA_DIM_T
|
| 376 |
+
for i in tl.static_range(V_PER_QK):
|
| 377 |
+
tl.store(
|
| 378 |
+
b_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i, tl.load(base_ba + i)
|
| 379 |
+
)
|
| 380 |
+
tl.store(
|
| 381 |
+
a_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i,
|
| 382 |
+
tl.load(base_ba + V_PER_QK + i),
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
@triton.jit
|
| 387 |
+
def _fused_qkvzba_split_reshape_cat_prefill_kernel(
|
| 388 |
+
mixed_qkv,
|
| 389 |
+
z,
|
| 390 |
+
b_out,
|
| 391 |
+
a_out,
|
| 392 |
+
mixed_qkvz,
|
| 393 |
+
mixed_ba,
|
| 394 |
+
NUM_HEADS_QK: tl.constexpr,
|
| 395 |
+
NUM_HEADS_V: tl.constexpr,
|
| 396 |
+
HEAD_QK: tl.constexpr,
|
| 397 |
+
HEAD_V: tl.constexpr,
|
| 398 |
+
):
|
| 399 |
+
"""
|
| 400 |
+
Generic prefill kernel (fallback for unsupported NUM_HEADS_QK values).
|
| 401 |
+
|
| 402 |
+
Uses 2D grid where each program processes one (seq_pos, qk_head).
|
| 403 |
+
Note: This may cause cache line conflicts; prefer specialized kernels.
|
| 404 |
+
"""
|
| 405 |
+
i_seq, i_qk = tl.program_id(0), tl.program_id(1)
|
| 406 |
+
|
| 407 |
+
V_PER_QK: tl.constexpr = NUM_HEADS_V // NUM_HEADS_QK
|
| 408 |
+
QKVZ_DIM_T: tl.constexpr = HEAD_QK * 2 + V_PER_QK * HEAD_V * 2
|
| 409 |
+
BA_DIM_T: tl.constexpr = V_PER_QK * 2
|
| 410 |
+
QKV_DIM_T: tl.constexpr = NUM_HEADS_QK * HEAD_QK * 2 + NUM_HEADS_V * HEAD_V
|
| 411 |
+
|
| 412 |
+
# Load from mixed_qkvz
|
| 413 |
+
base_qkvz = mixed_qkvz + i_seq * NUM_HEADS_QK * QKVZ_DIM_T + i_qk * QKVZ_DIM_T
|
| 414 |
+
|
| 415 |
+
q_end: tl.constexpr = HEAD_QK
|
| 416 |
+
blk_q = tl.load(base_qkvz + tl.arange(0, q_end))
|
| 417 |
+
|
| 418 |
+
k_end: tl.constexpr = q_end + HEAD_QK
|
| 419 |
+
blk_k = tl.load(base_qkvz + tl.arange(q_end, k_end))
|
| 420 |
+
|
| 421 |
+
v_end: tl.constexpr = k_end + V_PER_QK * HEAD_V
|
| 422 |
+
blk_v = tl.load(base_qkvz + tl.arange(k_end, v_end))
|
| 423 |
+
|
| 424 |
+
z_end: tl.constexpr = v_end + V_PER_QK * HEAD_V
|
| 425 |
+
blk_z = tl.load(base_qkvz + tl.arange(v_end, z_end))
|
| 426 |
+
|
| 427 |
+
# Load from mixed_ba
|
| 428 |
+
base_ba = mixed_ba + i_seq * NUM_HEADS_QK * BA_DIM_T + i_qk * BA_DIM_T
|
| 429 |
+
|
| 430 |
+
# Store to mixed_qkv (concatenated q, k, v)
|
| 431 |
+
q_out_ptr = mixed_qkv + i_seq * QKV_DIM_T + i_qk * HEAD_QK + tl.arange(0, HEAD_QK)
|
| 432 |
+
tl.store(q_out_ptr, blk_q)
|
| 433 |
+
|
| 434 |
+
k_out_ptr = (
|
| 435 |
+
mixed_qkv
|
| 436 |
+
+ i_seq * QKV_DIM_T
|
| 437 |
+
+ NUM_HEADS_QK * HEAD_QK
|
| 438 |
+
+ i_qk * HEAD_QK
|
| 439 |
+
+ tl.arange(0, HEAD_QK)
|
| 440 |
+
)
|
| 441 |
+
tl.store(k_out_ptr, blk_k)
|
| 442 |
+
|
| 443 |
+
v_out_ptr = (
|
| 444 |
+
mixed_qkv
|
| 445 |
+
+ i_seq * QKV_DIM_T
|
| 446 |
+
+ NUM_HEADS_QK * HEAD_QK * 2
|
| 447 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 448 |
+
+ tl.arange(0, V_PER_QK * HEAD_V)
|
| 449 |
+
)
|
| 450 |
+
tl.store(v_out_ptr, blk_v)
|
| 451 |
+
|
| 452 |
+
# Store z
|
| 453 |
+
z_out_ptr = (
|
| 454 |
+
z
|
| 455 |
+
+ i_seq * NUM_HEADS_V * HEAD_V
|
| 456 |
+
+ i_qk * V_PER_QK * HEAD_V
|
| 457 |
+
+ tl.arange(0, V_PER_QK * HEAD_V)
|
| 458 |
+
)
|
| 459 |
+
tl.store(z_out_ptr, blk_z)
|
| 460 |
+
|
| 461 |
+
# Store b, a
|
| 462 |
+
for i in tl.static_range(V_PER_QK):
|
| 463 |
+
blk_b_ptr = base_ba + i
|
| 464 |
+
blk_b_st_ptr = b_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i
|
| 465 |
+
tl.store(blk_b_st_ptr, tl.load(blk_b_ptr))
|
| 466 |
+
|
| 467 |
+
blk_a_ptr = base_ba + V_PER_QK + i
|
| 468 |
+
blk_a_st_ptr = a_out + i_seq * NUM_HEADS_V + i_qk * V_PER_QK + i
|
| 469 |
+
tl.store(blk_a_st_ptr, tl.load(blk_a_ptr))
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def fused_qkvzba_split_reshape_cat_prefill(
|
| 473 |
+
mixed_qkvz: torch.Tensor,
|
| 474 |
+
mixed_ba: torch.Tensor,
|
| 475 |
+
num_heads_qk: int,
|
| 476 |
+
num_heads_v: int,
|
| 477 |
+
head_qk: int,
|
| 478 |
+
head_v: int,
|
| 479 |
+
):
|
| 480 |
+
"""Prefill stage fused function."""
|
| 481 |
+
seq_len = mixed_qkvz.shape[0]
|
| 482 |
+
|
| 483 |
+
qkv_dim = num_heads_qk * head_qk * 2 + num_heads_v * head_v
|
| 484 |
+
mixed_qkv = torch.empty(
|
| 485 |
+
[seq_len, qkv_dim],
|
| 486 |
+
dtype=mixed_qkvz.dtype,
|
| 487 |
+
device=mixed_qkvz.device,
|
| 488 |
+
)
|
| 489 |
+
z = torch.empty(
|
| 490 |
+
[seq_len, num_heads_v, head_v],
|
| 491 |
+
dtype=mixed_qkvz.dtype,
|
| 492 |
+
device=mixed_qkvz.device,
|
| 493 |
+
)
|
| 494 |
+
b = torch.empty(
|
| 495 |
+
[seq_len, num_heads_v],
|
| 496 |
+
dtype=mixed_ba.dtype,
|
| 497 |
+
device=mixed_ba.device,
|
| 498 |
+
)
|
| 499 |
+
a = torch.empty_like(b)
|
| 500 |
+
|
| 501 |
+
# Select specialized kernel based on num_heads_qk
|
| 502 |
+
if num_heads_qk == 1:
|
| 503 |
+
grid = (seq_len,)
|
| 504 |
+
_fused_qkvzba_prefill_kernel_nqk1[grid](
|
| 505 |
+
mixed_qkv,
|
| 506 |
+
z,
|
| 507 |
+
b,
|
| 508 |
+
a,
|
| 509 |
+
mixed_qkvz,
|
| 510 |
+
mixed_ba,
|
| 511 |
+
num_heads_v,
|
| 512 |
+
head_qk,
|
| 513 |
+
head_v,
|
| 514 |
+
num_warps=1,
|
| 515 |
+
num_stages=3,
|
| 516 |
+
)
|
| 517 |
+
elif num_heads_qk == 2:
|
| 518 |
+
grid = (seq_len,)
|
| 519 |
+
_fused_qkvzba_prefill_kernel_nqk2[grid](
|
| 520 |
+
mixed_qkv,
|
| 521 |
+
z,
|
| 522 |
+
b,
|
| 523 |
+
a,
|
| 524 |
+
mixed_qkvz,
|
| 525 |
+
mixed_ba,
|
| 526 |
+
num_heads_v,
|
| 527 |
+
head_qk,
|
| 528 |
+
head_v,
|
| 529 |
+
num_warps=1,
|
| 530 |
+
num_stages=3,
|
| 531 |
+
)
|
| 532 |
+
elif num_heads_qk == 4:
|
| 533 |
+
grid = (seq_len,)
|
| 534 |
+
_fused_qkvzba_prefill_kernel_nqk4[grid](
|
| 535 |
+
mixed_qkv,
|
| 536 |
+
z,
|
| 537 |
+
b,
|
| 538 |
+
a,
|
| 539 |
+
mixed_qkvz,
|
| 540 |
+
mixed_ba,
|
| 541 |
+
num_heads_v,
|
| 542 |
+
head_qk,
|
| 543 |
+
head_v,
|
| 544 |
+
num_warps=1,
|
| 545 |
+
num_stages=3,
|
| 546 |
+
)
|
| 547 |
+
elif num_heads_qk == 8:
|
| 548 |
+
grid = (seq_len,)
|
| 549 |
+
_fused_qkvzba_prefill_kernel_nqk8[grid](
|
| 550 |
+
mixed_qkv,
|
| 551 |
+
z,
|
| 552 |
+
b,
|
| 553 |
+
a,
|
| 554 |
+
mixed_qkvz,
|
| 555 |
+
mixed_ba,
|
| 556 |
+
num_heads_v,
|
| 557 |
+
head_qk,
|
| 558 |
+
head_v,
|
| 559 |
+
num_warps=1,
|
| 560 |
+
num_stages=3,
|
| 561 |
+
)
|
| 562 |
+
else:
|
| 563 |
+
# Fallback to generic 2D-grid kernel
|
| 564 |
+
grid = (seq_len, num_heads_qk)
|
| 565 |
+
_fused_qkvzba_split_reshape_cat_prefill_kernel[grid](
|
| 566 |
+
mixed_qkv,
|
| 567 |
+
z,
|
| 568 |
+
b,
|
| 569 |
+
a,
|
| 570 |
+
mixed_qkvz,
|
| 571 |
+
mixed_ba,
|
| 572 |
+
num_heads_qk,
|
| 573 |
+
num_heads_v,
|
| 574 |
+
head_qk,
|
| 575 |
+
head_v,
|
| 576 |
+
num_warps=1,
|
| 577 |
+
num_stages=3,
|
| 578 |
+
)
|
| 579 |
+
|
| 580 |
+
return mixed_qkv, z, b, a
|
build/torch-rocm/_triton_kernels/gated_delta_rule/gated_delta_rule_utils.py
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (C) 2023-2026, Songlin Yang, Yu Zhang
|
| 2 |
+
|
| 3 |
+
import contextlib
|
| 4 |
+
import functools
|
| 5 |
+
import inspect
|
| 6 |
+
import logging
|
| 7 |
+
import math
|
| 8 |
+
import os
|
| 9 |
+
import sys
|
| 10 |
+
import warnings
|
| 11 |
+
from collections.abc import Callable
|
| 12 |
+
from enum import Enum
|
| 13 |
+
from functools import lru_cache
|
| 14 |
+
from typing import TYPE_CHECKING, Any
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
import triton
|
| 18 |
+
from packaging import version
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
# Autotune cache support
|
| 23 |
+
SUPPORTS_AUTOTUNE_CACHE = (
|
| 24 |
+
"cache_results" in inspect.signature(triton.autotune).parameters
|
| 25 |
+
)
|
| 26 |
+
FLA_CACHE_RESULTS = os.getenv("FLA_CACHE_RESULTS", "1") == "1"
|
| 27 |
+
autotune_cache_kwargs = (
|
| 28 |
+
{"cache_results": FLA_CACHE_RESULTS} if SUPPORTS_AUTOTUNE_CACHE else {}
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
from fla import __version__
|
| 33 |
+
|
| 34 |
+
FLA_CI_ENV = os.getenv("FLA_CI_ENV") == "1"
|
| 35 |
+
FLA_CACHE_RESULTS = os.getenv("FLA_CACHE_RESULTS", "1") == "1"
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
SUPPORTS_AUTOTUNE_CACHE = (
|
| 39 |
+
"cache_results" in inspect.signature(triton.autotune).parameters
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
autotune_cache_kwargs = (
|
| 43 |
+
{"cache_results": FLA_CACHE_RESULTS} if SUPPORTS_AUTOTUNE_CACHE else {}
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
GATED_DELTA_RULE_TRITON_AUTOTUNE = os.environ.get(
|
| 47 |
+
"GATED_DELTA_RULE_TRITON_AUTOTUNE", "0"
|
| 48 |
+
).lower() in ("1", "true", "yes", "on")
|
| 49 |
+
|
| 50 |
+
# log2(e) == 1/ln(2). Converts natural-log gate values to log2 space so
|
| 51 |
+
# kernels can use exp2 instead of exp. Python/wrapper-only: pass into kernels
|
| 52 |
+
# as a constexpr scale (e.g. G_SCALE); never reference inside @triton.jit kernels.
|
| 53 |
+
RCP_LN2: float = math.log2(math.e)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def gated_delta_rule_autotune_configs(
|
| 57 |
+
configs: list[triton.Config], default_config: triton.Config | None = None
|
| 58 |
+
) -> list[triton.Config]:
|
| 59 |
+
"""
|
| 60 |
+
Select Triton autotune configs based on the gated delta rule env flag.
|
| 61 |
+
|
| 62 |
+
When ``GATED_DELTA_RULE_TRITON_AUTOTUNE`` is enabled, return the full config
|
| 63 |
+
list so ``@triton.autotune`` benchmarks candidate kernels. When disabled,
|
| 64 |
+
return only ``default_config`` (or the first config) to skip tuning overhead
|
| 65 |
+
while keeping the decorator shape uniform across decode and prefill kernels.
|
| 66 |
+
"""
|
| 67 |
+
if GATED_DELTA_RULE_TRITON_AUTOTUNE:
|
| 68 |
+
return configs
|
| 69 |
+
cfg = default_config if default_config is not None else configs[0]
|
| 70 |
+
return [cfg]
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
@lru_cache(maxsize=1)
|
| 74 |
+
def check_environments():
|
| 75 |
+
"""
|
| 76 |
+
Checks the current operating system, Triton version, and Python version,
|
| 77 |
+
issuing warnings if they don't meet recommendations.
|
| 78 |
+
This function's body only runs once due to lru_cache.
|
| 79 |
+
"""
|
| 80 |
+
# Check Operating System
|
| 81 |
+
if sys.platform == "win32":
|
| 82 |
+
logger.warning(
|
| 83 |
+
"Detected Windows operating system. Triton does not have an official Windows release, "
|
| 84 |
+
"thus FLA will not be adapted for Windows, and any potential errors will not be fixed. "
|
| 85 |
+
"Please consider using a Linux environment for compatibility.",
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
triton_version = version.parse(triton.__version__)
|
| 89 |
+
required_triton_version = version.parse("3.2.0")
|
| 90 |
+
|
| 91 |
+
if triton_version < required_triton_version:
|
| 92 |
+
logger.warning(
|
| 93 |
+
f"Current Triton version {triton_version} is below the recommended 3.2.0 version. "
|
| 94 |
+
"Errors may occur and these issues will not be fixed. "
|
| 95 |
+
"Please consider upgrading Triton.",
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
# Check Python version
|
| 99 |
+
py_version = version.parse(f"{sys.version_info.major}.{sys.version_info.minor}")
|
| 100 |
+
required_py_version = version.parse("3.11")
|
| 101 |
+
|
| 102 |
+
if py_version < required_py_version:
|
| 103 |
+
logger.warning(
|
| 104 |
+
f"Current Python version {py_version} is below the recommended 3.11 version. "
|
| 105 |
+
"It is recommended to upgrade to Python 3.11 or higher for the best experience.",
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
return None
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
check_environments()
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def get_abs_err(x, y):
|
| 115 |
+
return (x.detach() - y.detach()).flatten().abs().max().item()
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def get_err_ratio(x, y):
|
| 119 |
+
err = (x.detach() - y.detach()).flatten().square().mean().sqrt().item()
|
| 120 |
+
base = (x.detach()).flatten().square().mean().sqrt().item()
|
| 121 |
+
return err / (base + 1e-8)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def assert_close(prefix, ref, tri, ratio, warning=False, err_atol=1e-6):
|
| 125 |
+
abs_atol = get_abs_err(ref, tri)
|
| 126 |
+
msg = f"{prefix:>16} diff: {abs_atol:.6f} ratio: {get_err_ratio(ref, tri):.6f}"
|
| 127 |
+
logger.info(msg)
|
| 128 |
+
error_rate = get_err_ratio(ref, tri)
|
| 129 |
+
if abs_atol <= err_atol:
|
| 130 |
+
return
|
| 131 |
+
if warning or (FLA_CI_ENV and (error_rate < 0.01 or abs_atol <= 0.3)):
|
| 132 |
+
if error_rate > ratio:
|
| 133 |
+
warnings.warn(msg)
|
| 134 |
+
else:
|
| 135 |
+
assert error_rate < ratio, msg
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def tensor_cache(
|
| 139 |
+
fn: Callable[..., torch.Tensor],
|
| 140 |
+
) -> Callable[..., torch.Tensor]:
|
| 141 |
+
"""
|
| 142 |
+
A decorator that caches the most recent result of a function with tensor inputs.
|
| 143 |
+
|
| 144 |
+
This decorator will store the output of the decorated function for the most recent set of input tensors.
|
| 145 |
+
If the function is called again with the same input tensors, it will return the cached result.
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
Args:
|
| 149 |
+
fn (Callable[..., torch.Tensor]):
|
| 150 |
+
The function to be decorated. It should take tensor inputs and return tensor outputs.
|
| 151 |
+
|
| 152 |
+
Returns:
|
| 153 |
+
Callable[..., torch.Tensor]:
|
| 154 |
+
A wrapped version of the input function with single-entry caching.
|
| 155 |
+
"""
|
| 156 |
+
last_args: tuple | None = None
|
| 157 |
+
last_kwargs: dict | None = None
|
| 158 |
+
last_result: Any = None
|
| 159 |
+
|
| 160 |
+
@functools.wraps(fn)
|
| 161 |
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
| 162 |
+
nonlocal last_args, last_kwargs, last_result
|
| 163 |
+
|
| 164 |
+
if last_args is not None and last_kwargs is not None:
|
| 165 |
+
if len(args) == len(last_args) and len(kwargs) == len(last_kwargs):
|
| 166 |
+
if all(a is b for a, b in zip(args, last_args, strict=False)) and all(
|
| 167 |
+
k in last_kwargs and v is last_kwargs[k] for k, v in kwargs.items()
|
| 168 |
+
):
|
| 169 |
+
return last_result
|
| 170 |
+
|
| 171 |
+
result = fn(*args, **kwargs)
|
| 172 |
+
last_args, last_kwargs, last_result = args, kwargs, result
|
| 173 |
+
return result
|
| 174 |
+
|
| 175 |
+
return wrapper
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def input_guard(
|
| 179 |
+
fn: Callable[..., torch.Tensor],
|
| 180 |
+
) -> Callable[..., torch.Tensor]:
|
| 181 |
+
"""
|
| 182 |
+
A decorator to make sure all input tensors are contiguous and set the device based on input tensors.
|
| 183 |
+
"""
|
| 184 |
+
|
| 185 |
+
@functools.wraps(fn)
|
| 186 |
+
def wrapper(*args, **kwargs):
|
| 187 |
+
contiguous_args = (
|
| 188 |
+
i if not isinstance(i, torch.Tensor) else i.contiguous() for i in args
|
| 189 |
+
)
|
| 190 |
+
contiguous_kwargs = {
|
| 191 |
+
k: (v if not isinstance(v, torch.Tensor) else v.contiguous())
|
| 192 |
+
for k, v in kwargs.items()
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
tensor = None
|
| 196 |
+
for arg in args:
|
| 197 |
+
if isinstance(arg, torch.Tensor):
|
| 198 |
+
tensor = arg
|
| 199 |
+
break
|
| 200 |
+
if tensor is None:
|
| 201 |
+
for value in kwargs.values():
|
| 202 |
+
if isinstance(value, torch.Tensor):
|
| 203 |
+
tensor = value
|
| 204 |
+
break
|
| 205 |
+
|
| 206 |
+
if tensor is not None:
|
| 207 |
+
ctx = custom_device_ctx(tensor.device.index)
|
| 208 |
+
else:
|
| 209 |
+
ctx = contextlib.nullcontext()
|
| 210 |
+
|
| 211 |
+
with ctx:
|
| 212 |
+
return fn(*contiguous_args, **contiguous_kwargs)
|
| 213 |
+
|
| 214 |
+
return wrapper
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
contiguous = input_guard
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def require_version(version, hint):
|
| 221 |
+
"""
|
| 222 |
+
Perform a runtime check of the dependency versions, using the exact same syntax used by pip.
|
| 223 |
+
"""
|
| 224 |
+
|
| 225 |
+
def decorator(fn):
|
| 226 |
+
@functools.wraps(fn)
|
| 227 |
+
def wrapper(ctx, *args, **kwargs):
|
| 228 |
+
from transformers.utils.versions import require_version
|
| 229 |
+
|
| 230 |
+
require_version(version, hint)
|
| 231 |
+
return fn(
|
| 232 |
+
ctx,
|
| 233 |
+
*(
|
| 234 |
+
i if not isinstance(i, torch.Tensor) else i.contiguous()
|
| 235 |
+
for i in args
|
| 236 |
+
),
|
| 237 |
+
**{
|
| 238 |
+
k: (v if not isinstance(v, torch.Tensor) else v.contiguous())
|
| 239 |
+
for k, v in kwargs.items()
|
| 240 |
+
},
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
return wrapper
|
| 244 |
+
|
| 245 |
+
return decorator
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
class Action(Enum):
|
| 249 |
+
NONE = "none"
|
| 250 |
+
NOTIFY = "notify"
|
| 251 |
+
NOTIFY_ALWAYS = "notify_always"
|
| 252 |
+
RAISE = "raise"
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def deprecate_kwarg(
|
| 256 |
+
old_name: str,
|
| 257 |
+
version: str,
|
| 258 |
+
new_name: str | None = None,
|
| 259 |
+
warn_if_greater_or_equal_version: bool = False,
|
| 260 |
+
raise_if_greater_or_equal_version: bool = False,
|
| 261 |
+
raise_if_both_names: bool = False,
|
| 262 |
+
additional_message: str | None = None,
|
| 263 |
+
):
|
| 264 |
+
"""
|
| 265 |
+
Decorator to notify users about deprecated keyword arguments, replacing them with a new name if specified.
|
| 266 |
+
|
| 267 |
+
This decorator allows you to:
|
| 268 |
+
- Notify users when a keyword argument is deprecated.
|
| 269 |
+
- Automatically replace deprecated keyword arguments with new ones.
|
| 270 |
+
- Raise an error if deprecated arguments are used, depending on the specified conditions.
|
| 271 |
+
|
| 272 |
+
By default, the decorator notifies the user about the deprecated argument while the `fla.__version__` < specified `version`
|
| 273 |
+
in the decorator. To keep notifications with any version `warn_if_greater_or_equal_version=True` can be set.
|
| 274 |
+
|
| 275 |
+
Args:
|
| 276 |
+
old_name (`str`):
|
| 277 |
+
Name of the deprecated keyword argument.
|
| 278 |
+
version (`str`):
|
| 279 |
+
The version in which the keyword argument was (or will be) deprecated.
|
| 280 |
+
new_name (`Optional[str]`, *optional*):
|
| 281 |
+
The new name for the deprecated keyword argument.
|
| 282 |
+
If specified, the deprecated keyword argument will be replaced with this new name.
|
| 283 |
+
warn_if_greater_or_equal_version (`bool`, *optional*, defaults to `False`):
|
| 284 |
+
Whether to show warning if current `fla` version is greater or equal to the deprecated version.
|
| 285 |
+
raise_if_greater_or_equal_version (`bool`, *optional*, defaults to `False`):
|
| 286 |
+
Whether to raise `ValueError` if current `fla` version is greater or equal to the deprecated version.
|
| 287 |
+
raise_if_both_names (`bool`, *optional*, defaults to `False`):
|
| 288 |
+
Whether to raise `ValueError` if both deprecated and new keyword arguments are set.
|
| 289 |
+
additional_message (`Optional[str]`, *optional*):
|
| 290 |
+
An additional message to append to the default deprecation message.
|
| 291 |
+
|
| 292 |
+
Raises:
|
| 293 |
+
ValueError:
|
| 294 |
+
If `raise_if_greater_or_equal_version` is `True` and the current version >= the deprecated one,
|
| 295 |
+
or if `raise_if_both_names` is `True` and both old and new keyword arguments are provided.
|
| 296 |
+
|
| 297 |
+
Returns:
|
| 298 |
+
Callable:
|
| 299 |
+
A wrapped function that handles the deprecated keyword arguments according to the specified parameters.
|
| 300 |
+
|
| 301 |
+
Example usage with renaming argument:
|
| 302 |
+
|
| 303 |
+
```python
|
| 304 |
+
@deprecate_kwarg("reduce_labels", new_name="do_reduce_labels", version="6.0.0")
|
| 305 |
+
def my_function(do_reduce_labels):
|
| 306 |
+
print(do_reduce_labels)
|
| 307 |
+
|
| 308 |
+
my_function(reduce_labels=True) # Will show a deprecation warning and use do_reduce_labels=True
|
| 309 |
+
```
|
| 310 |
+
|
| 311 |
+
Example usage without renaming argument:
|
| 312 |
+
|
| 313 |
+
```python
|
| 314 |
+
@deprecate_kwarg("max_size", version="6.0.0")
|
| 315 |
+
def my_function(max_size):
|
| 316 |
+
print(max_size)
|
| 317 |
+
|
| 318 |
+
my_function(max_size=1333) # Will show a deprecation warning
|
| 319 |
+
```
|
| 320 |
+
|
| 321 |
+
"""
|
| 322 |
+
deprecated_version = version.parse(version)
|
| 323 |
+
current_version = version.parse(__version__)
|
| 324 |
+
is_greater_or_equal_version = current_version >= deprecated_version
|
| 325 |
+
|
| 326 |
+
if is_greater_or_equal_version:
|
| 327 |
+
version_message = f"and removed starting from version {version}"
|
| 328 |
+
else:
|
| 329 |
+
version_message = f"and will be removed in version {version}"
|
| 330 |
+
|
| 331 |
+
def wrapper(func):
|
| 332 |
+
# Required for better warning message
|
| 333 |
+
sig = inspect.signature(func)
|
| 334 |
+
function_named_args = set(sig.parameters.keys())
|
| 335 |
+
is_instance_method = "self" in function_named_args
|
| 336 |
+
is_class_method = "cls" in function_named_args
|
| 337 |
+
|
| 338 |
+
@functools.wraps(func)
|
| 339 |
+
def wrapped_func(*args, **kwargs):
|
| 340 |
+
# Get class + function name (just for better warning message)
|
| 341 |
+
func_name = func.__name__
|
| 342 |
+
if is_instance_method:
|
| 343 |
+
func_name = f"{args[0].__class__.__name__}.{func_name}"
|
| 344 |
+
elif is_class_method:
|
| 345 |
+
func_name = f"{args[0].__name__}.{func_name}"
|
| 346 |
+
|
| 347 |
+
minimum_action = Action.NONE
|
| 348 |
+
message = None
|
| 349 |
+
|
| 350 |
+
# deprecated kwarg and its new version are set for function call -> replace it with new name
|
| 351 |
+
if old_name in kwargs and new_name in kwargs:
|
| 352 |
+
minimum_action = (
|
| 353 |
+
Action.RAISE if raise_if_both_names else Action.NOTIFY_ALWAYS
|
| 354 |
+
)
|
| 355 |
+
message = (
|
| 356 |
+
f"Both `{old_name}` and `{new_name}` are set for `{func_name}`. "
|
| 357 |
+
f"Using `{new_name}={kwargs[new_name]}` and ignoring deprecated `{old_name}={kwargs[old_name]}`."
|
| 358 |
+
)
|
| 359 |
+
kwargs.pop(old_name)
|
| 360 |
+
|
| 361 |
+
# only deprecated kwarg is set for function call -> replace it with new name
|
| 362 |
+
elif old_name in kwargs and new_name is not None and new_name not in kwargs:
|
| 363 |
+
minimum_action = Action.NOTIFY
|
| 364 |
+
message = (
|
| 365 |
+
f"`{old_name}` is deprecated {version_message} for `{func_name}`. "
|
| 366 |
+
f"Use `{new_name}` instead."
|
| 367 |
+
)
|
| 368 |
+
kwargs[new_name] = kwargs.pop(old_name)
|
| 369 |
+
|
| 370 |
+
# deprecated kwarg is not set for function call and new name is not specified -> just notify
|
| 371 |
+
elif old_name in kwargs:
|
| 372 |
+
minimum_action = Action.NOTIFY
|
| 373 |
+
message = (
|
| 374 |
+
f"`{old_name}` is deprecated {version_message} for `{func_name}`."
|
| 375 |
+
)
|
| 376 |
+
|
| 377 |
+
if message is not None and additional_message is not None:
|
| 378 |
+
message = f"{message} {additional_message}"
|
| 379 |
+
|
| 380 |
+
# update minimum_action if argument is ALREADY deprecated (current version >= deprecated version)
|
| 381 |
+
if is_greater_or_equal_version:
|
| 382 |
+
# change to (NOTIFY, NOTIFY_ALWAYS) -> RAISE if specified
|
| 383 |
+
# in case we want to raise error for already deprecated arguments
|
| 384 |
+
if raise_if_greater_or_equal_version and minimum_action != Action.NONE:
|
| 385 |
+
minimum_action = Action.RAISE
|
| 386 |
+
|
| 387 |
+
# change to NOTIFY -> NONE if specified (NOTIFY_ALWAYS can't be changed to NONE)
|
| 388 |
+
# in case we want to ignore notifications for already deprecated arguments
|
| 389 |
+
elif (
|
| 390 |
+
not warn_if_greater_or_equal_version
|
| 391 |
+
and minimum_action == Action.NOTIFY
|
| 392 |
+
):
|
| 393 |
+
minimum_action = Action.NONE
|
| 394 |
+
|
| 395 |
+
# raise error or notify user
|
| 396 |
+
if minimum_action == Action.RAISE:
|
| 397 |
+
raise ValueError(message)
|
| 398 |
+
elif minimum_action in (Action.NOTIFY, Action.NOTIFY_ALWAYS):
|
| 399 |
+
# DeprecationWarning is ignored by default, so we use FutureWarning instead
|
| 400 |
+
warnings.warn(message, FutureWarning, stacklevel=2)
|
| 401 |
+
|
| 402 |
+
return func(*args, **kwargs)
|
| 403 |
+
|
| 404 |
+
return wrapped_func
|
| 405 |
+
|
| 406 |
+
return wrapper
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
def checkpoint(fn):
|
| 410 |
+
def wrapper(*args, **kwargs):
|
| 411 |
+
return torch.utils.checkpoint.checkpoint(fn, *args, **kwargs)
|
| 412 |
+
|
| 413 |
+
return wrapper
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
@functools.cache
|
| 417 |
+
def check_pytorch_version(version_s: str = "2.4") -> bool:
|
| 418 |
+
return version.parse(torch.__version__) >= version.parse(version_s)
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def _cpu_device_warning():
|
| 422 |
+
warnings.warn(
|
| 423 |
+
("Triton is not supported on current platform, roll back to CPU."), stacklevel=1
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
@functools.cache
|
| 428 |
+
def get_multiprocessor_count(tensor_idx: int = 0) -> int:
|
| 429 |
+
try:
|
| 430 |
+
return triton.runtime.driver.active.utils.get_device_properties(tensor_idx)[
|
| 431 |
+
"multiprocessor_count"
|
| 432 |
+
]
|
| 433 |
+
except BaseException:
|
| 434 |
+
# Maybe we use a NPU device.
|
| 435 |
+
if triton.runtime.driver.active.get_current_target().backend == "npu":
|
| 436 |
+
return triton.runtime.driver.active.utils.get_device_properties(tensor_idx)[
|
| 437 |
+
"num_vectorcore"
|
| 438 |
+
]
|
| 439 |
+
else:
|
| 440 |
+
return 1
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
@functools.cache
|
| 444 |
+
def get_available_device() -> str:
|
| 445 |
+
try:
|
| 446 |
+
return triton.runtime.driver.active.get_current_target().backend
|
| 447 |
+
except BaseException:
|
| 448 |
+
_cpu_device_warning()
|
| 449 |
+
return "cpu"
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def map_triton_backend_to_torch_device() -> str:
|
| 453 |
+
backend = get_available_device() # 'cuda' | 'hip' | 'xpu' | 'cpu' | ...
|
| 454 |
+
return {"cuda": "cuda", "hip": "cuda", "xpu": "xpu"}.get(backend, backend)
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
# For AMD GPUs, the triton backend is 'hip', while for Nvidia GPUs, the triton backend is 'cuda'.
|
| 458 |
+
# However, the torch backend is 'cuda' for both Nvidia and AMD GPUs.
|
| 459 |
+
# Therefore, we need to check the triton backend to determine the actual GPU vendor.
|
| 460 |
+
device = get_available_device() if get_available_device() != "hip" else "cuda"
|
| 461 |
+
device_torch_lib = getattr(torch, device)
|
| 462 |
+
device_platform = get_available_device()
|
| 463 |
+
device_name = map_triton_backend_to_torch_device()
|
| 464 |
+
|
| 465 |
+
IS_AMD = device_platform == "hip"
|
| 466 |
+
IS_INTEL = device_platform == "xpu"
|
| 467 |
+
IS_NVIDIA = device_platform == "cuda"
|
| 468 |
+
IS_INTEL_ALCHEMIST = IS_INTEL and "Intel(R) Arc(TM) A" in torch.xpu.get_device_name(0)
|
| 469 |
+
IS_NVIDIA_HOPPER = IS_NVIDIA and (
|
| 470 |
+
"NVIDIA H" in torch.cuda.get_device_name(0)
|
| 471 |
+
or torch.cuda.get_device_capability()[0] >= 9
|
| 472 |
+
)
|
| 473 |
+
USE_CUDA_GRAPH = IS_NVIDIA and os.environ.get("FLA_USE_CUDA_GRAPH", "0") == "1"
|
| 474 |
+
|
| 475 |
+
# Nvidia Ampere or newer, haven't check AMD and intel yet.
|
| 476 |
+
IS_TF32_SUPPORTED = IS_NVIDIA and torch.cuda.get_device_capability(0)[0] >= 8
|
| 477 |
+
IS_GATHER_SUPPORTED = hasattr(triton.language, "gather")
|
| 478 |
+
IS_TMA_SUPPORTED = (
|
| 479 |
+
(IS_NVIDIA and torch.cuda.get_device_capability(0)[0] >= 9)
|
| 480 |
+
and os.environ.get("FLA_USE_TMA", "0") == "1"
|
| 481 |
+
and (
|
| 482 |
+
hasattr(triton.language, "_experimental_make_tensor_descriptor")
|
| 483 |
+
or hasattr(triton.language, "make_tensor_descriptor")
|
| 484 |
+
)
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
if IS_NVIDIA and not IS_TF32_SUPPORTED:
|
| 488 |
+
# Make old card happy, since triton will use tf32 by default.
|
| 489 |
+
# This is a workaround for old nvidia card.
|
| 490 |
+
os.environ["TRITON_F32_DEFAULT"] = "ieee"
|
| 491 |
+
|
| 492 |
+
if IS_TMA_SUPPORTED:
|
| 493 |
+
logger.info("TMA is supported, using TMA by default.")
|
| 494 |
+
|
| 495 |
+
def alloc_fn(size: int, alignment: int, stream: int | None):
|
| 496 |
+
return torch.empty(
|
| 497 |
+
size,
|
| 498 |
+
device=torch.device(device_name, device_torch_lib.current_device()),
|
| 499 |
+
dtype=torch.int8,
|
| 500 |
+
)
|
| 501 |
+
|
| 502 |
+
triton.set_allocator(alloc_fn)
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
def get_all_max_shared_mem():
|
| 506 |
+
try:
|
| 507 |
+
return [
|
| 508 |
+
triton.runtime.driver.active.utils.get_device_properties(i)[
|
| 509 |
+
"max_shared_mem"
|
| 510 |
+
]
|
| 511 |
+
for i in range(device_torch_lib.device_count())
|
| 512 |
+
]
|
| 513 |
+
except BaseException:
|
| 514 |
+
_cpu_device_warning()
|
| 515 |
+
return [-1]
|
| 516 |
+
|
| 517 |
+
|
| 518 |
+
class Backend(Enum):
|
| 519 |
+
ADA = 101376 # RTX 4090
|
| 520 |
+
AMPERE = 166912 # A100
|
| 521 |
+
HOPPER = 232448 # H100
|
| 522 |
+
DEFAULT = 102400 # Default
|
| 523 |
+
|
| 524 |
+
@classmethod
|
| 525 |
+
def get_shared_memory(cls, arch: str) -> int:
|
| 526 |
+
try:
|
| 527 |
+
return cls[arch.upper()].value
|
| 528 |
+
except KeyError:
|
| 529 |
+
return cls.DEFAULT.value
|
| 530 |
+
|
| 531 |
+
|
| 532 |
+
@functools.cache
|
| 533 |
+
def check_shared_mem(arch: str = "none", tensor_idx: int = 0) -> bool:
|
| 534 |
+
try:
|
| 535 |
+
device_shared_mem_list = get_all_max_shared_mem()
|
| 536 |
+
max_shared_memory = device_shared_mem_list[tensor_idx]
|
| 537 |
+
return max_shared_memory >= Backend.get_shared_memory(arch)
|
| 538 |
+
except Exception:
|
| 539 |
+
return False
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
if check_pytorch_version("2.4"):
|
| 543 |
+
device = "cuda" if device == "cpu" else device
|
| 544 |
+
autocast_custom_fwd = functools.partial(torch.amp.custom_fwd, device_type=device)
|
| 545 |
+
autocast_custom_bwd = functools.partial(torch.amp.custom_bwd, device_type=device)
|
| 546 |
+
|
| 547 |
+
def custom_device_ctx(index: int):
|
| 548 |
+
return device_torch_lib.device(index)
|
| 549 |
+
|
| 550 |
+
else:
|
| 551 |
+
assert (
|
| 552 |
+
device == "cuda"
|
| 553 |
+
), "Only cuda device is supported for PyTorch version < 2.4.0."
|
| 554 |
+
autocast_custom_fwd = device_torch_lib.amp.custom_fwd
|
| 555 |
+
autocast_custom_bwd = device_torch_lib.amp.custom_bwd
|
| 556 |
+
|
| 557 |
+
def custom_device_ctx(index: int):
|
| 558 |
+
return torch.cuda.device(index)
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
def _register_aliases():
|
| 562 |
+
current_module = sys.modules[__name__]
|
| 563 |
+
for key in (
|
| 564 |
+
"IS_AMD",
|
| 565 |
+
"IS_INTEL",
|
| 566 |
+
"IS_NVIDIA",
|
| 567 |
+
"IS_INTEL_ALCHEMIST",
|
| 568 |
+
"IS_NVIDIA_HOPPER",
|
| 569 |
+
"USE_CUDA_GRAPH",
|
| 570 |
+
"IS_TF32_SUPPORTED",
|
| 571 |
+
"IS_GATHER_SUPPORTED",
|
| 572 |
+
"IS_TMA_SUPPORTED",
|
| 573 |
+
):
|
| 574 |
+
if hasattr(current_module, key):
|
| 575 |
+
setattr(current_module, key.lower(), getattr(current_module, key))
|
| 576 |
+
|
| 577 |
+
|
| 578 |
+
_register_aliases()
|
| 579 |
+
|
| 580 |
+
del _register_aliases
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/__init__.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Gated Delta Rule Prefill Operations (Forward Only).
|
| 7 |
+
|
| 8 |
+
This module provides optimized Triton kernels for prefill/training operations.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from .chunk import (
|
| 12 |
+
chunk_gated_delta_rule_fwd,
|
| 13 |
+
chunk_gated_delta_rule_fwd_opt,
|
| 14 |
+
chunk_gated_delta_rule_fwd_opt_vk,
|
| 15 |
+
)
|
| 16 |
+
from .chunk_delta_h import (
|
| 17 |
+
chunk_gated_delta_rule_fwd_h,
|
| 18 |
+
chunk_gated_delta_rule_fwd_h_opt,
|
| 19 |
+
chunk_gated_delta_rule_fwd_h_opt_vk,
|
| 20 |
+
)
|
| 21 |
+
from .chunk_o import chunk_fwd_o, chunk_fwd_o_opt, chunk_fwd_o_opt_vk
|
| 22 |
+
from .fused_cumsum_kkt import (
|
| 23 |
+
fused_cumsum_kkt,
|
| 24 |
+
fused_chunk_local_cumsum_scaled_dot_kkt_fwd,
|
| 25 |
+
)
|
| 26 |
+
from .fused_solve_tril_recompute import fused_solve_tril_recompute_w_u
|
| 27 |
+
from .fused_gdn_gating_prefill import fused_gdn_gating_and_sigmoid
|
| 28 |
+
|
| 29 |
+
__all__ = [
|
| 30 |
+
"chunk_gated_delta_rule_fwd",
|
| 31 |
+
"chunk_gated_delta_rule_fwd_opt",
|
| 32 |
+
"chunk_gated_delta_rule_fwd_opt_vk",
|
| 33 |
+
"chunk_gated_delta_rule_fwd_h",
|
| 34 |
+
"chunk_gated_delta_rule_fwd_h_opt",
|
| 35 |
+
"chunk_gated_delta_rule_fwd_h_opt_vk",
|
| 36 |
+
"chunk_fwd_o",
|
| 37 |
+
"chunk_fwd_o_opt",
|
| 38 |
+
"chunk_fwd_o_opt_vk",
|
| 39 |
+
"fused_cumsum_kkt",
|
| 40 |
+
"fused_chunk_local_cumsum_scaled_dot_kkt_fwd",
|
| 41 |
+
"fused_solve_tril_recompute_w_u",
|
| 42 |
+
"fused_gdn_gating_and_sigmoid",
|
| 43 |
+
]
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/causal_conv1d_fwd_split_qkv.py
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Causal conv1d with fused split q/k/v output for prefill."""
|
| 2 |
+
|
| 3 |
+
from typing import List, Optional, Tuple
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import triton
|
| 7 |
+
import triton.language as tl
|
| 8 |
+
|
| 9 |
+
PAD_SLOT_ID = -1
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@triton.jit()
|
| 13 |
+
def _causal_conv1d_fwd_split_kernel(
|
| 14 |
+
x_ptr,
|
| 15 |
+
w_ptr,
|
| 16 |
+
bias_ptr,
|
| 17 |
+
initial_states_ptr,
|
| 18 |
+
cache_indices_ptr,
|
| 19 |
+
has_initial_states_ptr,
|
| 20 |
+
query_start_loc_ptr,
|
| 21 |
+
q_ptr,
|
| 22 |
+
k_ptr,
|
| 23 |
+
v_ptr,
|
| 24 |
+
key_dim: tl.constexpr,
|
| 25 |
+
value_dim: tl.constexpr,
|
| 26 |
+
dim: tl.constexpr,
|
| 27 |
+
seqlen: tl.int32,
|
| 28 |
+
num_cache_lines: tl.constexpr,
|
| 29 |
+
stride_x_dim: tl.constexpr,
|
| 30 |
+
stride_x_token: tl.constexpr,
|
| 31 |
+
stride_w_dim: tl.constexpr,
|
| 32 |
+
stride_w_width: tl.constexpr,
|
| 33 |
+
stride_istate_seq: tl.constexpr,
|
| 34 |
+
stride_istate_dim: tl.constexpr,
|
| 35 |
+
stride_istate_token: tl.constexpr,
|
| 36 |
+
stride_q_token: tl.constexpr,
|
| 37 |
+
stride_q_dim: tl.constexpr,
|
| 38 |
+
stride_k_token: tl.constexpr,
|
| 39 |
+
stride_k_dim: tl.constexpr,
|
| 40 |
+
stride_v_token: tl.constexpr,
|
| 41 |
+
stride_v_dim: tl.constexpr,
|
| 42 |
+
pad_slot_id: tl.constexpr,
|
| 43 |
+
HAS_BIAS: tl.constexpr,
|
| 44 |
+
KERNEL_WIDTH: tl.constexpr,
|
| 45 |
+
SILU_ACTIVATION: tl.constexpr,
|
| 46 |
+
HAS_INITIAL_STATES: tl.constexpr,
|
| 47 |
+
HAS_CACHE: tl.constexpr,
|
| 48 |
+
IS_CONTINUOUS_BATCHING: tl.constexpr,
|
| 49 |
+
USE_PAD_SLOT: tl.constexpr,
|
| 50 |
+
NP2_STATELEN: tl.constexpr,
|
| 51 |
+
BLOCK_M: tl.constexpr,
|
| 52 |
+
BLOCK_N: tl.constexpr,
|
| 53 |
+
):
|
| 54 |
+
"""Fused causal conv1d + split q/k/v output for prefill."""
|
| 55 |
+
conv_states_ptr = initial_states_ptr
|
| 56 |
+
conv_state_indices_ptr = cache_indices_ptr
|
| 57 |
+
stride_conv_state_seq = stride_istate_seq
|
| 58 |
+
stride_conv_state_dim = stride_istate_dim
|
| 59 |
+
stride_conv_state_tok = stride_istate_token
|
| 60 |
+
state_len = KERNEL_WIDTH - 1
|
| 61 |
+
|
| 62 |
+
idx_seq = tl.program_id(0)
|
| 63 |
+
chunk_offset = tl.program_id(1)
|
| 64 |
+
idx_feats = tl.program_id(2) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 65 |
+
|
| 66 |
+
if idx_seq == pad_slot_id:
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
sequence_start_index = tl.load(query_start_loc_ptr + idx_seq)
|
| 70 |
+
sequence_end_index = tl.load(query_start_loc_ptr + idx_seq + 1)
|
| 71 |
+
seqlen = sequence_end_index - sequence_start_index
|
| 72 |
+
|
| 73 |
+
token_offset = BLOCK_M * chunk_offset
|
| 74 |
+
segment_len = min(BLOCK_M, seqlen - token_offset)
|
| 75 |
+
|
| 76 |
+
if segment_len <= 0:
|
| 77 |
+
return
|
| 78 |
+
|
| 79 |
+
x_base = x_ptr + sequence_start_index * stride_x_token + idx_feats * stride_x_dim
|
| 80 |
+
|
| 81 |
+
if IS_CONTINUOUS_BATCHING:
|
| 82 |
+
conv_state_batch_coord = tl.load(conv_state_indices_ptr + idx_seq).to(tl.int64)
|
| 83 |
+
else:
|
| 84 |
+
conv_state_batch_coord = idx_seq
|
| 85 |
+
|
| 86 |
+
if USE_PAD_SLOT:
|
| 87 |
+
if conv_state_batch_coord == pad_slot_id:
|
| 88 |
+
return
|
| 89 |
+
|
| 90 |
+
conv_states_base = (
|
| 91 |
+
conv_states_ptr
|
| 92 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 93 |
+
+ (idx_feats * stride_conv_state_dim)
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
w_base = w_ptr + (idx_feats * stride_w_dim)
|
| 97 |
+
|
| 98 |
+
if chunk_offset == 0:
|
| 99 |
+
load_init_state = False
|
| 100 |
+
if HAS_INITIAL_STATES:
|
| 101 |
+
load_init_state = tl.load(has_initial_states_ptr + idx_seq).to(tl.int1)
|
| 102 |
+
if load_init_state:
|
| 103 |
+
prior_tokens = conv_states_base + (state_len - 1) * stride_conv_state_tok
|
| 104 |
+
mask_w = idx_feats < dim
|
| 105 |
+
if KERNEL_WIDTH == 2:
|
| 106 |
+
col0 = tl.load(prior_tokens, mask_w, 0.0)
|
| 107 |
+
if KERNEL_WIDTH == 3:
|
| 108 |
+
col1 = tl.load(prior_tokens, mask_w, 0.0)
|
| 109 |
+
col0 = tl.load(prior_tokens - 1 * stride_conv_state_tok, mask_w, 0.0)
|
| 110 |
+
if KERNEL_WIDTH == 4:
|
| 111 |
+
col2 = tl.load(prior_tokens, mask_w, 0.0)
|
| 112 |
+
col1 = tl.load(prior_tokens - 1 * stride_conv_state_tok, mask_w, 0.0)
|
| 113 |
+
col0 = tl.load(prior_tokens - 2 * stride_conv_state_tok, mask_w, 0.0)
|
| 114 |
+
else:
|
| 115 |
+
if KERNEL_WIDTH >= 2:
|
| 116 |
+
col0 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 117 |
+
if KERNEL_WIDTH >= 3:
|
| 118 |
+
col1 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 119 |
+
if KERNEL_WIDTH >= 4:
|
| 120 |
+
col2 = tl.zeros((BLOCK_N,), dtype=x_ptr.dtype.element_ty)
|
| 121 |
+
|
| 122 |
+
if state_len <= seqlen:
|
| 123 |
+
idx_tokens_last = (seqlen - state_len) + tl.arange(0, NP2_STATELEN)
|
| 124 |
+
x_ptrs = (
|
| 125 |
+
x_ptr
|
| 126 |
+
+ ((sequence_start_index + idx_tokens_last) * stride_x_token)[:, None]
|
| 127 |
+
+ (idx_feats * stride_x_dim)[None, :]
|
| 128 |
+
)
|
| 129 |
+
mask_x = (
|
| 130 |
+
(idx_tokens_last >= 0)[:, None]
|
| 131 |
+
& (idx_tokens_last < seqlen)[:, None]
|
| 132 |
+
& (idx_feats < dim)[None, :]
|
| 133 |
+
)
|
| 134 |
+
new_conv_state = tl.load(x_ptrs, mask_x, 0.0)
|
| 135 |
+
idx_tokens_conv = tl.arange(0, NP2_STATELEN)
|
| 136 |
+
conv_states_ptrs_target = (
|
| 137 |
+
conv_states_base[None, :]
|
| 138 |
+
+ (idx_tokens_conv * stride_conv_state_tok)[:, None]
|
| 139 |
+
)
|
| 140 |
+
mask = (idx_tokens_conv < state_len)[:, None] & (idx_feats < dim)[None, :]
|
| 141 |
+
tl.debug_barrier()
|
| 142 |
+
tl.store(conv_states_ptrs_target, new_conv_state, mask)
|
| 143 |
+
else:
|
| 144 |
+
if load_init_state:
|
| 145 |
+
idx_tokens_conv = tl.arange(0, NP2_STATELEN)
|
| 146 |
+
conv_states_ptrs_source = (
|
| 147 |
+
conv_states_ptr
|
| 148 |
+
+ (conv_state_batch_coord * stride_conv_state_seq)
|
| 149 |
+
+ (idx_feats * stride_conv_state_dim)[None, :]
|
| 150 |
+
+ ((idx_tokens_conv + seqlen) * stride_conv_state_tok)[:, None]
|
| 151 |
+
)
|
| 152 |
+
mask = (
|
| 153 |
+
(conv_state_batch_coord < num_cache_lines)
|
| 154 |
+
& ((idx_tokens_conv + seqlen) < state_len)[:, None]
|
| 155 |
+
& (idx_feats < dim)[None, :]
|
| 156 |
+
)
|
| 157 |
+
conv_state = tl.load(conv_states_ptrs_source, mask, other=0.0)
|
| 158 |
+
VAL = state_len - seqlen
|
| 159 |
+
x_ptrs = (
|
| 160 |
+
x_base[None, :]
|
| 161 |
+
+ ((idx_tokens_conv - VAL) * stride_x_token)[:, None]
|
| 162 |
+
)
|
| 163 |
+
mask_x = (
|
| 164 |
+
(idx_tokens_conv - VAL >= 0)[:, None]
|
| 165 |
+
& (idx_tokens_conv - VAL < seqlen)[:, None]
|
| 166 |
+
& (idx_feats < dim)[None, :]
|
| 167 |
+
)
|
| 168 |
+
loaded_x = tl.load(x_ptrs, mask_x, 0.0)
|
| 169 |
+
tl.debug_barrier()
|
| 170 |
+
new_conv_state = tl.where(mask, conv_state, loaded_x)
|
| 171 |
+
conv_states_ptrs_target = (
|
| 172 |
+
conv_states_base
|
| 173 |
+
+ (idx_tokens_conv * stride_conv_state_tok)[:, None]
|
| 174 |
+
)
|
| 175 |
+
mask = (idx_tokens_conv < state_len)[:, None] & (idx_feats < dim)[
|
| 176 |
+
None, :
|
| 177 |
+
]
|
| 178 |
+
tl.store(conv_states_ptrs_target, new_conv_state, mask)
|
| 179 |
+
else:
|
| 180 |
+
idx_tokens_conv = tl.arange(0, NP2_STATELEN)
|
| 181 |
+
VAL = state_len - seqlen
|
| 182 |
+
x_ptrs = (
|
| 183 |
+
x_base[None, :]
|
| 184 |
+
+ ((idx_tokens_conv - VAL) * stride_x_token)[:, None]
|
| 185 |
+
)
|
| 186 |
+
mask_x = (
|
| 187 |
+
(idx_tokens_conv - VAL >= 0)[:, None]
|
| 188 |
+
& (idx_tokens_conv - VAL < seqlen)[:, None]
|
| 189 |
+
& (idx_feats < dim)[None, :]
|
| 190 |
+
)
|
| 191 |
+
new_conv_state = tl.load(x_ptrs, mask_x, 0.0)
|
| 192 |
+
conv_states_ptrs_target = (
|
| 193 |
+
conv_states_base
|
| 194 |
+
+ (idx_tokens_conv * stride_conv_state_tok)[:, None]
|
| 195 |
+
)
|
| 196 |
+
mask = (idx_tokens_conv < state_len)[:, None] & (idx_feats < dim)[
|
| 197 |
+
None, :
|
| 198 |
+
]
|
| 199 |
+
tl.store(conv_states_ptrs_target, new_conv_state, mask)
|
| 200 |
+
else:
|
| 201 |
+
prior_tokens = x_base + (token_offset - 1) * stride_x_token
|
| 202 |
+
mask_w = idx_feats < dim
|
| 203 |
+
if KERNEL_WIDTH == 2:
|
| 204 |
+
col0 = tl.load(prior_tokens, mask_w, 0.0, cache_modifier=".ca")
|
| 205 |
+
if KERNEL_WIDTH == 3:
|
| 206 |
+
col1 = tl.load(prior_tokens, mask_w, 0.0, cache_modifier=".ca")
|
| 207 |
+
col0 = tl.load(
|
| 208 |
+
prior_tokens - 1 * stride_x_token, mask_w, 0.0, cache_modifier=".ca"
|
| 209 |
+
)
|
| 210 |
+
if KERNEL_WIDTH == 4:
|
| 211 |
+
col2 = tl.load(prior_tokens, mask_w, 0.0, cache_modifier=".ca")
|
| 212 |
+
col1 = tl.load(
|
| 213 |
+
prior_tokens - 1 * stride_x_token, mask_w, 0.0, cache_modifier=".ca"
|
| 214 |
+
)
|
| 215 |
+
col0 = tl.load(
|
| 216 |
+
prior_tokens - 2 * stride_x_token, mask_w, 0.0, cache_modifier=".ca"
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
if HAS_BIAS:
|
| 220 |
+
bias = bias_ptr + idx_feats
|
| 221 |
+
mask_bias = idx_feats < dim
|
| 222 |
+
acc_preload = tl.load(bias, mask=mask_bias, other=0.0).to(tl.float32)
|
| 223 |
+
else:
|
| 224 |
+
acc_preload = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 225 |
+
|
| 226 |
+
x_base_1d = x_base + token_offset * stride_x_token
|
| 227 |
+
|
| 228 |
+
mask_w = idx_feats < dim
|
| 229 |
+
if KERNEL_WIDTH >= 2:
|
| 230 |
+
w_col0 = tl.load(w_base + 0 * stride_w_width, mask_w, other=0.0)
|
| 231 |
+
w_col1 = tl.load(w_base + 1 * stride_w_width, mask_w, other=0.0)
|
| 232 |
+
if KERNEL_WIDTH >= 3:
|
| 233 |
+
w_col2 = tl.load(w_base + 2 * stride_w_width, mask_w, other=0.0)
|
| 234 |
+
if KERNEL_WIDTH >= 4:
|
| 235 |
+
w_col3 = tl.load(w_base + 3 * stride_w_width, mask_w, other=0.0)
|
| 236 |
+
|
| 237 |
+
mask_x_1d = idx_feats < dim
|
| 238 |
+
|
| 239 |
+
for idx_token in range(segment_len):
|
| 240 |
+
acc = acc_preload
|
| 241 |
+
matrix_w = w_col0
|
| 242 |
+
matrix_x = col0
|
| 243 |
+
|
| 244 |
+
for j in tl.static_range(KERNEL_WIDTH):
|
| 245 |
+
if KERNEL_WIDTH == 2:
|
| 246 |
+
if j == 1:
|
| 247 |
+
matrix_w = w_col1
|
| 248 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 249 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 250 |
+
elif KERNEL_WIDTH == 3:
|
| 251 |
+
if j == 1:
|
| 252 |
+
matrix_w = w_col1
|
| 253 |
+
matrix_x = col1
|
| 254 |
+
elif j == 2:
|
| 255 |
+
matrix_w = w_col2
|
| 256 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 257 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 258 |
+
elif KERNEL_WIDTH == 4:
|
| 259 |
+
if j == 1:
|
| 260 |
+
matrix_w = w_col1
|
| 261 |
+
matrix_x = col1
|
| 262 |
+
elif j == 2:
|
| 263 |
+
matrix_w = w_col2
|
| 264 |
+
matrix_x = col2
|
| 265 |
+
elif j == 3:
|
| 266 |
+
matrix_w = w_col3
|
| 267 |
+
x_ptrs_1d = x_base_1d + idx_token * stride_x_token
|
| 268 |
+
matrix_x = tl.load(x_ptrs_1d, mask=mask_x_1d)
|
| 269 |
+
|
| 270 |
+
acc += matrix_x * matrix_w
|
| 271 |
+
|
| 272 |
+
if KERNEL_WIDTH == 2:
|
| 273 |
+
col0 = matrix_x
|
| 274 |
+
elif KERNEL_WIDTH == 3:
|
| 275 |
+
col0 = col1
|
| 276 |
+
col1 = matrix_x
|
| 277 |
+
elif KERNEL_WIDTH == 4:
|
| 278 |
+
col0 = col1
|
| 279 |
+
col1 = col2
|
| 280 |
+
col2 = matrix_x
|
| 281 |
+
|
| 282 |
+
if SILU_ACTIVATION:
|
| 283 |
+
acc = acc / (1 + tl.exp(-acc))
|
| 284 |
+
|
| 285 |
+
global_token_idx = sequence_start_index + token_offset + idx_token
|
| 286 |
+
mask_feat = (idx_token < segment_len) & (idx_feats < dim)
|
| 287 |
+
|
| 288 |
+
is_query = idx_feats < key_dim
|
| 289 |
+
q_ptrs = q_ptr + global_token_idx * stride_q_token + idx_feats * stride_q_dim
|
| 290 |
+
tl.store(q_ptrs, acc, mask=mask_feat & is_query)
|
| 291 |
+
|
| 292 |
+
is_key = (idx_feats >= key_dim) & (idx_feats < 2 * key_dim)
|
| 293 |
+
k_ptrs = (
|
| 294 |
+
k_ptr
|
| 295 |
+
+ global_token_idx * stride_k_token
|
| 296 |
+
+ (idx_feats - key_dim) * stride_k_dim
|
| 297 |
+
)
|
| 298 |
+
tl.store(k_ptrs, acc, mask=mask_feat & is_key)
|
| 299 |
+
|
| 300 |
+
is_value = (idx_feats >= 2 * key_dim) & (idx_feats < 2 * key_dim + value_dim)
|
| 301 |
+
v_ptrs = (
|
| 302 |
+
v_ptr
|
| 303 |
+
+ global_token_idx * stride_v_token
|
| 304 |
+
+ (idx_feats - 2 * key_dim) * stride_v_dim
|
| 305 |
+
)
|
| 306 |
+
tl.store(v_ptrs, acc, mask=mask_feat & is_value)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def causal_conv1d_fn_split_qkv(
|
| 310 |
+
x: torch.Tensor,
|
| 311 |
+
weight: torch.Tensor,
|
| 312 |
+
bias: Optional[torch.Tensor],
|
| 313 |
+
conv_states: torch.Tensor,
|
| 314 |
+
query_start_loc: torch.Tensor,
|
| 315 |
+
seq_lens_cpu: List[int],
|
| 316 |
+
k_dim: int,
|
| 317 |
+
v_dim: int,
|
| 318 |
+
cache_indices: Optional[torch.Tensor] = None,
|
| 319 |
+
has_initial_state: Optional[torch.Tensor] = None,
|
| 320 |
+
activation: str = "silu",
|
| 321 |
+
pad_slot_id: int = PAD_SLOT_ID,
|
| 322 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 323 |
+
"""Causal conv1d with fused split output for prefill. Returns q, k, v as contiguous."""
|
| 324 |
+
dim, cu_seqlen = x.shape
|
| 325 |
+
_, width = weight.shape
|
| 326 |
+
state_len = width - 1
|
| 327 |
+
np2_statelen = triton.next_power_of_2(state_len)
|
| 328 |
+
|
| 329 |
+
q_out = torch.empty(cu_seqlen, k_dim, device=x.device, dtype=x.dtype)
|
| 330 |
+
k_out = torch.empty(cu_seqlen, k_dim, device=x.device, dtype=x.dtype)
|
| 331 |
+
v_out = torch.empty(cu_seqlen, v_dim, device=x.device, dtype=x.dtype)
|
| 332 |
+
|
| 333 |
+
stride_x_dim = x.stride(0)
|
| 334 |
+
stride_x_token = x.stride(1)
|
| 335 |
+
stride_w_dim = weight.stride(0)
|
| 336 |
+
stride_w_width = weight.stride(1)
|
| 337 |
+
|
| 338 |
+
num_cache_lines = 0
|
| 339 |
+
stride_istate_seq = 0
|
| 340 |
+
stride_istate_dim = 0
|
| 341 |
+
stride_istate_token = 0
|
| 342 |
+
if conv_states is not None:
|
| 343 |
+
num_cache_lines = conv_states.size(0)
|
| 344 |
+
stride_istate_seq = conv_states.stride(0)
|
| 345 |
+
stride_istate_dim = conv_states.stride(1)
|
| 346 |
+
stride_istate_token = conv_states.stride(2)
|
| 347 |
+
|
| 348 |
+
def grid(META):
|
| 349 |
+
max_seq_len = max(seq_lens_cpu)
|
| 350 |
+
return (
|
| 351 |
+
len(seq_lens_cpu),
|
| 352 |
+
(max_seq_len + META["BLOCK_M"] - 1) // META["BLOCK_M"],
|
| 353 |
+
triton.cdiv(dim, META["BLOCK_N"]),
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
_causal_conv1d_fwd_split_kernel[grid](
|
| 357 |
+
x,
|
| 358 |
+
weight,
|
| 359 |
+
bias,
|
| 360 |
+
conv_states,
|
| 361 |
+
cache_indices,
|
| 362 |
+
has_initial_state,
|
| 363 |
+
query_start_loc,
|
| 364 |
+
q_out,
|
| 365 |
+
k_out,
|
| 366 |
+
v_out,
|
| 367 |
+
k_dim,
|
| 368 |
+
v_dim,
|
| 369 |
+
dim,
|
| 370 |
+
cu_seqlen,
|
| 371 |
+
num_cache_lines,
|
| 372 |
+
stride_x_dim,
|
| 373 |
+
stride_x_token,
|
| 374 |
+
stride_w_dim,
|
| 375 |
+
stride_w_width,
|
| 376 |
+
stride_istate_seq,
|
| 377 |
+
stride_istate_dim,
|
| 378 |
+
stride_istate_token,
|
| 379 |
+
q_out.stride(0),
|
| 380 |
+
q_out.stride(1),
|
| 381 |
+
k_out.stride(0),
|
| 382 |
+
k_out.stride(1),
|
| 383 |
+
v_out.stride(0),
|
| 384 |
+
v_out.stride(1),
|
| 385 |
+
pad_slot_id,
|
| 386 |
+
HAS_BIAS=bias is not None,
|
| 387 |
+
KERNEL_WIDTH=width,
|
| 388 |
+
SILU_ACTIVATION=activation in ["silu", "swish"],
|
| 389 |
+
HAS_INITIAL_STATES=has_initial_state is not None,
|
| 390 |
+
HAS_CACHE=conv_states is not None,
|
| 391 |
+
IS_CONTINUOUS_BATCHING=cache_indices is not None,
|
| 392 |
+
USE_PAD_SLOT=pad_slot_id is not None,
|
| 393 |
+
NP2_STATELEN=np2_statelen,
|
| 394 |
+
BLOCK_M=8,
|
| 395 |
+
BLOCK_N=256,
|
| 396 |
+
num_stages=2,
|
| 397 |
+
)
|
| 398 |
+
|
| 399 |
+
return q_out, k_out, v_out
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/chunk.py
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Chunk-based gated delta rule forward computation.
|
| 7 |
+
|
| 8 |
+
This module implements the chunk-based parallel computation for the gated delta rule.
|
| 9 |
+
Note: Only forward pass is implemented. Backward pass is not supported in aiter.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
from .chunk_delta_h import (
|
| 15 |
+
chunk_gated_delta_rule_fwd_h,
|
| 16 |
+
chunk_gated_delta_rule_fwd_h_opt,
|
| 17 |
+
chunk_gated_delta_rule_fwd_h_opt_vk,
|
| 18 |
+
)
|
| 19 |
+
from .chunk_o import chunk_fwd_o, chunk_fwd_o_opt, chunk_fwd_o_opt_vk
|
| 20 |
+
from .fused_cumsum_kkt import fused_chunk_local_cumsum_scaled_dot_kkt_fwd
|
| 21 |
+
from .fused_solve_tril_recompute import fused_solve_tril_recompute_w_u
|
| 22 |
+
from ..utils import (
|
| 23 |
+
chunk_local_cumsum,
|
| 24 |
+
chunk_scaled_dot_kkt_fwd,
|
| 25 |
+
recompute_w_u_fwd,
|
| 26 |
+
solve_tril,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _is_gfx12_runtime() -> bool:
|
| 31 |
+
try:
|
| 32 |
+
props = torch.cuda.get_device_properties(torch.cuda.current_device())
|
| 33 |
+
arch = getattr(props, "gcnArchName", "")
|
| 34 |
+
return arch.split(":")[0].startswith("gfx12") if arch else False
|
| 35 |
+
except Exception:
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def chunk_gated_delta_rule_fwd(
|
| 40 |
+
q: torch.Tensor,
|
| 41 |
+
k: torch.Tensor,
|
| 42 |
+
v: torch.Tensor,
|
| 43 |
+
g: torch.Tensor,
|
| 44 |
+
beta: torch.Tensor,
|
| 45 |
+
scale: float,
|
| 46 |
+
initial_state: torch.Tensor,
|
| 47 |
+
output_final_state: bool,
|
| 48 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 49 |
+
):
|
| 50 |
+
"""
|
| 51 |
+
Chunk gated delta rule forward computation (Forward only).
|
| 52 |
+
|
| 53 |
+
This function implements chunk-based parallel computation for the gated delta rule,
|
| 54 |
+
combining all necessary steps for efficient sequence processing.
|
| 55 |
+
|
| 56 |
+
Note: This implementation only supports forward pass. Backward pass is not available.
|
| 57 |
+
|
| 58 |
+
Args:
|
| 59 |
+
q: Query tensor of shape [B, T, H, K]
|
| 60 |
+
k: Key tensor of shape [B, T, H, K]
|
| 61 |
+
v: Value tensor of shape [B, T, H, V]
|
| 62 |
+
g: Gate tensor (in log space) of shape [B, T, H]
|
| 63 |
+
beta: Beta parameter tensor of shape [B, T, H]
|
| 64 |
+
scale: Scaling factor for queries
|
| 65 |
+
initial_state: Initial hidden state of shape [N, H, K, V]
|
| 66 |
+
output_final_state: Whether to output the final state
|
| 67 |
+
cu_seqlens: Cumulative sequence lengths for variable-length inputs (optional) [N+1]
|
| 68 |
+
|
| 69 |
+
Returns:
|
| 70 |
+
tuple: (g, o, A, final_state) where:
|
| 71 |
+
- g: Cumulative gate values [B, T, H]
|
| 72 |
+
- o: Output tensor [B, T, H, V]
|
| 73 |
+
- A: WY representation matrix
|
| 74 |
+
- final_state: Final hidden state [N, H, K, V] if output_final_state=True, else None
|
| 75 |
+
"""
|
| 76 |
+
# Step 1: Compute local cumulative sum of gates
|
| 77 |
+
g = chunk_local_cumsum(g, chunk_size=64, cu_seqlens=cu_seqlens)
|
| 78 |
+
|
| 79 |
+
# Step 2: Compute WY representation
|
| 80 |
+
A = chunk_scaled_dot_kkt_fwd(
|
| 81 |
+
k=k,
|
| 82 |
+
g=g,
|
| 83 |
+
beta=beta,
|
| 84 |
+
cu_seqlens=cu_seqlens,
|
| 85 |
+
output_dtype=torch.float32,
|
| 86 |
+
)
|
| 87 |
+
A = solve_tril(
|
| 88 |
+
A=A,
|
| 89 |
+
cu_seqlens=cu_seqlens,
|
| 90 |
+
output_dtype=k.dtype,
|
| 91 |
+
)
|
| 92 |
+
w, u = recompute_w_u_fwd(
|
| 93 |
+
k=k,
|
| 94 |
+
v=v,
|
| 95 |
+
beta=beta,
|
| 96 |
+
A=A,
|
| 97 |
+
g=g,
|
| 98 |
+
cu_seqlens=cu_seqlens,
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
# Step 3: Compute hidden states
|
| 102 |
+
h, v_new, final_state = chunk_gated_delta_rule_fwd_h(
|
| 103 |
+
k=k,
|
| 104 |
+
w=w,
|
| 105 |
+
u=u,
|
| 106 |
+
g=g,
|
| 107 |
+
initial_state=initial_state,
|
| 108 |
+
output_final_state=output_final_state,
|
| 109 |
+
cu_seqlens=cu_seqlens,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
# Step 4: Compute output
|
| 113 |
+
o = chunk_fwd_o(
|
| 114 |
+
q=q,
|
| 115 |
+
k=k,
|
| 116 |
+
v=v_new,
|
| 117 |
+
h=h,
|
| 118 |
+
g=g,
|
| 119 |
+
scale=scale,
|
| 120 |
+
cu_seqlens=cu_seqlens,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
return g, o, A, final_state
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def chunk_gated_delta_rule_fwd_opt(
|
| 127 |
+
q: torch.Tensor,
|
| 128 |
+
k: torch.Tensor,
|
| 129 |
+
v: torch.Tensor,
|
| 130 |
+
g: torch.Tensor,
|
| 131 |
+
beta: torch.Tensor,
|
| 132 |
+
scale: float,
|
| 133 |
+
initial_state: torch.Tensor | None,
|
| 134 |
+
output_final_state: bool,
|
| 135 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 136 |
+
):
|
| 137 |
+
"""
|
| 138 |
+
Optimized chunk gated delta rule forward computation (Forward only).
|
| 139 |
+
|
| 140 |
+
This function implements an optimized chunk-based parallel computation for
|
| 141 |
+
the gated delta rule, using fused kernels and transposed intermediate layouts
|
| 142 |
+
to reduce global memory round-trips.
|
| 143 |
+
|
| 144 |
+
Note: This implementation only supports forward pass. Backward pass is not available.
|
| 145 |
+
|
| 146 |
+
Args:
|
| 147 |
+
q: Query tensor of shape [B, T, Hg, K]
|
| 148 |
+
k: Key tensor of shape [B, T, Hg, K]
|
| 149 |
+
v: Value tensor of shape [B, T, H, V]
|
| 150 |
+
g: Gate tensor (in log space, pre-cumsum) of shape [B, T, H]
|
| 151 |
+
beta: Beta parameter tensor of shape [B, T, H]
|
| 152 |
+
scale: Scaling factor for queries
|
| 153 |
+
initial_state: Optional initial hidden state of shape [N, H, K, V]
|
| 154 |
+
output_final_state: Whether to output the final state
|
| 155 |
+
cu_seqlens: Cumulative sequence lengths for variable-length inputs (optional) [N+1]
|
| 156 |
+
|
| 157 |
+
Returns:
|
| 158 |
+
tuple: (g_cumsum, o, final_state) where:
|
| 159 |
+
- g_cumsum: Cumulative gate values [B, H, T]
|
| 160 |
+
- o: Output tensor [B, T, H, V]
|
| 161 |
+
- final_state: Final hidden state [N, H, K, V] if output_final_state=True, else None
|
| 162 |
+
"""
|
| 163 |
+
# Step 1: Compute fused local cumulative sum of gates and KKT
|
| 164 |
+
g_cumsum, A_raw = fused_chunk_local_cumsum_scaled_dot_kkt_fwd(
|
| 165 |
+
k=k,
|
| 166 |
+
beta=beta,
|
| 167 |
+
g=g,
|
| 168 |
+
cu_seqlens=cu_seqlens,
|
| 169 |
+
use_exp2=False,
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
# Step 2: Compute fused triangular solve and recompute w, u
|
| 173 |
+
# w, u are already in [B, H, T, K/V] head-major contiguous layout
|
| 174 |
+
w, u = fused_solve_tril_recompute_w_u(
|
| 175 |
+
A_raw=A_raw,
|
| 176 |
+
k=k,
|
| 177 |
+
v=v,
|
| 178 |
+
beta=beta,
|
| 179 |
+
g_cumsum=g_cumsum,
|
| 180 |
+
cu_seqlens=cu_seqlens,
|
| 181 |
+
use_exp2=False,
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
# k5_opt / k6_opt index g with token-major [B, T, H] strides, but the fused
|
| 185 |
+
# k12 returns g_cumsum head-major [B, H, T]. Convert to token-major here.
|
| 186 |
+
g_cumsum_tok = g_cumsum.transpose(1, 2).contiguous()
|
| 187 |
+
|
| 188 |
+
# Step 3: Compute hidden states
|
| 189 |
+
h, v_new, final_state = chunk_gated_delta_rule_fwd_h_opt(
|
| 190 |
+
k=k,
|
| 191 |
+
w=w,
|
| 192 |
+
u=u,
|
| 193 |
+
g=g_cumsum_tok,
|
| 194 |
+
initial_state=initial_state,
|
| 195 |
+
output_final_state=output_final_state,
|
| 196 |
+
cu_seqlens=cu_seqlens,
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
# Step 4: Compute output
|
| 200 |
+
o = chunk_fwd_o_opt(
|
| 201 |
+
q=q,
|
| 202 |
+
k=k,
|
| 203 |
+
v=v_new,
|
| 204 |
+
h=h,
|
| 205 |
+
g=g_cumsum_tok,
|
| 206 |
+
scale=scale,
|
| 207 |
+
cu_seqlens=cu_seqlens,
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
return g_cumsum, o, final_state
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def chunk_gated_delta_rule_fwd_opt_vk(
|
| 214 |
+
q: torch.Tensor,
|
| 215 |
+
k: torch.Tensor,
|
| 216 |
+
v: torch.Tensor,
|
| 217 |
+
g: torch.Tensor,
|
| 218 |
+
beta: torch.Tensor,
|
| 219 |
+
scale: float,
|
| 220 |
+
initial_state: torch.Tensor | None,
|
| 221 |
+
output_final_state: bool,
|
| 222 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 223 |
+
use_chunk_hip: bool = False,
|
| 224 |
+
use_chunk_flydsl: bool = False,
|
| 225 |
+
state_dtype: torch.dtype | None = None,
|
| 226 |
+
use_exp2: bool = True,
|
| 227 |
+
o: torch.Tensor | None = None,
|
| 228 |
+
num_decodes: int = 0,
|
| 229 |
+
num_decode_tokens: int = 0,
|
| 230 |
+
):
|
| 231 |
+
"""
|
| 232 |
+
Optimized chunk gated delta rule forward with h layout [V, K].
|
| 233 |
+
|
| 234 |
+
Uses the same fused kernels as opt, but with transposed
|
| 235 |
+
h layout [V, K] instead of [K, V].
|
| 236 |
+
|
| 237 |
+
When use_chunk_hip=True, hidden state computation uses a HIP kernel
|
| 238 |
+
instead of Triton. When use_chunk_flydsl=True, hidden state computation
|
| 239 |
+
uses the FlyDSL kernel. The two flags are mutually exclusive.
|
| 240 |
+
|
| 241 |
+
Args:
|
| 242 |
+
q: [B, T, Hg, K]
|
| 243 |
+
k: [B, T, Hg, K]
|
| 244 |
+
v: [B, T, H, V]
|
| 245 |
+
g: [B, T, H] — raw gate (pre-cumsum)
|
| 246 |
+
beta: [B, T, H]
|
| 247 |
+
scale: float
|
| 248 |
+
initial_state: optional [N, H, V, K] — note transposed h layout
|
| 249 |
+
output_final_state: bool
|
| 250 |
+
cu_seqlens: [N+1] optional
|
| 251 |
+
use_chunk_hip: bool — use HIP kernel for hidden state (K5)
|
| 252 |
+
use_chunk_flydsl: bool — use FlyDSL kernel for hidden state (K5)
|
| 253 |
+
state_dtype: optional initial/final state dtype (`fp32` or `bf16`),
|
| 254 |
+
supported by both the HIP and Triton hidden-state paths
|
| 255 |
+
use_exp2: bool — use exp2 instead of exp for gate computation
|
| 256 |
+
o: optional pre-allocated [B, T, H, V] output buffer (written in
|
| 257 |
+
place by K6). If None, a fresh buffer is allocated.
|
| 258 |
+
num_decodes / num_decode_tokens: skip a leading decode-only prefix in
|
| 259 |
+
the ORIGINAL cu_seqlens (data tensors are expected pre-sliced).
|
| 260 |
+
Threaded into every stage; the cached prologue helpers
|
| 261 |
+
(prepare_chunk_indices / prepare_chunk_offsets /
|
| 262 |
+
prepare_rebased_cu_seqlens) key on the original cu_seqlens identity,
|
| 263 |
+
so chunk-index / offset builds stay cache-warm across forwards
|
| 264 |
+
(no per-forward .tolist() D2H).
|
| 265 |
+
|
| 266 |
+
Returns:
|
| 267 |
+
tuple: (g_cumsum, o, final_state) where:
|
| 268 |
+
- g_cumsum: [B, H, T]
|
| 269 |
+
- o: [B, T, H, V]
|
| 270 |
+
- final_state: [N, H, V, K] if output_final_state=True, else None
|
| 271 |
+
"""
|
| 272 |
+
if use_chunk_hip and use_chunk_flydsl:
|
| 273 |
+
raise ValueError(
|
| 274 |
+
"use_chunk_hip and use_chunk_flydsl are mutually exclusive; "
|
| 275 |
+
"set at most one."
|
| 276 |
+
)
|
| 277 |
+
if use_chunk_hip and (_is_gfx12_runtime() or num_decodes > 0):
|
| 278 |
+
use_chunk_hip = False
|
| 279 |
+
|
| 280 |
+
g_cumsum, A_raw = fused_chunk_local_cumsum_scaled_dot_kkt_fwd(
|
| 281 |
+
k=k,
|
| 282 |
+
beta=beta,
|
| 283 |
+
g=g,
|
| 284 |
+
cu_seqlens=cu_seqlens,
|
| 285 |
+
use_exp2=use_exp2,
|
| 286 |
+
num_decodes=num_decodes,
|
| 287 |
+
num_decode_tokens=num_decode_tokens,
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
w, u = fused_solve_tril_recompute_w_u(
|
| 291 |
+
A_raw=A_raw,
|
| 292 |
+
k=k,
|
| 293 |
+
v=v,
|
| 294 |
+
beta=beta,
|
| 295 |
+
g_cumsum=g_cumsum,
|
| 296 |
+
cu_seqlens=cu_seqlens,
|
| 297 |
+
use_exp2=use_exp2,
|
| 298 |
+
num_decodes=num_decodes,
|
| 299 |
+
num_decode_tokens=num_decode_tokens,
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
if use_chunk_hip:
|
| 303 |
+
from aiter.ops.chunk_gated_delta_rule_fwd_h import (
|
| 304 |
+
chunk_gated_delta_rule_fwd_h_hip_fn,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
h, v_new, final_state = chunk_gated_delta_rule_fwd_h_hip_fn(
|
| 308 |
+
k=k,
|
| 309 |
+
w=w,
|
| 310 |
+
u=u,
|
| 311 |
+
g=g_cumsum,
|
| 312 |
+
initial_state=initial_state,
|
| 313 |
+
output_final_state=output_final_state,
|
| 314 |
+
cu_seqlens=cu_seqlens,
|
| 315 |
+
state_dtype=state_dtype,
|
| 316 |
+
use_exp2=use_exp2,
|
| 317 |
+
g_head_major=True,
|
| 318 |
+
)
|
| 319 |
+
elif use_chunk_flydsl:
|
| 320 |
+
# FlyDSL K5 wrapper expects ``g`` in head-major [B, H, T] layout
|
| 321 |
+
# (matches Triton VK / HIP). ``g_cumsum`` from K1+K2 is already
|
| 322 |
+
# head-major, so pass it through directly. The wrapper accepts
|
| 323 |
+
# ``use_exp2`` as a kwarg and pre-scales ``gk`` internally.
|
| 324 |
+
from aiter.ops.flydsl.linear_attention_prefill_kernels import (
|
| 325 |
+
chunk_gated_delta_rule_fwd_h_flydsl,
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
h, v_new, final_state = chunk_gated_delta_rule_fwd_h_flydsl(
|
| 329 |
+
k=k,
|
| 330 |
+
w=w,
|
| 331 |
+
u=u,
|
| 332 |
+
g=g_cumsum,
|
| 333 |
+
initial_state=initial_state,
|
| 334 |
+
output_final_state=output_final_state,
|
| 335 |
+
cu_seqlens=cu_seqlens,
|
| 336 |
+
state_dtype=state_dtype,
|
| 337 |
+
use_exp2=use_exp2,
|
| 338 |
+
num_decodes=num_decodes,
|
| 339 |
+
num_decode_tokens=num_decode_tokens,
|
| 340 |
+
)
|
| 341 |
+
else:
|
| 342 |
+
h, v_new, final_state = chunk_gated_delta_rule_fwd_h_opt_vk(
|
| 343 |
+
k=k,
|
| 344 |
+
w=w,
|
| 345 |
+
u=u,
|
| 346 |
+
g=g_cumsum,
|
| 347 |
+
initial_state=initial_state,
|
| 348 |
+
output_final_state=output_final_state,
|
| 349 |
+
cu_seqlens=cu_seqlens,
|
| 350 |
+
use_exp2=use_exp2,
|
| 351 |
+
state_dtype=state_dtype,
|
| 352 |
+
num_decodes=num_decodes,
|
| 353 |
+
num_decode_tokens=num_decode_tokens,
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
if o is None:
|
| 357 |
+
# Output matches v's [B, T, H, V] layout.
|
| 358 |
+
o = v.new_empty(v.shape)
|
| 359 |
+
|
| 360 |
+
o = chunk_fwd_o_opt_vk(
|
| 361 |
+
q=q,
|
| 362 |
+
k=k,
|
| 363 |
+
v=v_new,
|
| 364 |
+
o=o,
|
| 365 |
+
h=h,
|
| 366 |
+
g=g_cumsum,
|
| 367 |
+
scale=scale,
|
| 368 |
+
cu_seqlens=cu_seqlens,
|
| 369 |
+
use_exp2=use_exp2,
|
| 370 |
+
num_decodes=num_decodes,
|
| 371 |
+
num_decode_tokens=num_decode_tokens,
|
| 372 |
+
)
|
| 373 |
+
|
| 374 |
+
return g_cumsum, o, final_state
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/chunk_delta_h.py
ADDED
|
@@ -0,0 +1,1455 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Chunk-based hidden state computation for gated delta rule (Forward only).
|
| 7 |
+
|
| 8 |
+
This module computes the per-chunk hidden states and the recomputed value
|
| 9 |
+
tensor (`v_new`) consumed by the chunked gated delta rule, supporting both
|
| 10 |
+
the `[K, V]` and the transposed `[V, K]` hidden-state layouts.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
import triton
|
| 15 |
+
import triton.language as tl
|
| 16 |
+
|
| 17 |
+
from ..utils import (
|
| 18 |
+
prepare_chunk_indices,
|
| 19 |
+
prepare_chunk_offsets,
|
| 20 |
+
prepare_rebased_cu_seqlens,
|
| 21 |
+
)
|
| 22 |
+
from ..utils.op import exp
|
| 23 |
+
from ..gated_delta_rule_utils import (
|
| 24 |
+
RCP_LN2,
|
| 25 |
+
IS_AMD,
|
| 26 |
+
IS_NVIDIA_HOPPER,
|
| 27 |
+
USE_CUDA_GRAPH,
|
| 28 |
+
autotune_cache_kwargs,
|
| 29 |
+
check_shared_mem,
|
| 30 |
+
gated_delta_rule_autotune_configs,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
NUM_WARPS = [2, 4] if IS_NVIDIA_HOPPER else [2, 4, 8, 16]
|
| 34 |
+
# Workaround: AMD ROCm Triton compiler fails with num_stages=4 in stream pipeline
|
| 35 |
+
NUM_STAGES_FWD = [2, 3] if IS_AMD else [2, 3, 4]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@triton.heuristics(
|
| 39 |
+
{
|
| 40 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 41 |
+
"USE_GK": lambda args: args["gk"] is not None,
|
| 42 |
+
"USE_INITIAL_STATE": lambda args: args["h0"] is not None,
|
| 43 |
+
"STORE_FINAL_STATE": lambda args: args["ht"] is not None,
|
| 44 |
+
"SAVE_NEW_VALUE": lambda args: args["v_new"] is not None,
|
| 45 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 46 |
+
}
|
| 47 |
+
)
|
| 48 |
+
@triton.autotune(
|
| 49 |
+
configs=gated_delta_rule_autotune_configs(
|
| 50 |
+
[
|
| 51 |
+
triton.Config({"BV": BV}, num_warps=num_warps, num_stages=num_stages)
|
| 52 |
+
for num_warps in [2, 4]
|
| 53 |
+
for num_stages in NUM_STAGES_FWD
|
| 54 |
+
for BV in [32, 64]
|
| 55 |
+
]
|
| 56 |
+
),
|
| 57 |
+
key=["H", "K", "V", "BT"],
|
| 58 |
+
use_cuda_graph=USE_CUDA_GRAPH,
|
| 59 |
+
**autotune_cache_kwargs,
|
| 60 |
+
)
|
| 61 |
+
@triton.jit(do_not_specialize=["T"])
|
| 62 |
+
def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
|
| 63 |
+
k,
|
| 64 |
+
v,
|
| 65 |
+
w,
|
| 66 |
+
v_new,
|
| 67 |
+
g,
|
| 68 |
+
gk,
|
| 69 |
+
h,
|
| 70 |
+
h0,
|
| 71 |
+
ht,
|
| 72 |
+
cu_seqlens,
|
| 73 |
+
chunk_offsets,
|
| 74 |
+
T,
|
| 75 |
+
H: tl.constexpr,
|
| 76 |
+
K: tl.constexpr,
|
| 77 |
+
V: tl.constexpr,
|
| 78 |
+
BT: tl.constexpr,
|
| 79 |
+
BV: tl.constexpr,
|
| 80 |
+
USE_G: tl.constexpr,
|
| 81 |
+
USE_GK: tl.constexpr,
|
| 82 |
+
USE_INITIAL_STATE: tl.constexpr,
|
| 83 |
+
STORE_FINAL_STATE: tl.constexpr,
|
| 84 |
+
SAVE_NEW_VALUE: tl.constexpr,
|
| 85 |
+
IS_VARLEN: tl.constexpr,
|
| 86 |
+
):
|
| 87 |
+
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
| 88 |
+
i_n, i_h = i_nh // H, i_nh % H
|
| 89 |
+
if IS_VARLEN:
|
| 90 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(
|
| 91 |
+
cu_seqlens + i_n + 1
|
| 92 |
+
).to(tl.int32)
|
| 93 |
+
T = eos - bos
|
| 94 |
+
NT = tl.cdiv(T, BT)
|
| 95 |
+
boh = tl.load(chunk_offsets + i_n).to(tl.int32)
|
| 96 |
+
else:
|
| 97 |
+
bos, eos = i_n * T, i_n * T + T
|
| 98 |
+
NT = tl.cdiv(T, BT)
|
| 99 |
+
boh = i_n * NT
|
| 100 |
+
|
| 101 |
+
# [BK, BV]
|
| 102 |
+
b_h1 = tl.zeros([64, BV], dtype=tl.float32)
|
| 103 |
+
if K > 64:
|
| 104 |
+
b_h2 = tl.zeros([64, BV], dtype=tl.float32)
|
| 105 |
+
if K > 128:
|
| 106 |
+
b_h3 = tl.zeros([64, BV], dtype=tl.float32)
|
| 107 |
+
if K > 192:
|
| 108 |
+
b_h4 = tl.zeros([64, BV], dtype=tl.float32)
|
| 109 |
+
|
| 110 |
+
# calculate offset
|
| 111 |
+
h += ((boh * H + i_h) * K * V).to(tl.int64)
|
| 112 |
+
v += ((bos * H + i_h) * V).to(tl.int64)
|
| 113 |
+
k += ((bos * H + i_h) * K).to(tl.int64)
|
| 114 |
+
w += ((bos * H + i_h) * K).to(tl.int64)
|
| 115 |
+
if SAVE_NEW_VALUE:
|
| 116 |
+
v_new += ((bos * H + i_h) * V).to(tl.int64)
|
| 117 |
+
stride_v = H * V
|
| 118 |
+
stride_h = H * K * V
|
| 119 |
+
stride_k = H * K
|
| 120 |
+
if USE_INITIAL_STATE:
|
| 121 |
+
h0 = h0 + i_nh * K * V
|
| 122 |
+
if STORE_FINAL_STATE:
|
| 123 |
+
ht = ht + i_nh * K * V
|
| 124 |
+
|
| 125 |
+
# load initial state
|
| 126 |
+
if USE_INITIAL_STATE:
|
| 127 |
+
p_h0_1 = tl.make_block_ptr(h0, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
|
| 128 |
+
b_h1 += tl.load(p_h0_1, boundary_check=(0, 1)).to(tl.float32)
|
| 129 |
+
if K > 64:
|
| 130 |
+
p_h0_2 = tl.make_block_ptr(
|
| 131 |
+
h0, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 132 |
+
)
|
| 133 |
+
b_h2 += tl.load(p_h0_2, boundary_check=(0, 1)).to(tl.float32)
|
| 134 |
+
if K > 128:
|
| 135 |
+
p_h0_3 = tl.make_block_ptr(
|
| 136 |
+
h0, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 137 |
+
)
|
| 138 |
+
b_h3 += tl.load(p_h0_3, boundary_check=(0, 1)).to(tl.float32)
|
| 139 |
+
if K > 192:
|
| 140 |
+
p_h0_4 = tl.make_block_ptr(
|
| 141 |
+
h0, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 142 |
+
)
|
| 143 |
+
b_h4 += tl.load(p_h0_4, boundary_check=(0, 1)).to(tl.float32)
|
| 144 |
+
|
| 145 |
+
# main recurrence
|
| 146 |
+
for i_t in range(NT):
|
| 147 |
+
p_h1 = tl.make_block_ptr(
|
| 148 |
+
h + i_t * stride_h, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0)
|
| 149 |
+
)
|
| 150 |
+
tl.store(p_h1, b_h1.to(p_h1.dtype.element_ty), boundary_check=(0, 1))
|
| 151 |
+
if K > 64:
|
| 152 |
+
p_h2 = tl.make_block_ptr(
|
| 153 |
+
h + i_t * stride_h, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 154 |
+
)
|
| 155 |
+
tl.store(p_h2, b_h2.to(p_h2.dtype.element_ty), boundary_check=(0, 1))
|
| 156 |
+
if K > 128:
|
| 157 |
+
p_h3 = tl.make_block_ptr(
|
| 158 |
+
h + i_t * stride_h, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 159 |
+
)
|
| 160 |
+
tl.store(p_h3, b_h3.to(p_h3.dtype.element_ty), boundary_check=(0, 1))
|
| 161 |
+
if K > 192:
|
| 162 |
+
p_h4 = tl.make_block_ptr(
|
| 163 |
+
h + i_t * stride_h, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 164 |
+
)
|
| 165 |
+
tl.store(p_h4, b_h4.to(p_h4.dtype.element_ty), boundary_check=(0, 1))
|
| 166 |
+
|
| 167 |
+
p_w = tl.make_block_ptr(
|
| 168 |
+
w, (T, K), (stride_k, 1), (i_t * BT, 0), (BT, 64), (1, 0)
|
| 169 |
+
)
|
| 170 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 171 |
+
b_v = tl.dot(b_w, b_h1.to(b_w.dtype))
|
| 172 |
+
if K > 64:
|
| 173 |
+
p_w = tl.make_block_ptr(
|
| 174 |
+
w, (T, K), (stride_k, 1), (i_t * BT, 64), (BT, 64), (1, 0)
|
| 175 |
+
)
|
| 176 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 177 |
+
b_v = tl.dot(b_w, b_h2.to(b_w.dtype), acc=b_v)
|
| 178 |
+
if K > 128:
|
| 179 |
+
p_w = tl.make_block_ptr(
|
| 180 |
+
w, (T, K), (stride_k, 1), (i_t * BT, 128), (BT, 64), (1, 0)
|
| 181 |
+
)
|
| 182 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 183 |
+
b_v = tl.dot(b_w, b_h3.to(b_w.dtype), acc=b_v)
|
| 184 |
+
if K > 192:
|
| 185 |
+
p_w = tl.make_block_ptr(
|
| 186 |
+
w, (T, K), (stride_k, 1), (i_t * BT, 192), (BT, 64), (1, 0)
|
| 187 |
+
)
|
| 188 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 189 |
+
b_v = tl.dot(b_w, b_h4.to(b_w.dtype), acc=b_v)
|
| 190 |
+
p_v = tl.make_block_ptr(
|
| 191 |
+
v, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 192 |
+
)
|
| 193 |
+
b_v = tl.load(p_v, boundary_check=(0, 1)) - b_v
|
| 194 |
+
|
| 195 |
+
if SAVE_NEW_VALUE:
|
| 196 |
+
p_v = tl.make_block_ptr(
|
| 197 |
+
v_new, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 198 |
+
)
|
| 199 |
+
tl.store(p_v, b_v.to(p_v.dtype.element_ty), boundary_check=(0, 1))
|
| 200 |
+
|
| 201 |
+
last_idx = min((i_t + 1) * BT, T) - 1
|
| 202 |
+
if USE_G:
|
| 203 |
+
m_t = (i_t * BT + tl.arange(0, BT)) < T
|
| 204 |
+
b_g_last = tl.load(g + bos * H + last_idx * H + i_h)
|
| 205 |
+
p_g = tl.make_block_ptr(
|
| 206 |
+
g + bos * H + i_h, (T,), (H,), (i_t * BT,), (BT,), (0,)
|
| 207 |
+
)
|
| 208 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 209 |
+
b_v = b_v * tl.where(m_t, exp(b_g_last - b_g), 0)[:, None]
|
| 210 |
+
b_g_last = exp(b_g_last)
|
| 211 |
+
b_h1 *= b_g_last
|
| 212 |
+
if K > 64:
|
| 213 |
+
b_h2 *= b_g_last
|
| 214 |
+
if K > 128:
|
| 215 |
+
b_h3 *= b_g_last
|
| 216 |
+
if K > 192:
|
| 217 |
+
b_h4 *= b_g_last
|
| 218 |
+
|
| 219 |
+
if USE_GK:
|
| 220 |
+
o_k1 = tl.arange(0, 64)
|
| 221 |
+
b_gk_last1 = tl.load(
|
| 222 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k1,
|
| 223 |
+
mask=(o_k1 < K),
|
| 224 |
+
other=0.0,
|
| 225 |
+
)
|
| 226 |
+
b_h1 *= exp(b_gk_last1)[:, None]
|
| 227 |
+
if K > 64:
|
| 228 |
+
o_k2 = 64 + o_k1
|
| 229 |
+
b_gk_last2 = tl.load(
|
| 230 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k2,
|
| 231 |
+
mask=(o_k2 < K),
|
| 232 |
+
other=0.0,
|
| 233 |
+
)
|
| 234 |
+
b_h2 *= exp(b_gk_last2)[:, None]
|
| 235 |
+
if K > 128:
|
| 236 |
+
o_k3 = 128 + o_k1
|
| 237 |
+
b_gk_last3 = tl.load(
|
| 238 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k3,
|
| 239 |
+
mask=(o_k3 < K),
|
| 240 |
+
other=0.0,
|
| 241 |
+
)
|
| 242 |
+
b_h3 *= exp(b_gk_last3)[:, None]
|
| 243 |
+
if K > 192:
|
| 244 |
+
o_k4 = 192 + o_k1
|
| 245 |
+
b_gk_last4 = tl.load(
|
| 246 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k4,
|
| 247 |
+
mask=(o_k4 < K),
|
| 248 |
+
other=0.0,
|
| 249 |
+
)
|
| 250 |
+
b_h4 *= exp(b_gk_last4)[:, None]
|
| 251 |
+
b_v = b_v.to(k.dtype.element_ty)
|
| 252 |
+
|
| 253 |
+
p_k = tl.make_block_ptr(
|
| 254 |
+
k, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1)
|
| 255 |
+
)
|
| 256 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 257 |
+
b_h1 = tl.dot(b_k, b_v, acc=b_h1)
|
| 258 |
+
if K > 64:
|
| 259 |
+
p_k = tl.make_block_ptr(
|
| 260 |
+
k, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1)
|
| 261 |
+
)
|
| 262 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 263 |
+
b_h2 = tl.dot(b_k, b_v, acc=b_h2)
|
| 264 |
+
if K > 128:
|
| 265 |
+
p_k = tl.make_block_ptr(
|
| 266 |
+
k, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1)
|
| 267 |
+
)
|
| 268 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 269 |
+
b_h3 = tl.dot(b_k, b_v, acc=b_h3)
|
| 270 |
+
if K > 192:
|
| 271 |
+
p_k = tl.make_block_ptr(
|
| 272 |
+
k, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1)
|
| 273 |
+
)
|
| 274 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 275 |
+
b_h4 = tl.dot(b_k, b_v, acc=b_h4)
|
| 276 |
+
# epilogue
|
| 277 |
+
if STORE_FINAL_STATE:
|
| 278 |
+
p_ht = tl.make_block_ptr(ht, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
|
| 279 |
+
tl.store(p_ht, b_h1.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 280 |
+
if K > 64:
|
| 281 |
+
p_ht = tl.make_block_ptr(
|
| 282 |
+
ht, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 283 |
+
)
|
| 284 |
+
tl.store(p_ht, b_h2.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 285 |
+
if K > 128:
|
| 286 |
+
p_ht = tl.make_block_ptr(
|
| 287 |
+
ht, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 288 |
+
)
|
| 289 |
+
tl.store(p_ht, b_h3.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 290 |
+
if K > 192:
|
| 291 |
+
p_ht = tl.make_block_ptr(
|
| 292 |
+
ht, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 293 |
+
)
|
| 294 |
+
tl.store(p_ht, b_h4.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
@triton.heuristics(
|
| 298 |
+
{
|
| 299 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 300 |
+
"USE_GK": lambda args: args["gk"] is not None,
|
| 301 |
+
"USE_INITIAL_STATE": lambda args: args["dh0"] is not None,
|
| 302 |
+
"USE_FINAL_STATE_GRADIENT": lambda args: args["dht"] is not None,
|
| 303 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 304 |
+
}
|
| 305 |
+
)
|
| 306 |
+
@triton.autotune(
|
| 307 |
+
configs=gated_delta_rule_autotune_configs(
|
| 308 |
+
[
|
| 309 |
+
triton.Config({"BV": BV}, num_warps=num_warps, num_stages=num_stages)
|
| 310 |
+
for num_warps in [2, 4]
|
| 311 |
+
for num_stages in (
|
| 312 |
+
[3, 2] if IS_AMD else ([4, 3, 2] if check_shared_mem("ampere") else [1])
|
| 313 |
+
)
|
| 314 |
+
for BV in [64, 32]
|
| 315 |
+
]
|
| 316 |
+
),
|
| 317 |
+
key=["H", "K", "V", "BT", "BV", "USE_G"],
|
| 318 |
+
use_cuda_graph=USE_CUDA_GRAPH,
|
| 319 |
+
**autotune_cache_kwargs,
|
| 320 |
+
)
|
| 321 |
+
@triton.jit(do_not_specialize=["T"])
|
| 322 |
+
def chunk_gated_delta_rule_bwd_kernel_dhu_blockdim64(
|
| 323 |
+
q,
|
| 324 |
+
k,
|
| 325 |
+
w,
|
| 326 |
+
g,
|
| 327 |
+
gk,
|
| 328 |
+
dht,
|
| 329 |
+
dh0,
|
| 330 |
+
do,
|
| 331 |
+
dh,
|
| 332 |
+
dv,
|
| 333 |
+
dv2,
|
| 334 |
+
cu_seqlens,
|
| 335 |
+
chunk_offsets,
|
| 336 |
+
scale,
|
| 337 |
+
T,
|
| 338 |
+
H: tl.constexpr,
|
| 339 |
+
K: tl.constexpr,
|
| 340 |
+
V: tl.constexpr,
|
| 341 |
+
BT: tl.constexpr,
|
| 342 |
+
BV: tl.constexpr,
|
| 343 |
+
USE_G: tl.constexpr,
|
| 344 |
+
USE_GK: tl.constexpr,
|
| 345 |
+
USE_INITIAL_STATE: tl.constexpr,
|
| 346 |
+
USE_FINAL_STATE_GRADIENT: tl.constexpr,
|
| 347 |
+
IS_VARLEN: tl.constexpr,
|
| 348 |
+
):
|
| 349 |
+
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
| 350 |
+
i_n, i_h = i_nh // H, i_nh % H
|
| 351 |
+
if IS_VARLEN:
|
| 352 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(
|
| 353 |
+
cu_seqlens + i_n + 1
|
| 354 |
+
).to(tl.int32)
|
| 355 |
+
T = eos - bos
|
| 356 |
+
NT = tl.cdiv(T, BT)
|
| 357 |
+
boh = tl.load(chunk_offsets + i_n).to(tl.int32)
|
| 358 |
+
else:
|
| 359 |
+
bos, eos = i_n * T, i_n * T + T
|
| 360 |
+
NT = tl.cdiv(T, BT)
|
| 361 |
+
boh = i_n * NT
|
| 362 |
+
|
| 363 |
+
# [BK, BV]
|
| 364 |
+
b_dh1 = tl.zeros([64, BV], dtype=tl.float32)
|
| 365 |
+
if K > 64:
|
| 366 |
+
b_dh2 = tl.zeros([64, BV], dtype=tl.float32)
|
| 367 |
+
if K > 128:
|
| 368 |
+
b_dh3 = tl.zeros([64, BV], dtype=tl.float32)
|
| 369 |
+
if K > 192:
|
| 370 |
+
b_dh4 = tl.zeros([64, BV], dtype=tl.float32)
|
| 371 |
+
|
| 372 |
+
# calculate offset
|
| 373 |
+
q += ((bos * H + i_h) * K).to(tl.int64)
|
| 374 |
+
k += ((bos * H + i_h) * K).to(tl.int64)
|
| 375 |
+
w += ((bos * H + i_h) * K).to(tl.int64)
|
| 376 |
+
do += ((bos * H + i_h) * V).to(tl.int64)
|
| 377 |
+
dv += ((bos * H + i_h) * V).to(tl.int64)
|
| 378 |
+
dv2 += ((bos * H + i_h) * V).to(tl.int64)
|
| 379 |
+
dh += ((boh * H + i_h) * K * V).to(tl.int64)
|
| 380 |
+
if USE_GK:
|
| 381 |
+
gk += ((bos * H + i_h) * K).to(tl.int64)
|
| 382 |
+
|
| 383 |
+
stride_v = H * V
|
| 384 |
+
stride_h = H * K * V
|
| 385 |
+
stride_k = H * K
|
| 386 |
+
if USE_INITIAL_STATE:
|
| 387 |
+
dh0 += i_nh * K * V
|
| 388 |
+
if USE_FINAL_STATE_GRADIENT:
|
| 389 |
+
dht += i_nh * K * V
|
| 390 |
+
|
| 391 |
+
if USE_FINAL_STATE_GRADIENT:
|
| 392 |
+
p_dht1 = tl.make_block_ptr(dht, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
|
| 393 |
+
b_dh1 += tl.load(p_dht1, boundary_check=(0, 1))
|
| 394 |
+
if K > 64:
|
| 395 |
+
p_dht2 = tl.make_block_ptr(
|
| 396 |
+
dht, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 397 |
+
)
|
| 398 |
+
b_dh2 += tl.load(p_dht2, boundary_check=(0, 1))
|
| 399 |
+
if K > 128:
|
| 400 |
+
p_dht3 = tl.make_block_ptr(
|
| 401 |
+
dht, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 402 |
+
)
|
| 403 |
+
b_dh3 += tl.load(p_dht3, boundary_check=(0, 1))
|
| 404 |
+
if K > 192:
|
| 405 |
+
p_dht4 = tl.make_block_ptr(
|
| 406 |
+
dht, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 407 |
+
)
|
| 408 |
+
b_dh4 += tl.load(p_dht4, boundary_check=(0, 1))
|
| 409 |
+
|
| 410 |
+
for i_t in range(NT - 1, -1, -1):
|
| 411 |
+
p_dh1 = tl.make_block_ptr(
|
| 412 |
+
dh + i_t * stride_h, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0)
|
| 413 |
+
)
|
| 414 |
+
tl.store(p_dh1, b_dh1.to(p_dh1.dtype.element_ty), boundary_check=(0, 1))
|
| 415 |
+
if K > 64:
|
| 416 |
+
p_dh2 = tl.make_block_ptr(
|
| 417 |
+
dh + i_t * stride_h, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 418 |
+
)
|
| 419 |
+
tl.store(p_dh2, b_dh2.to(p_dh2.dtype.element_ty), boundary_check=(0, 1))
|
| 420 |
+
if K > 128:
|
| 421 |
+
p_dh3 = tl.make_block_ptr(
|
| 422 |
+
dh + i_t * stride_h, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 423 |
+
)
|
| 424 |
+
tl.store(p_dh3, b_dh3.to(p_dh3.dtype.element_ty), boundary_check=(0, 1))
|
| 425 |
+
if K > 192:
|
| 426 |
+
p_dh4 = tl.make_block_ptr(
|
| 427 |
+
dh + i_t * stride_h, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 428 |
+
)
|
| 429 |
+
tl.store(p_dh4, b_dh4.to(p_dh4.dtype.element_ty), boundary_check=(0, 1))
|
| 430 |
+
|
| 431 |
+
last_idx = min((i_t + 1) * BT, T) - 1
|
| 432 |
+
if USE_G:
|
| 433 |
+
bg_last = tl.load(g + (bos + last_idx) * H + i_h)
|
| 434 |
+
bg_last_exp = exp(bg_last)
|
| 435 |
+
p_g = tl.make_block_ptr(
|
| 436 |
+
g + bos * H + i_h, (T,), (H,), (i_t * BT,), (BT,), (0,)
|
| 437 |
+
)
|
| 438 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 439 |
+
b_g_exp = exp(b_g)
|
| 440 |
+
|
| 441 |
+
p_dv = tl.make_block_ptr(
|
| 442 |
+
dv, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 443 |
+
)
|
| 444 |
+
p_dv2 = tl.make_block_ptr(
|
| 445 |
+
dv2, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 446 |
+
)
|
| 447 |
+
p_do = tl.make_block_ptr(
|
| 448 |
+
do, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 449 |
+
)
|
| 450 |
+
|
| 451 |
+
b_do = tl.load(p_do, boundary_check=(0, 1))
|
| 452 |
+
|
| 453 |
+
# Update dv
|
| 454 |
+
p_k = tl.make_block_ptr(
|
| 455 |
+
k, (T, K), (stride_k, 1), (i_t * BT, 0), (BT, 64), (1, 0)
|
| 456 |
+
)
|
| 457 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 458 |
+
if USE_GK:
|
| 459 |
+
o_k1 = tl.arange(0, 64)
|
| 460 |
+
b_gk_last1 = tl.load(
|
| 461 |
+
gk + last_idx * H * K + o_k1, mask=(o_k1 < K), other=0.0
|
| 462 |
+
)
|
| 463 |
+
b_dv = tl.dot(b_k, b_dh1.to(b_k.dtype))
|
| 464 |
+
|
| 465 |
+
if K > 64:
|
| 466 |
+
p_k = tl.make_block_ptr(
|
| 467 |
+
k, (T, K), (stride_k, 1), (i_t * BT, 64), (BT, 64), (1, 0)
|
| 468 |
+
)
|
| 469 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 470 |
+
if USE_GK:
|
| 471 |
+
o_k2 = 64 + o_k1
|
| 472 |
+
b_gk_last2 = tl.load(
|
| 473 |
+
gk + last_idx * H * K + o_k2, mask=(o_k2 < K), other=0.0
|
| 474 |
+
)
|
| 475 |
+
b_dv = tl.dot(b_k, b_dh2.to(b_k.dtype), acc=b_dv)
|
| 476 |
+
|
| 477 |
+
if K > 128:
|
| 478 |
+
p_k = tl.make_block_ptr(
|
| 479 |
+
k, (T, K), (stride_k, 1), (i_t * BT, 128), (BT, 64), (1, 0)
|
| 480 |
+
)
|
| 481 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 482 |
+
if USE_GK:
|
| 483 |
+
o_k3 = 128 + o_k1
|
| 484 |
+
b_gk_last3 = tl.load(
|
| 485 |
+
gk + last_idx * H * K + o_k3, mask=(o_k3 < K), other=0.0
|
| 486 |
+
)
|
| 487 |
+
b_dv = tl.dot(b_k, b_dh3.to(b_k.dtype), acc=b_dv)
|
| 488 |
+
|
| 489 |
+
if K > 192:
|
| 490 |
+
p_k = tl.make_block_ptr(
|
| 491 |
+
k, (T, K), (stride_k, 1), (i_t * BT, 192), (BT, 64), (1, 0)
|
| 492 |
+
)
|
| 493 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 494 |
+
if USE_GK:
|
| 495 |
+
o_k4 = 192 + o_k1
|
| 496 |
+
b_gk_last4 = tl.load(
|
| 497 |
+
gk + last_idx * H * K + o_k4, mask=(o_k4 < K), other=0.0
|
| 498 |
+
)
|
| 499 |
+
b_dv = tl.dot(b_k, b_dh4.to(b_k.dtype), acc=b_dv)
|
| 500 |
+
|
| 501 |
+
if USE_G:
|
| 502 |
+
m_t = (i_t * BT + tl.arange(0, BT)) < T
|
| 503 |
+
b_dv *= tl.where(m_t, exp(bg_last - b_g), 0)[:, None]
|
| 504 |
+
b_dv += tl.load(p_dv, boundary_check=(0, 1))
|
| 505 |
+
|
| 506 |
+
tl.store(p_dv2, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
|
| 507 |
+
# Update dh
|
| 508 |
+
p_w = tl.make_block_ptr(
|
| 509 |
+
w, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1)
|
| 510 |
+
)
|
| 511 |
+
p_q = tl.make_block_ptr(
|
| 512 |
+
q, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1)
|
| 513 |
+
)
|
| 514 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 515 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 516 |
+
if USE_G:
|
| 517 |
+
b_dh1 *= bg_last_exp
|
| 518 |
+
b_q = b_q * b_g_exp[None, :]
|
| 519 |
+
if USE_GK:
|
| 520 |
+
b_dh1 *= exp(b_gk_last1[:, None])
|
| 521 |
+
b_dh1 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(
|
| 522 |
+
b_w, b_dv.to(b_w.dtype)
|
| 523 |
+
)
|
| 524 |
+
if K > 64:
|
| 525 |
+
p_q = tl.make_block_ptr(
|
| 526 |
+
q, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1)
|
| 527 |
+
)
|
| 528 |
+
p_w = tl.make_block_ptr(
|
| 529 |
+
w, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1)
|
| 530 |
+
)
|
| 531 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 532 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 533 |
+
if USE_G:
|
| 534 |
+
b_dh2 *= bg_last_exp
|
| 535 |
+
b_q = b_q * b_g_exp[None, :]
|
| 536 |
+
if USE_GK:
|
| 537 |
+
b_dh2 *= exp(b_gk_last2[:, None])
|
| 538 |
+
b_dh2 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(
|
| 539 |
+
b_w, b_dv.to(b_w.dtype)
|
| 540 |
+
)
|
| 541 |
+
if K > 128:
|
| 542 |
+
p_q = tl.make_block_ptr(
|
| 543 |
+
q, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1)
|
| 544 |
+
)
|
| 545 |
+
p_w = tl.make_block_ptr(
|
| 546 |
+
w, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1)
|
| 547 |
+
)
|
| 548 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 549 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 550 |
+
if USE_G:
|
| 551 |
+
b_dh3 *= bg_last_exp
|
| 552 |
+
b_q = b_q * b_g_exp[None, :]
|
| 553 |
+
if USE_GK:
|
| 554 |
+
b_dh3 *= exp(b_gk_last3[:, None])
|
| 555 |
+
b_dh3 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(
|
| 556 |
+
b_w, b_dv.to(b_w.dtype)
|
| 557 |
+
)
|
| 558 |
+
if K > 192:
|
| 559 |
+
p_q = tl.make_block_ptr(
|
| 560 |
+
q, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1)
|
| 561 |
+
)
|
| 562 |
+
p_w = tl.make_block_ptr(
|
| 563 |
+
w, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1)
|
| 564 |
+
)
|
| 565 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 566 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 567 |
+
if USE_G:
|
| 568 |
+
b_dh4 *= bg_last_exp
|
| 569 |
+
b_q = b_q * b_g_exp[None, :]
|
| 570 |
+
if USE_GK:
|
| 571 |
+
b_dh4 *= exp(b_gk_last4[:, None])
|
| 572 |
+
b_dh4 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(
|
| 573 |
+
b_w, b_dv.to(b_w.dtype)
|
| 574 |
+
)
|
| 575 |
+
|
| 576 |
+
if USE_INITIAL_STATE:
|
| 577 |
+
p_dh0 = tl.make_block_ptr(dh0, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
|
| 578 |
+
tl.store(p_dh0, b_dh1.to(p_dh0.dtype.element_ty), boundary_check=(0, 1))
|
| 579 |
+
if K > 64:
|
| 580 |
+
p_dh1 = tl.make_block_ptr(
|
| 581 |
+
dh0, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 582 |
+
)
|
| 583 |
+
tl.store(p_dh1, b_dh2.to(p_dh1.dtype.element_ty), boundary_check=(0, 1))
|
| 584 |
+
if K > 128:
|
| 585 |
+
p_dh2 = tl.make_block_ptr(
|
| 586 |
+
dh0, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 587 |
+
)
|
| 588 |
+
tl.store(p_dh2, b_dh3.to(p_dh2.dtype.element_ty), boundary_check=(0, 1))
|
| 589 |
+
if K > 192:
|
| 590 |
+
p_dh3 = tl.make_block_ptr(
|
| 591 |
+
dh0, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 592 |
+
)
|
| 593 |
+
tl.store(p_dh3, b_dh4.to(p_dh3.dtype.element_ty), boundary_check=(0, 1))
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
def chunk_gated_delta_rule_fwd_h(
|
| 597 |
+
k: torch.Tensor,
|
| 598 |
+
w: torch.Tensor,
|
| 599 |
+
u: torch.Tensor,
|
| 600 |
+
g: torch.Tensor | None = None,
|
| 601 |
+
gk: torch.Tensor | None = None,
|
| 602 |
+
initial_state: torch.Tensor | None = None,
|
| 603 |
+
output_final_state: bool = False,
|
| 604 |
+
chunk_size: int = 64, # SY: remove this argument and force chunk size 64?
|
| 605 |
+
save_new_value: bool = True,
|
| 606 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 607 |
+
chunk_indices: torch.LongTensor | None = None,
|
| 608 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 609 |
+
B, T, H, K, V = *k.shape, u.shape[-1]
|
| 610 |
+
BT = chunk_size
|
| 611 |
+
|
| 612 |
+
if chunk_indices is None and cu_seqlens is not None:
|
| 613 |
+
chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size)
|
| 614 |
+
# N: the actual number of sequences in the batch with either equal or variable lengths
|
| 615 |
+
if cu_seqlens is None:
|
| 616 |
+
N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
|
| 617 |
+
else:
|
| 618 |
+
N, NT, chunk_offsets = (
|
| 619 |
+
len(cu_seqlens) - 1,
|
| 620 |
+
len(chunk_indices),
|
| 621 |
+
prepare_chunk_offsets(cu_seqlens, BT),
|
| 622 |
+
)
|
| 623 |
+
assert K <= 256, "current kernel does not support head dimension larger than 256."
|
| 624 |
+
|
| 625 |
+
h = k.new_empty(B, NT, H, K, V)
|
| 626 |
+
final_state = (
|
| 627 |
+
k.new_empty(N, H, K, V, dtype=torch.float32) if output_final_state else None
|
| 628 |
+
)
|
| 629 |
+
|
| 630 |
+
v_new = torch.empty_like(u) if save_new_value else None
|
| 631 |
+
|
| 632 |
+
def grid(meta):
|
| 633 |
+
return (triton.cdiv(V, meta["BV"]), N * H)
|
| 634 |
+
|
| 635 |
+
chunk_gated_delta_rule_fwd_kernel_h_blockdim64[grid](
|
| 636 |
+
k=k,
|
| 637 |
+
v=u,
|
| 638 |
+
w=w,
|
| 639 |
+
v_new=v_new,
|
| 640 |
+
g=g,
|
| 641 |
+
gk=gk,
|
| 642 |
+
h=h,
|
| 643 |
+
h0=initial_state,
|
| 644 |
+
ht=final_state,
|
| 645 |
+
cu_seqlens=cu_seqlens,
|
| 646 |
+
chunk_offsets=chunk_offsets,
|
| 647 |
+
T=T,
|
| 648 |
+
H=H,
|
| 649 |
+
K=K,
|
| 650 |
+
V=V,
|
| 651 |
+
BT=BT,
|
| 652 |
+
)
|
| 653 |
+
return h, v_new, final_state
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
@triton.heuristics(
|
| 657 |
+
{
|
| 658 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 659 |
+
"USE_GK": lambda args: args["gk"] is not None,
|
| 660 |
+
"USE_INITIAL_STATE": lambda args: args["h0"] is not None,
|
| 661 |
+
"STORE_FINAL_STATE": lambda args: args["ht"] is not None,
|
| 662 |
+
"SAVE_NEW_VALUE": lambda args: args["v_new"] is not None,
|
| 663 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 664 |
+
}
|
| 665 |
+
)
|
| 666 |
+
@triton.autotune(
|
| 667 |
+
configs=gated_delta_rule_autotune_configs(
|
| 668 |
+
[
|
| 669 |
+
triton.Config({"BV": BV}, num_warps=num_warps, num_stages=num_stages)
|
| 670 |
+
for num_warps in [2, 4]
|
| 671 |
+
for num_stages in NUM_STAGES_FWD
|
| 672 |
+
for BV in [16, 32, 64]
|
| 673 |
+
]
|
| 674 |
+
),
|
| 675 |
+
key=["H", "K", "V", "BT", "IS_VARLEN"],
|
| 676 |
+
use_cuda_graph=USE_CUDA_GRAPH,
|
| 677 |
+
**autotune_cache_kwargs,
|
| 678 |
+
)
|
| 679 |
+
@triton.jit(do_not_specialize=["T", "T_flat"])
|
| 680 |
+
def chunk_gated_delta_rule_fwd_kernel_h_opt(
|
| 681 |
+
k,
|
| 682 |
+
v,
|
| 683 |
+
w,
|
| 684 |
+
v_new,
|
| 685 |
+
g,
|
| 686 |
+
gk,
|
| 687 |
+
h,
|
| 688 |
+
h0,
|
| 689 |
+
ht,
|
| 690 |
+
cu_seqlens,
|
| 691 |
+
chunk_offsets,
|
| 692 |
+
T,
|
| 693 |
+
T_flat,
|
| 694 |
+
H: tl.constexpr,
|
| 695 |
+
Hg: tl.constexpr,
|
| 696 |
+
K: tl.constexpr,
|
| 697 |
+
V: tl.constexpr,
|
| 698 |
+
BT: tl.constexpr,
|
| 699 |
+
BV: tl.constexpr,
|
| 700 |
+
USE_G: tl.constexpr,
|
| 701 |
+
USE_GK: tl.constexpr,
|
| 702 |
+
USE_INITIAL_STATE: tl.constexpr,
|
| 703 |
+
STORE_FINAL_STATE: tl.constexpr,
|
| 704 |
+
SAVE_NEW_VALUE: tl.constexpr,
|
| 705 |
+
IS_VARLEN: tl.constexpr,
|
| 706 |
+
):
|
| 707 |
+
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
| 708 |
+
i_n, i_h = i_nh // H, i_nh % H
|
| 709 |
+
if IS_VARLEN:
|
| 710 |
+
bos, eos = (
|
| 711 |
+
tl.load(cu_seqlens + i_n).to(tl.int32),
|
| 712 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int32),
|
| 713 |
+
)
|
| 714 |
+
T = eos - bos
|
| 715 |
+
NT = tl.cdiv(T, BT)
|
| 716 |
+
boh = tl.load(chunk_offsets + i_n).to(tl.int32)
|
| 717 |
+
else:
|
| 718 |
+
bos, eos = i_n * T, i_n * T + T
|
| 719 |
+
NT = tl.cdiv(T, BT)
|
| 720 |
+
boh = i_n * NT
|
| 721 |
+
|
| 722 |
+
b_h1 = tl.zeros([64, BV], dtype=tl.float32)
|
| 723 |
+
if K > 64:
|
| 724 |
+
b_h2 = tl.zeros([64, BV], dtype=tl.float32)
|
| 725 |
+
if K > 128:
|
| 726 |
+
b_h3 = tl.zeros([64, BV], dtype=tl.float32)
|
| 727 |
+
if K > 192:
|
| 728 |
+
b_h4 = tl.zeros([64, BV], dtype=tl.float32)
|
| 729 |
+
|
| 730 |
+
h += ((boh * H + i_h) * K * V).to(tl.int64)
|
| 731 |
+
k += ((bos * Hg + i_h // (H // Hg)) * K).to(tl.int64)
|
| 732 |
+
if IS_VARLEN:
|
| 733 |
+
v += ((i_h * T_flat + bos) * V).to(tl.int64)
|
| 734 |
+
w += ((i_h * T_flat + bos) * K).to(tl.int64)
|
| 735 |
+
else:
|
| 736 |
+
v += (((i_n * H + i_h) * T_flat) * V).to(tl.int64)
|
| 737 |
+
w += (((i_n * H + i_h) * T_flat) * K).to(tl.int64)
|
| 738 |
+
stride_v = V
|
| 739 |
+
stride_w = K
|
| 740 |
+
if SAVE_NEW_VALUE:
|
| 741 |
+
if IS_VARLEN:
|
| 742 |
+
v_new += ((i_h * T_flat + bos) * V).to(tl.int64)
|
| 743 |
+
else:
|
| 744 |
+
v_new += (((i_n * H + i_h) * T_flat) * V).to(tl.int64)
|
| 745 |
+
stride_h = H * K * V
|
| 746 |
+
stride_k = Hg * K
|
| 747 |
+
if USE_INITIAL_STATE:
|
| 748 |
+
h0 = h0 + i_nh * K * V
|
| 749 |
+
if STORE_FINAL_STATE:
|
| 750 |
+
ht = ht + i_nh * K * V
|
| 751 |
+
|
| 752 |
+
if USE_INITIAL_STATE:
|
| 753 |
+
p_h0_1 = tl.make_block_ptr(h0, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
|
| 754 |
+
b_h1 += tl.load(p_h0_1, boundary_check=(0, 1)).to(tl.float32)
|
| 755 |
+
if K > 64:
|
| 756 |
+
p_h0_2 = tl.make_block_ptr(
|
| 757 |
+
h0, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 758 |
+
)
|
| 759 |
+
b_h2 += tl.load(p_h0_2, boundary_check=(0, 1)).to(tl.float32)
|
| 760 |
+
if K > 128:
|
| 761 |
+
p_h0_3 = tl.make_block_ptr(
|
| 762 |
+
h0, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 763 |
+
)
|
| 764 |
+
b_h3 += tl.load(p_h0_3, boundary_check=(0, 1)).to(tl.float32)
|
| 765 |
+
if K > 192:
|
| 766 |
+
p_h0_4 = tl.make_block_ptr(
|
| 767 |
+
h0, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 768 |
+
)
|
| 769 |
+
b_h4 += tl.load(p_h0_4, boundary_check=(0, 1)).to(tl.float32)
|
| 770 |
+
|
| 771 |
+
for i_t in range(NT):
|
| 772 |
+
p_h1 = tl.make_block_ptr(
|
| 773 |
+
h + i_t * stride_h, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0)
|
| 774 |
+
)
|
| 775 |
+
tl.store(p_h1, b_h1.to(p_h1.dtype.element_ty), boundary_check=(0, 1))
|
| 776 |
+
if K > 64:
|
| 777 |
+
p_h2 = tl.make_block_ptr(
|
| 778 |
+
h + i_t * stride_h, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 779 |
+
)
|
| 780 |
+
tl.store(p_h2, b_h2.to(p_h2.dtype.element_ty), boundary_check=(0, 1))
|
| 781 |
+
if K > 128:
|
| 782 |
+
p_h3 = tl.make_block_ptr(
|
| 783 |
+
h + i_t * stride_h, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 784 |
+
)
|
| 785 |
+
tl.store(p_h3, b_h3.to(p_h3.dtype.element_ty), boundary_check=(0, 1))
|
| 786 |
+
if K > 192:
|
| 787 |
+
p_h4 = tl.make_block_ptr(
|
| 788 |
+
h + i_t * stride_h, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 789 |
+
)
|
| 790 |
+
tl.store(p_h4, b_h4.to(p_h4.dtype.element_ty), boundary_check=(0, 1))
|
| 791 |
+
|
| 792 |
+
p_w = tl.make_block_ptr(
|
| 793 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 0), (BT, 64), (1, 0)
|
| 794 |
+
)
|
| 795 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 796 |
+
b_v = tl.dot(b_w, b_h1.to(b_w.dtype))
|
| 797 |
+
if K > 64:
|
| 798 |
+
p_w = tl.make_block_ptr(
|
| 799 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 64), (BT, 64), (1, 0)
|
| 800 |
+
)
|
| 801 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 802 |
+
b_v = tl.dot(b_w, b_h2.to(b_w.dtype), acc=b_v)
|
| 803 |
+
if K > 128:
|
| 804 |
+
p_w = tl.make_block_ptr(
|
| 805 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 128), (BT, 64), (1, 0)
|
| 806 |
+
)
|
| 807 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 808 |
+
b_v = tl.dot(b_w, b_h3.to(b_w.dtype), acc=b_v)
|
| 809 |
+
if K > 192:
|
| 810 |
+
p_w = tl.make_block_ptr(
|
| 811 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 192), (BT, 64), (1, 0)
|
| 812 |
+
)
|
| 813 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 814 |
+
b_v = tl.dot(b_w, b_h4.to(b_w.dtype), acc=b_v)
|
| 815 |
+
p_v = tl.make_block_ptr(
|
| 816 |
+
v, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 817 |
+
)
|
| 818 |
+
b_v = tl.load(p_v, boundary_check=(0, 1)) - b_v
|
| 819 |
+
|
| 820 |
+
if SAVE_NEW_VALUE:
|
| 821 |
+
p_vn = tl.make_block_ptr(
|
| 822 |
+
v_new, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 823 |
+
)
|
| 824 |
+
tl.store(p_vn, b_v.to(p_vn.dtype.element_ty), boundary_check=(0, 1))
|
| 825 |
+
|
| 826 |
+
last_idx = min((i_t + 1) * BT, T) - 1
|
| 827 |
+
if USE_G:
|
| 828 |
+
m_t = (i_t * BT + tl.arange(0, BT)) < T
|
| 829 |
+
b_g_last = tl.load(g + bos * H + last_idx * H + i_h)
|
| 830 |
+
p_g = tl.make_block_ptr(
|
| 831 |
+
g + bos * H + i_h, (T,), (H,), (i_t * BT,), (BT,), (0,)
|
| 832 |
+
)
|
| 833 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 834 |
+
b_v = b_v * tl.where(m_t, exp(b_g_last - b_g), 0)[:, None]
|
| 835 |
+
b_g_last = exp(b_g_last)
|
| 836 |
+
b_h1 *= b_g_last
|
| 837 |
+
if K > 64:
|
| 838 |
+
b_h2 *= b_g_last
|
| 839 |
+
if K > 128:
|
| 840 |
+
b_h3 *= b_g_last
|
| 841 |
+
if K > 192:
|
| 842 |
+
b_h4 *= b_g_last
|
| 843 |
+
|
| 844 |
+
if USE_GK:
|
| 845 |
+
o_k1 = tl.arange(0, 64)
|
| 846 |
+
b_gk_last1 = tl.load(
|
| 847 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k1,
|
| 848 |
+
mask=(o_k1 < K),
|
| 849 |
+
other=0.0,
|
| 850 |
+
)
|
| 851 |
+
b_h1 *= exp(b_gk_last1)[:, None]
|
| 852 |
+
if K > 64:
|
| 853 |
+
o_k2 = 64 + o_k1
|
| 854 |
+
b_gk_last2 = tl.load(
|
| 855 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k2,
|
| 856 |
+
mask=(o_k2 < K),
|
| 857 |
+
other=0.0,
|
| 858 |
+
)
|
| 859 |
+
b_h2 *= exp(b_gk_last2)[:, None]
|
| 860 |
+
if K > 128:
|
| 861 |
+
o_k3 = 128 + o_k1
|
| 862 |
+
b_gk_last3 = tl.load(
|
| 863 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k3,
|
| 864 |
+
mask=(o_k3 < K),
|
| 865 |
+
other=0.0,
|
| 866 |
+
)
|
| 867 |
+
b_h3 *= exp(b_gk_last3)[:, None]
|
| 868 |
+
if K > 192:
|
| 869 |
+
o_k4 = 192 + o_k1
|
| 870 |
+
b_gk_last4 = tl.load(
|
| 871 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k4,
|
| 872 |
+
mask=(o_k4 < K),
|
| 873 |
+
other=0.0,
|
| 874 |
+
)
|
| 875 |
+
b_h4 *= exp(b_gk_last4)[:, None]
|
| 876 |
+
b_v = b_v.to(k.dtype.element_ty)
|
| 877 |
+
|
| 878 |
+
p_k = tl.make_block_ptr(
|
| 879 |
+
k, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1)
|
| 880 |
+
)
|
| 881 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 882 |
+
b_h1 = tl.dot(b_k, b_v, acc=b_h1)
|
| 883 |
+
if K > 64:
|
| 884 |
+
p_k = tl.make_block_ptr(
|
| 885 |
+
k, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1)
|
| 886 |
+
)
|
| 887 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 888 |
+
b_h2 = tl.dot(b_k, b_v, acc=b_h2)
|
| 889 |
+
if K > 128:
|
| 890 |
+
p_k = tl.make_block_ptr(
|
| 891 |
+
k, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1)
|
| 892 |
+
)
|
| 893 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 894 |
+
b_h3 = tl.dot(b_k, b_v, acc=b_h3)
|
| 895 |
+
if K > 192:
|
| 896 |
+
p_k = tl.make_block_ptr(
|
| 897 |
+
k, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1)
|
| 898 |
+
)
|
| 899 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 900 |
+
b_h4 = tl.dot(b_k, b_v, acc=b_h4)
|
| 901 |
+
|
| 902 |
+
if STORE_FINAL_STATE:
|
| 903 |
+
p_ht = tl.make_block_ptr(ht, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
|
| 904 |
+
tl.store(p_ht, b_h1.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 905 |
+
if K > 64:
|
| 906 |
+
p_ht = tl.make_block_ptr(
|
| 907 |
+
ht, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0)
|
| 908 |
+
)
|
| 909 |
+
tl.store(p_ht, b_h2.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 910 |
+
if K > 128:
|
| 911 |
+
p_ht = tl.make_block_ptr(
|
| 912 |
+
ht, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0)
|
| 913 |
+
)
|
| 914 |
+
tl.store(p_ht, b_h3.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 915 |
+
if K > 192:
|
| 916 |
+
p_ht = tl.make_block_ptr(
|
| 917 |
+
ht, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0)
|
| 918 |
+
)
|
| 919 |
+
tl.store(p_ht, b_h4.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 920 |
+
|
| 921 |
+
|
| 922 |
+
def chunk_gated_delta_rule_fwd_h_opt(
|
| 923 |
+
k: torch.Tensor,
|
| 924 |
+
w: torch.Tensor,
|
| 925 |
+
u: torch.Tensor,
|
| 926 |
+
g: torch.Tensor | None = None,
|
| 927 |
+
gk: torch.Tensor | None = None,
|
| 928 |
+
initial_state: torch.Tensor | None = None,
|
| 929 |
+
output_final_state: bool = False,
|
| 930 |
+
chunk_size: int = 64,
|
| 931 |
+
save_new_value: bool = True,
|
| 932 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 933 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor | None]:
|
| 934 |
+
"""
|
| 935 |
+
Optimized hidden state forward with Hg-aware k strides.
|
| 936 |
+
|
| 937 |
+
w and u are expected in head-major contiguous layout [B, H, T, K] / [B, H, T, V].
|
| 938 |
+
v_new output is [B, H, T_flat, V].
|
| 939 |
+
"""
|
| 940 |
+
B, T, Hg, K = k.shape
|
| 941 |
+
BT = chunk_size
|
| 942 |
+
|
| 943 |
+
H = w.shape[1]
|
| 944 |
+
V = u.shape[-1]
|
| 945 |
+
T_flat = w.shape[2]
|
| 946 |
+
|
| 947 |
+
if cu_seqlens is not None:
|
| 948 |
+
chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size)
|
| 949 |
+
N = len(cu_seqlens) - 1
|
| 950 |
+
NT = len(chunk_indices)
|
| 951 |
+
chunk_offsets = prepare_chunk_offsets(cu_seqlens, BT)
|
| 952 |
+
else:
|
| 953 |
+
N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
|
| 954 |
+
|
| 955 |
+
assert K <= 256, "current kernel does not support head dimension larger than 256."
|
| 956 |
+
|
| 957 |
+
h = k.new_empty(B, NT, H, K, V)
|
| 958 |
+
final_state = (
|
| 959 |
+
k.new_empty(N, H, K, V, dtype=torch.float32) if output_final_state else None
|
| 960 |
+
)
|
| 961 |
+
v_new = k.new_empty(B, H, T_flat, V, dtype=u.dtype) if save_new_value else None
|
| 962 |
+
|
| 963 |
+
def grid(meta):
|
| 964 |
+
return (triton.cdiv(V, meta["BV"]), N * H)
|
| 965 |
+
|
| 966 |
+
chunk_gated_delta_rule_fwd_kernel_h_opt[grid](
|
| 967 |
+
k=k,
|
| 968 |
+
v=u,
|
| 969 |
+
w=w,
|
| 970 |
+
v_new=v_new,
|
| 971 |
+
g=g,
|
| 972 |
+
gk=gk,
|
| 973 |
+
h=h,
|
| 974 |
+
h0=initial_state,
|
| 975 |
+
ht=final_state,
|
| 976 |
+
cu_seqlens=cu_seqlens,
|
| 977 |
+
chunk_offsets=chunk_offsets,
|
| 978 |
+
T=T,
|
| 979 |
+
T_flat=T_flat,
|
| 980 |
+
H=H,
|
| 981 |
+
Hg=Hg,
|
| 982 |
+
K=K,
|
| 983 |
+
V=V,
|
| 984 |
+
BT=BT,
|
| 985 |
+
)
|
| 986 |
+
return h, v_new, final_state
|
| 987 |
+
|
| 988 |
+
|
| 989 |
+
# =====================================================================
|
| 990 |
+
# opt_vk variant: h layout [V, K] (transposed from opt's [K, V])
|
| 991 |
+
# All other layouts (k, w, u, v_new) are identical to opt.
|
| 992 |
+
# =====================================================================
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
@triton.heuristics(
|
| 996 |
+
{
|
| 997 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 998 |
+
"USE_GK": lambda args: args["gk"] is not None,
|
| 999 |
+
"USE_INITIAL_STATE": lambda args: args["h0"] is not None,
|
| 1000 |
+
"STORE_FINAL_STATE": lambda args: args["ht"] is not None,
|
| 1001 |
+
"SAVE_NEW_VALUE": lambda args: args["v_new"] is not None,
|
| 1002 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 1003 |
+
}
|
| 1004 |
+
)
|
| 1005 |
+
@triton.autotune(
|
| 1006 |
+
configs=gated_delta_rule_autotune_configs(
|
| 1007 |
+
[
|
| 1008 |
+
triton.Config({"BV": BV}, num_warps=num_warps, num_stages=num_stages)
|
| 1009 |
+
for num_warps in [2, 4]
|
| 1010 |
+
for num_stages in NUM_STAGES_FWD
|
| 1011 |
+
for BV in [16, 32, 64]
|
| 1012 |
+
]
|
| 1013 |
+
),
|
| 1014 |
+
key=["H", "K", "V", "BT", "IS_VARLEN"],
|
| 1015 |
+
use_cuda_graph=USE_CUDA_GRAPH,
|
| 1016 |
+
**autotune_cache_kwargs,
|
| 1017 |
+
)
|
| 1018 |
+
@triton.jit(do_not_specialize=["T", "T_flat"])
|
| 1019 |
+
def chunk_gated_delta_rule_fwd_kernel_h_opt_vk(
|
| 1020 |
+
k,
|
| 1021 |
+
v,
|
| 1022 |
+
w,
|
| 1023 |
+
v_new,
|
| 1024 |
+
g,
|
| 1025 |
+
gk,
|
| 1026 |
+
h,
|
| 1027 |
+
h0,
|
| 1028 |
+
ht,
|
| 1029 |
+
cu_seqlens,
|
| 1030 |
+
chunk_offsets,
|
| 1031 |
+
T,
|
| 1032 |
+
T_flat,
|
| 1033 |
+
H: tl.constexpr,
|
| 1034 |
+
Hg: tl.constexpr,
|
| 1035 |
+
K: tl.constexpr,
|
| 1036 |
+
V: tl.constexpr,
|
| 1037 |
+
BT: tl.constexpr,
|
| 1038 |
+
BV: tl.constexpr,
|
| 1039 |
+
USE_G: tl.constexpr,
|
| 1040 |
+
USE_GK: tl.constexpr,
|
| 1041 |
+
USE_INITIAL_STATE: tl.constexpr,
|
| 1042 |
+
STORE_FINAL_STATE: tl.constexpr,
|
| 1043 |
+
SAVE_NEW_VALUE: tl.constexpr,
|
| 1044 |
+
IS_VARLEN: tl.constexpr,
|
| 1045 |
+
USE_EXP2: tl.constexpr = False,
|
| 1046 |
+
):
|
| 1047 |
+
i_v, i_nh = tl.program_id(0), tl.program_id(1)
|
| 1048 |
+
i_n, i_h = i_nh // H, i_nh % H
|
| 1049 |
+
if IS_VARLEN:
|
| 1050 |
+
bos, eos = (
|
| 1051 |
+
tl.load(cu_seqlens + i_n).to(tl.int32),
|
| 1052 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int32),
|
| 1053 |
+
)
|
| 1054 |
+
T = eos - bos
|
| 1055 |
+
NT = tl.cdiv(T, BT)
|
| 1056 |
+
boh = tl.load(chunk_offsets + i_n).to(tl.int32)
|
| 1057 |
+
else:
|
| 1058 |
+
bos, eos = i_n * T, i_n * T + T
|
| 1059 |
+
NT = tl.cdiv(T, BT)
|
| 1060 |
+
boh = i_n * NT
|
| 1061 |
+
|
| 1062 |
+
# [BV, 64] — h in [V, K] layout (transposed from opt's [64, BV])
|
| 1063 |
+
b_h1 = tl.zeros([BV, 64], dtype=tl.float32)
|
| 1064 |
+
if K > 64:
|
| 1065 |
+
b_h2 = tl.zeros([BV, 64], dtype=tl.float32)
|
| 1066 |
+
if K > 128:
|
| 1067 |
+
b_h3 = tl.zeros([BV, 64], dtype=tl.float32)
|
| 1068 |
+
if K > 192:
|
| 1069 |
+
b_h4 = tl.zeros([BV, 64], dtype=tl.float32)
|
| 1070 |
+
|
| 1071 |
+
h += ((boh * H + i_h) * V * K).to(tl.int64)
|
| 1072 |
+
k += ((bos * Hg + i_h // (H // Hg)) * K).to(tl.int64)
|
| 1073 |
+
if IS_VARLEN:
|
| 1074 |
+
v += ((i_h * T_flat + bos) * V).to(tl.int64)
|
| 1075 |
+
w += ((i_h * T_flat + bos) * K).to(tl.int64)
|
| 1076 |
+
else:
|
| 1077 |
+
v += (((i_n * H + i_h) * T_flat) * V).to(tl.int64)
|
| 1078 |
+
w += (((i_n * H + i_h) * T_flat) * K).to(tl.int64)
|
| 1079 |
+
stride_v = V
|
| 1080 |
+
stride_w = K
|
| 1081 |
+
if SAVE_NEW_VALUE:
|
| 1082 |
+
if IS_VARLEN:
|
| 1083 |
+
v_new += ((i_h * T_flat + bos) * V).to(tl.int64)
|
| 1084 |
+
else:
|
| 1085 |
+
v_new += (((i_n * H + i_h) * T_flat) * V).to(tl.int64)
|
| 1086 |
+
stride_h = H * V * K
|
| 1087 |
+
stride_k = Hg * K
|
| 1088 |
+
if USE_INITIAL_STATE:
|
| 1089 |
+
h0 = h0 + i_nh * V * K
|
| 1090 |
+
if STORE_FINAL_STATE:
|
| 1091 |
+
ht = ht + i_nh * V * K
|
| 1092 |
+
|
| 1093 |
+
if USE_G:
|
| 1094 |
+
if IS_VARLEN:
|
| 1095 |
+
g += (i_h * T_flat + bos).to(tl.int64)
|
| 1096 |
+
else:
|
| 1097 |
+
g += (((i_n * H + i_h) * T_flat)).to(tl.int64)
|
| 1098 |
+
|
| 1099 |
+
if USE_INITIAL_STATE:
|
| 1100 |
+
p_h0_1 = tl.make_block_ptr(h0, (V, K), (K, 1), (i_v * BV, 0), (BV, 64), (1, 0))
|
| 1101 |
+
b_h1 += tl.load(p_h0_1, boundary_check=(0, 1)).to(tl.float32)
|
| 1102 |
+
if K > 64:
|
| 1103 |
+
p_h0_2 = tl.make_block_ptr(
|
| 1104 |
+
h0, (V, K), (K, 1), (i_v * BV, 64), (BV, 64), (1, 0)
|
| 1105 |
+
)
|
| 1106 |
+
b_h2 += tl.load(p_h0_2, boundary_check=(0, 1)).to(tl.float32)
|
| 1107 |
+
if K > 128:
|
| 1108 |
+
p_h0_3 = tl.make_block_ptr(
|
| 1109 |
+
h0, (V, K), (K, 1), (i_v * BV, 128), (BV, 64), (1, 0)
|
| 1110 |
+
)
|
| 1111 |
+
b_h3 += tl.load(p_h0_3, boundary_check=(0, 1)).to(tl.float32)
|
| 1112 |
+
if K > 192:
|
| 1113 |
+
p_h0_4 = tl.make_block_ptr(
|
| 1114 |
+
h0, (V, K), (K, 1), (i_v * BV, 192), (BV, 64), (1, 0)
|
| 1115 |
+
)
|
| 1116 |
+
b_h4 += tl.load(p_h0_4, boundary_check=(0, 1)).to(tl.float32)
|
| 1117 |
+
|
| 1118 |
+
for i_t in range(NT):
|
| 1119 |
+
# Store h snapshot [V, K]
|
| 1120 |
+
p_h1 = tl.make_block_ptr(
|
| 1121 |
+
h + i_t * stride_h, (V, K), (K, 1), (i_v * BV, 0), (BV, 64), (1, 0)
|
| 1122 |
+
)
|
| 1123 |
+
tl.store(p_h1, b_h1.to(p_h1.dtype.element_ty), boundary_check=(0, 1))
|
| 1124 |
+
if K > 64:
|
| 1125 |
+
p_h2 = tl.make_block_ptr(
|
| 1126 |
+
h + i_t * stride_h, (V, K), (K, 1), (i_v * BV, 64), (BV, 64), (1, 0)
|
| 1127 |
+
)
|
| 1128 |
+
tl.store(p_h2, b_h2.to(p_h2.dtype.element_ty), boundary_check=(0, 1))
|
| 1129 |
+
if K > 128:
|
| 1130 |
+
p_h3 = tl.make_block_ptr(
|
| 1131 |
+
h + i_t * stride_h, (V, K), (K, 1), (i_v * BV, 128), (BV, 64), (1, 0)
|
| 1132 |
+
)
|
| 1133 |
+
tl.store(p_h3, b_h3.to(p_h3.dtype.element_ty), boundary_check=(0, 1))
|
| 1134 |
+
if K > 192:
|
| 1135 |
+
p_h4 = tl.make_block_ptr(
|
| 1136 |
+
h + i_t * stride_h, (V, K), (K, 1), (i_v * BV, 192), (BV, 64), (1, 0)
|
| 1137 |
+
)
|
| 1138 |
+
tl.store(p_h4, b_h4.to(p_h4.dtype.element_ty), boundary_check=(0, 1))
|
| 1139 |
+
|
| 1140 |
+
# b_v = u - w @ h^T (h is [BV,64], need [64,BV] for dot with w[BT,64])
|
| 1141 |
+
p_w = tl.make_block_ptr(
|
| 1142 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 0), (BT, 64), (1, 0)
|
| 1143 |
+
)
|
| 1144 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 1145 |
+
b_v = tl.dot(b_w, tl.trans(b_h1).to(b_w.dtype))
|
| 1146 |
+
if K > 64:
|
| 1147 |
+
p_w = tl.make_block_ptr(
|
| 1148 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 64), (BT, 64), (1, 0)
|
| 1149 |
+
)
|
| 1150 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 1151 |
+
b_v = tl.dot(b_w, tl.trans(b_h2).to(b_w.dtype), acc=b_v)
|
| 1152 |
+
if K > 128:
|
| 1153 |
+
p_w = tl.make_block_ptr(
|
| 1154 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 128), (BT, 64), (1, 0)
|
| 1155 |
+
)
|
| 1156 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 1157 |
+
b_v = tl.dot(b_w, tl.trans(b_h3).to(b_w.dtype), acc=b_v)
|
| 1158 |
+
if K > 192:
|
| 1159 |
+
p_w = tl.make_block_ptr(
|
| 1160 |
+
w, (T, K), (stride_w, 1), (i_t * BT, 192), (BT, 64), (1, 0)
|
| 1161 |
+
)
|
| 1162 |
+
b_w = tl.load(p_w, boundary_check=(0, 1))
|
| 1163 |
+
b_v = tl.dot(b_w, tl.trans(b_h4).to(b_w.dtype), acc=b_v)
|
| 1164 |
+
p_v = tl.make_block_ptr(
|
| 1165 |
+
v, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 1166 |
+
)
|
| 1167 |
+
b_v = tl.load(p_v, boundary_check=(0, 1)) - b_v
|
| 1168 |
+
|
| 1169 |
+
if SAVE_NEW_VALUE:
|
| 1170 |
+
p_vn = tl.make_block_ptr(
|
| 1171 |
+
v_new, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 1172 |
+
)
|
| 1173 |
+
tl.store(p_vn, b_v.to(p_vn.dtype.element_ty), boundary_check=(0, 1))
|
| 1174 |
+
|
| 1175 |
+
last_idx = min((i_t + 1) * BT, T) - 1
|
| 1176 |
+
if USE_G:
|
| 1177 |
+
m_t = (i_t * BT + tl.arange(0, BT)) < T
|
| 1178 |
+
b_g_last = tl.load(g + last_idx)
|
| 1179 |
+
p_g = tl.make_block_ptr(g, (T,), (1,), (i_t * BT,), (BT,), (0,))
|
| 1180 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 1181 |
+
if USE_EXP2:
|
| 1182 |
+
b_v = b_v * tl.where(m_t, tl.math.exp2(b_g_last - b_g), 0)[:, None]
|
| 1183 |
+
b_g_last = tl.math.exp2(b_g_last)
|
| 1184 |
+
else:
|
| 1185 |
+
b_v = b_v * tl.where(m_t, exp(b_g_last - b_g), 0)[:, None]
|
| 1186 |
+
b_g_last = exp(b_g_last)
|
| 1187 |
+
b_h1 *= b_g_last
|
| 1188 |
+
if K > 64:
|
| 1189 |
+
b_h2 *= b_g_last
|
| 1190 |
+
if K > 128:
|
| 1191 |
+
b_h3 *= b_g_last
|
| 1192 |
+
if K > 192:
|
| 1193 |
+
b_h4 *= b_g_last
|
| 1194 |
+
|
| 1195 |
+
if USE_GK:
|
| 1196 |
+
o_k1 = tl.arange(0, 64)
|
| 1197 |
+
b_gk_last1 = tl.load(
|
| 1198 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k1,
|
| 1199 |
+
mask=(o_k1 < K),
|
| 1200 |
+
other=0.0,
|
| 1201 |
+
)
|
| 1202 |
+
b_h1 *= (tl.math.exp2(b_gk_last1) if USE_EXP2 else exp(b_gk_last1))[None, :]
|
| 1203 |
+
if K > 64:
|
| 1204 |
+
o_k2 = 64 + o_k1
|
| 1205 |
+
b_gk_last2 = tl.load(
|
| 1206 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k2,
|
| 1207 |
+
mask=(o_k2 < K),
|
| 1208 |
+
other=0.0,
|
| 1209 |
+
)
|
| 1210 |
+
b_h2 *= (tl.math.exp2(b_gk_last2) if USE_EXP2 else exp(b_gk_last2))[
|
| 1211 |
+
None, :
|
| 1212 |
+
]
|
| 1213 |
+
if K > 128:
|
| 1214 |
+
o_k3 = 128 + o_k1
|
| 1215 |
+
b_gk_last3 = tl.load(
|
| 1216 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k3,
|
| 1217 |
+
mask=(o_k3 < K),
|
| 1218 |
+
other=0.0,
|
| 1219 |
+
)
|
| 1220 |
+
b_h3 *= (tl.math.exp2(b_gk_last3) if USE_EXP2 else exp(b_gk_last3))[
|
| 1221 |
+
None, :
|
| 1222 |
+
]
|
| 1223 |
+
if K > 192:
|
| 1224 |
+
o_k4 = 192 + o_k1
|
| 1225 |
+
b_gk_last4 = tl.load(
|
| 1226 |
+
gk + (bos + last_idx) * H * K + i_h * K + o_k4,
|
| 1227 |
+
mask=(o_k4 < K),
|
| 1228 |
+
other=0.0,
|
| 1229 |
+
)
|
| 1230 |
+
b_h4 *= (tl.math.exp2(b_gk_last4) if USE_EXP2 else exp(b_gk_last4))[
|
| 1231 |
+
None, :
|
| 1232 |
+
]
|
| 1233 |
+
b_v = b_v.to(k.dtype.element_ty)
|
| 1234 |
+
|
| 1235 |
+
# h[V,K] += v_new^T @ k → [BV,64] += trans(dot(k[64,BT], v[BT,BV]))
|
| 1236 |
+
p_k = tl.make_block_ptr(
|
| 1237 |
+
k, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1)
|
| 1238 |
+
)
|
| 1239 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 1240 |
+
b_h1 += tl.trans(tl.dot(b_k, b_v))
|
| 1241 |
+
if K > 64:
|
| 1242 |
+
p_k = tl.make_block_ptr(
|
| 1243 |
+
k, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1)
|
| 1244 |
+
)
|
| 1245 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 1246 |
+
b_h2 += tl.trans(tl.dot(b_k, b_v))
|
| 1247 |
+
if K > 128:
|
| 1248 |
+
p_k = tl.make_block_ptr(
|
| 1249 |
+
k, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1)
|
| 1250 |
+
)
|
| 1251 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 1252 |
+
b_h3 += tl.trans(tl.dot(b_k, b_v))
|
| 1253 |
+
if K > 192:
|
| 1254 |
+
p_k = tl.make_block_ptr(
|
| 1255 |
+
k, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1)
|
| 1256 |
+
)
|
| 1257 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 1258 |
+
b_h4 += tl.trans(tl.dot(b_k, b_v))
|
| 1259 |
+
|
| 1260 |
+
if STORE_FINAL_STATE:
|
| 1261 |
+
p_ht = tl.make_block_ptr(ht, (V, K), (K, 1), (i_v * BV, 0), (BV, 64), (1, 0))
|
| 1262 |
+
tl.store(p_ht, b_h1.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 1263 |
+
if K > 64:
|
| 1264 |
+
p_ht = tl.make_block_ptr(
|
| 1265 |
+
ht, (V, K), (K, 1), (i_v * BV, 64), (BV, 64), (1, 0)
|
| 1266 |
+
)
|
| 1267 |
+
tl.store(p_ht, b_h2.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 1268 |
+
if K > 128:
|
| 1269 |
+
p_ht = tl.make_block_ptr(
|
| 1270 |
+
ht, (V, K), (K, 1), (i_v * BV, 128), (BV, 64), (1, 0)
|
| 1271 |
+
)
|
| 1272 |
+
tl.store(p_ht, b_h3.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 1273 |
+
if K > 192:
|
| 1274 |
+
p_ht = tl.make_block_ptr(
|
| 1275 |
+
ht, (V, K), (K, 1), (i_v * BV, 192), (BV, 64), (1, 0)
|
| 1276 |
+
)
|
| 1277 |
+
tl.store(p_ht, b_h4.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
|
| 1278 |
+
|
| 1279 |
+
|
| 1280 |
+
def chunk_gated_delta_rule_fwd_h_opt_vk(
|
| 1281 |
+
k: torch.Tensor,
|
| 1282 |
+
w: torch.Tensor,
|
| 1283 |
+
u: torch.Tensor,
|
| 1284 |
+
g: torch.Tensor | None = None,
|
| 1285 |
+
gk: torch.Tensor | None = None,
|
| 1286 |
+
initial_state: torch.Tensor | None = None,
|
| 1287 |
+
output_final_state: bool = False,
|
| 1288 |
+
chunk_size: int = 64,
|
| 1289 |
+
save_new_value: bool = True,
|
| 1290 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 1291 |
+
use_exp2: bool = True,
|
| 1292 |
+
state_dtype: torch.dtype | None = None,
|
| 1293 |
+
num_decodes: int = 0,
|
| 1294 |
+
num_decode_tokens: int = 0,
|
| 1295 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor | None]:
|
| 1296 |
+
"""
|
| 1297 |
+
Optimized hidden state forward with h layout [V, K].
|
| 1298 |
+
|
| 1299 |
+
w and u are expected in head-major contiguous layout [B, H, T, K] / [B, H, T, V].
|
| 1300 |
+
initial_state/final_state: [N, H, V, K].
|
| 1301 |
+
h snapshots: [B, NT, H, V, K].
|
| 1302 |
+
v_new output is [B, H, T_flat, V].
|
| 1303 |
+
`g` is expected in head-major layout [B, H, T].
|
| 1304 |
+
use_exp2 selects whether cumulative gates are interpreted in log2 space.
|
| 1305 |
+
state_dtype selects the initial/final hidden-state dtype (`fp32` or `bf16`);
|
| 1306 |
+
defaults to fp32. The kernel accumulates in fp32 and casts on store.
|
| 1307 |
+
num_decodes / num_decode_tokens skip a leading decode-only prefix in the
|
| 1308 |
+
ORIGINAL cu_seqlens (data tensors are expected pre-sliced); offsets are
|
| 1309 |
+
rebased internally via the cached prologue helpers so the chunk-index /
|
| 1310 |
+
offset build stays cache-warm across forward calls.
|
| 1311 |
+
"""
|
| 1312 |
+
B, T, Hg, K = k.shape
|
| 1313 |
+
BT = chunk_size
|
| 1314 |
+
|
| 1315 |
+
H = w.shape[1]
|
| 1316 |
+
V = u.shape[-1]
|
| 1317 |
+
T_flat = w.shape[2]
|
| 1318 |
+
|
| 1319 |
+
if cu_seqlens is not None:
|
| 1320 |
+
# Pass the ORIGINAL (cache-stable) cu_seqlens + decode ints into the
|
| 1321 |
+
# cached prologue helpers so chunk_indices / chunk_offsets are built
|
| 1322 |
+
# once per (cu_seqlens_id, BT, num_decodes, num_decode_tokens) tuple
|
| 1323 |
+
# (no per-forward .tolist() D2H). The kernel walks the pre-sliced
|
| 1324 |
+
# prefill data via the rebased cu_seqlens.
|
| 1325 |
+
chunk_indices = prepare_chunk_indices(
|
| 1326 |
+
cu_seqlens, chunk_size, num_decodes, num_decode_tokens
|
| 1327 |
+
)
|
| 1328 |
+
chunk_offsets = prepare_chunk_offsets(
|
| 1329 |
+
cu_seqlens, BT, num_decodes, num_decode_tokens
|
| 1330 |
+
)
|
| 1331 |
+
kernel_cu_seqlens = prepare_rebased_cu_seqlens(
|
| 1332 |
+
cu_seqlens, num_decodes, num_decode_tokens
|
| 1333 |
+
)
|
| 1334 |
+
N = len(kernel_cu_seqlens) - 1
|
| 1335 |
+
NT = len(chunk_indices)
|
| 1336 |
+
else:
|
| 1337 |
+
N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
|
| 1338 |
+
kernel_cu_seqlens = None
|
| 1339 |
+
|
| 1340 |
+
assert K <= 256, "current kernel does not support head dimension larger than 256."
|
| 1341 |
+
|
| 1342 |
+
if state_dtype is not None and state_dtype not in (torch.float32, torch.bfloat16):
|
| 1343 |
+
raise ValueError(f"`state_dtype` must be fp32 or bf16, got {state_dtype}.")
|
| 1344 |
+
_state_dtype = state_dtype if state_dtype is not None else torch.float32
|
| 1345 |
+
if (
|
| 1346 |
+
state_dtype is not None
|
| 1347 |
+
and initial_state is not None
|
| 1348 |
+
and initial_state.dtype != _state_dtype
|
| 1349 |
+
):
|
| 1350 |
+
raise ValueError(
|
| 1351 |
+
f"`initial_state.dtype` ({initial_state.dtype}) must match "
|
| 1352 |
+
f"`state_dtype` ({_state_dtype})."
|
| 1353 |
+
)
|
| 1354 |
+
|
| 1355 |
+
if gk is not None:
|
| 1356 |
+
gk = gk.contiguous()
|
| 1357 |
+
if use_exp2:
|
| 1358 |
+
# gk is expressed in natural-log space, so pre-scale it for exp2 kernels.
|
| 1359 |
+
gk = gk * RCP_LN2
|
| 1360 |
+
|
| 1361 |
+
h = k.new_empty(B, NT, H, V, K)
|
| 1362 |
+
final_state = (
|
| 1363 |
+
k.new_empty(N, H, V, K, dtype=_state_dtype) if output_final_state else None
|
| 1364 |
+
)
|
| 1365 |
+
v_new = k.new_empty(B, H, T_flat, V, dtype=u.dtype) if save_new_value else None
|
| 1366 |
+
|
| 1367 |
+
def grid(meta):
|
| 1368 |
+
return (triton.cdiv(V, meta["BV"]), N * H)
|
| 1369 |
+
|
| 1370 |
+
chunk_gated_delta_rule_fwd_kernel_h_opt_vk[grid](
|
| 1371 |
+
k=k,
|
| 1372 |
+
v=u,
|
| 1373 |
+
w=w,
|
| 1374 |
+
v_new=v_new,
|
| 1375 |
+
g=g,
|
| 1376 |
+
gk=gk,
|
| 1377 |
+
h=h,
|
| 1378 |
+
h0=initial_state,
|
| 1379 |
+
ht=final_state,
|
| 1380 |
+
cu_seqlens=kernel_cu_seqlens,
|
| 1381 |
+
chunk_offsets=chunk_offsets,
|
| 1382 |
+
T=T,
|
| 1383 |
+
T_flat=T_flat,
|
| 1384 |
+
H=H,
|
| 1385 |
+
Hg=Hg,
|
| 1386 |
+
K=K,
|
| 1387 |
+
V=V,
|
| 1388 |
+
BT=BT,
|
| 1389 |
+
USE_EXP2=use_exp2,
|
| 1390 |
+
)
|
| 1391 |
+
return h, v_new, final_state
|
| 1392 |
+
|
| 1393 |
+
|
| 1394 |
+
def chunk_gated_delta_rule_bwd_dhu(
|
| 1395 |
+
q: torch.Tensor,
|
| 1396 |
+
k: torch.Tensor,
|
| 1397 |
+
w: torch.Tensor,
|
| 1398 |
+
do: torch.Tensor,
|
| 1399 |
+
dv: torch.Tensor,
|
| 1400 |
+
g: torch.Tensor | None = None,
|
| 1401 |
+
gk: torch.Tensor | None = None,
|
| 1402 |
+
h0: torch.Tensor | None = None,
|
| 1403 |
+
dht: torch.Tensor | None = None,
|
| 1404 |
+
scale: float | None = None,
|
| 1405 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 1406 |
+
chunk_size: int = 64, # SY: remove this argument and force chunk size 64?
|
| 1407 |
+
chunk_indices: torch.LongTensor | None = None,
|
| 1408 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 1409 |
+
B, T, H, K, V = *q.shape, do.shape[-1]
|
| 1410 |
+
# N: the actual number of sequences in the batch with either equal or variable lengths
|
| 1411 |
+
BT = 64
|
| 1412 |
+
assert (
|
| 1413 |
+
K <= 256
|
| 1414 |
+
), "current kernel does not support head dimension being larger than 256."
|
| 1415 |
+
|
| 1416 |
+
if chunk_indices is None and cu_seqlens is not None:
|
| 1417 |
+
chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size)
|
| 1418 |
+
if cu_seqlens is None:
|
| 1419 |
+
N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
|
| 1420 |
+
else:
|
| 1421 |
+
N, NT, chunk_offsets = (
|
| 1422 |
+
len(cu_seqlens) - 1,
|
| 1423 |
+
len(chunk_indices),
|
| 1424 |
+
prepare_chunk_offsets(cu_seqlens, BT),
|
| 1425 |
+
)
|
| 1426 |
+
|
| 1427 |
+
dh = q.new_empty(B, NT, H, K, V)
|
| 1428 |
+
dh0 = torch.empty_like(h0, dtype=torch.float32) if h0 is not None else None
|
| 1429 |
+
dv2 = torch.empty_like(dv)
|
| 1430 |
+
|
| 1431 |
+
def grid(meta):
|
| 1432 |
+
return (triton.cdiv(V, meta["BV"]), N * H)
|
| 1433 |
+
|
| 1434 |
+
chunk_gated_delta_rule_bwd_kernel_dhu_blockdim64[grid](
|
| 1435 |
+
q=q,
|
| 1436 |
+
k=k,
|
| 1437 |
+
w=w,
|
| 1438 |
+
g=g,
|
| 1439 |
+
gk=gk,
|
| 1440 |
+
dht=dht,
|
| 1441 |
+
dh0=dh0,
|
| 1442 |
+
do=do,
|
| 1443 |
+
dh=dh,
|
| 1444 |
+
dv=dv,
|
| 1445 |
+
dv2=dv2,
|
| 1446 |
+
cu_seqlens=cu_seqlens,
|
| 1447 |
+
chunk_offsets=chunk_offsets,
|
| 1448 |
+
scale=scale,
|
| 1449 |
+
T=T,
|
| 1450 |
+
H=H,
|
| 1451 |
+
K=K,
|
| 1452 |
+
V=V,
|
| 1453 |
+
BT=BT,
|
| 1454 |
+
)
|
| 1455 |
+
return dh, dh0, dv2
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/chunk_o.py
ADDED
|
@@ -0,0 +1,1197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
# Adapted from flash-linear-attention: Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Chunk-based output computation (Forward only).
|
| 7 |
+
|
| 8 |
+
This module provides functions for computing the final output in chunk mode.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import triton
|
| 13 |
+
import triton.language as tl
|
| 14 |
+
|
| 15 |
+
from ..gated_delta_rule_utils import (
|
| 16 |
+
IS_NVIDIA_HOPPER,
|
| 17 |
+
autotune_cache_kwargs,
|
| 18 |
+
check_shared_mem,
|
| 19 |
+
gated_delta_rule_autotune_configs,
|
| 20 |
+
)
|
| 21 |
+
from ..utils import prepare_chunk_indices, prepare_rebased_cu_seqlens
|
| 22 |
+
from ..utils.op import exp
|
| 23 |
+
|
| 24 |
+
BKV_LIST = [64, 128] if check_shared_mem() else [32, 64]
|
| 25 |
+
NUM_WARPS = [2, 4] if IS_NVIDIA_HOPPER else [2, 4, 8]
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@triton.heuristics(
|
| 29 |
+
{
|
| 30 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 31 |
+
"USE_G_GAMMA": lambda args: args["g_gamma"] is not None,
|
| 32 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 33 |
+
}
|
| 34 |
+
)
|
| 35 |
+
@triton.autotune(
|
| 36 |
+
configs=gated_delta_rule_autotune_configs(
|
| 37 |
+
[
|
| 38 |
+
triton.Config({"BK": 128, "BV": 128}, num_warps=8, num_stages=3),
|
| 39 |
+
triton.Config({"BK": 64, "BV": 64}, num_warps=4, num_stages=3),
|
| 40 |
+
triton.Config({"BK": 32, "BV": 32}, num_warps=2, num_stages=3),
|
| 41 |
+
]
|
| 42 |
+
),
|
| 43 |
+
key=["H", "K", "V", "BT"],
|
| 44 |
+
**autotune_cache_kwargs,
|
| 45 |
+
)
|
| 46 |
+
@triton.jit(do_not_specialize=["T"])
|
| 47 |
+
def chunk_fwd_kernel_o(
|
| 48 |
+
q,
|
| 49 |
+
k,
|
| 50 |
+
v,
|
| 51 |
+
h,
|
| 52 |
+
g,
|
| 53 |
+
g_gamma,
|
| 54 |
+
o,
|
| 55 |
+
cu_seqlens,
|
| 56 |
+
chunk_indices,
|
| 57 |
+
scale,
|
| 58 |
+
T,
|
| 59 |
+
H: tl.constexpr,
|
| 60 |
+
K: tl.constexpr,
|
| 61 |
+
V: tl.constexpr,
|
| 62 |
+
BT: tl.constexpr,
|
| 63 |
+
BK: tl.constexpr,
|
| 64 |
+
BV: tl.constexpr,
|
| 65 |
+
USE_G: tl.constexpr,
|
| 66 |
+
USE_G_GAMMA: tl.constexpr,
|
| 67 |
+
IS_VARLEN: tl.constexpr,
|
| 68 |
+
):
|
| 69 |
+
i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 70 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 71 |
+
|
| 72 |
+
if IS_VARLEN:
|
| 73 |
+
i_tg = i_t
|
| 74 |
+
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(
|
| 75 |
+
chunk_indices + i_t * 2 + 1
|
| 76 |
+
).to(tl.int32)
|
| 77 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(
|
| 78 |
+
cu_seqlens + i_n + 1
|
| 79 |
+
).to(tl.int32)
|
| 80 |
+
T = eos - bos
|
| 81 |
+
NT = tl.cdiv(T, BT)
|
| 82 |
+
else:
|
| 83 |
+
NT = tl.cdiv(T, BT)
|
| 84 |
+
i_tg = i_b * NT + i_t
|
| 85 |
+
bos, eos = i_b * T, i_b * T + T
|
| 86 |
+
|
| 87 |
+
# offset calculation
|
| 88 |
+
q += (bos * H + i_h) * K
|
| 89 |
+
k += (bos * H + i_h) * K
|
| 90 |
+
v += (bos * H + i_h) * V
|
| 91 |
+
o += (bos * H + i_h) * V
|
| 92 |
+
h += (i_tg * H + i_h).to(tl.int64) * K * V
|
| 93 |
+
|
| 94 |
+
b_o = tl.zeros([BT, BV], dtype=tl.float32)
|
| 95 |
+
b_A = tl.zeros([BT, BT], dtype=tl.float32)
|
| 96 |
+
|
| 97 |
+
for i_k in range(tl.cdiv(K, BK)):
|
| 98 |
+
p_q = tl.make_block_ptr(
|
| 99 |
+
q, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 100 |
+
)
|
| 101 |
+
p_k = tl.make_block_ptr(
|
| 102 |
+
k, (K, T), (1, H * K), (i_k * BK, i_t * BT), (BK, BT), (0, 1)
|
| 103 |
+
)
|
| 104 |
+
p_h = tl.make_block_ptr(
|
| 105 |
+
h, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0)
|
| 106 |
+
)
|
| 107 |
+
# [BT, BK]
|
| 108 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 109 |
+
# [BK, BT]
|
| 110 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 111 |
+
# [BK, BV]
|
| 112 |
+
b_h = tl.load(p_h, boundary_check=(0, 1))
|
| 113 |
+
|
| 114 |
+
# [BT, BK] @ [BK, BV] -> [BT, BV]
|
| 115 |
+
b_o = tl.dot(b_q, b_h, acc=b_o)
|
| 116 |
+
# [BT, BK] @ [BK, BT] -> [BT, BT]
|
| 117 |
+
b_A = tl.dot(b_q, b_k, acc=b_A)
|
| 118 |
+
|
| 119 |
+
if USE_G:
|
| 120 |
+
g += bos * H + i_h
|
| 121 |
+
p_g = tl.make_block_ptr(g, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 122 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 123 |
+
b_o = b_o * exp(b_g)[:, None]
|
| 124 |
+
b_A = b_A * exp(b_g[:, None] - b_g[None, :])
|
| 125 |
+
|
| 126 |
+
if USE_G_GAMMA:
|
| 127 |
+
b_gamma = tl.load(g_gamma + i_h)
|
| 128 |
+
b_g = b_gamma * (tl.arange(0, BT) + 1)
|
| 129 |
+
b_o = b_o * exp(b_g)[:, None]
|
| 130 |
+
b_A = b_A * exp(b_g[:, None] - b_g[None, :])
|
| 131 |
+
|
| 132 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 133 |
+
m_t = o_t < T
|
| 134 |
+
m_A = (o_t[:, None] >= o_t[None, :]) & (m_t[:, None] & m_t)
|
| 135 |
+
b_A = tl.where(m_A, b_A, 0)
|
| 136 |
+
|
| 137 |
+
p_v = tl.make_block_ptr(
|
| 138 |
+
v, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 139 |
+
)
|
| 140 |
+
p_o = tl.make_block_ptr(
|
| 141 |
+
o, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
b_v = tl.load(p_v, boundary_check=(0, 1))
|
| 145 |
+
# to fix mma -> mma layout conversion
|
| 146 |
+
# already solved by triton v3.2 or higher
|
| 147 |
+
b_o = b_o * scale + tl.dot(b_A.to(b_v.dtype), b_v) * scale
|
| 148 |
+
tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
@triton.heuristics(
|
| 152 |
+
{
|
| 153 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 154 |
+
"USE_G_GAMMA": lambda args: args["g_gamma"] is not None,
|
| 155 |
+
"USE_DW": lambda args: args["dw"] is not None,
|
| 156 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 157 |
+
}
|
| 158 |
+
)
|
| 159 |
+
@triton.autotune(
|
| 160 |
+
configs=gated_delta_rule_autotune_configs(
|
| 161 |
+
[
|
| 162 |
+
triton.Config({}, num_warps=num_warps, num_stages=num_stages)
|
| 163 |
+
for num_warps in NUM_WARPS
|
| 164 |
+
for num_stages in [2, 3, 4]
|
| 165 |
+
]
|
| 166 |
+
),
|
| 167 |
+
key=["H", "K", "V", "BT", "BK", "BV", "USE_G", "USE_G_GAMMA", "USE_DW"],
|
| 168 |
+
**autotune_cache_kwargs,
|
| 169 |
+
)
|
| 170 |
+
@triton.jit(do_not_specialize=["T"])
|
| 171 |
+
def chunk_bwd_kernel_dqkwg(
|
| 172 |
+
q,
|
| 173 |
+
k,
|
| 174 |
+
v,
|
| 175 |
+
g,
|
| 176 |
+
g_gamma,
|
| 177 |
+
h,
|
| 178 |
+
do,
|
| 179 |
+
dh,
|
| 180 |
+
dq,
|
| 181 |
+
dk,
|
| 182 |
+
dw,
|
| 183 |
+
dv,
|
| 184 |
+
dg,
|
| 185 |
+
cu_seqlens,
|
| 186 |
+
chunk_indices,
|
| 187 |
+
scale,
|
| 188 |
+
B: tl.constexpr,
|
| 189 |
+
T,
|
| 190 |
+
H: tl.constexpr,
|
| 191 |
+
K: tl.constexpr,
|
| 192 |
+
V: tl.constexpr,
|
| 193 |
+
BT: tl.constexpr,
|
| 194 |
+
BK: tl.constexpr,
|
| 195 |
+
BV: tl.constexpr,
|
| 196 |
+
USE_G: tl.constexpr,
|
| 197 |
+
USE_G_GAMMA: tl.constexpr,
|
| 198 |
+
USE_DW: tl.constexpr,
|
| 199 |
+
IS_VARLEN: tl.constexpr,
|
| 200 |
+
):
|
| 201 |
+
i_k, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 202 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 203 |
+
|
| 204 |
+
all = B * T
|
| 205 |
+
if IS_VARLEN:
|
| 206 |
+
i_tg = i_t
|
| 207 |
+
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(
|
| 208 |
+
chunk_indices + i_t * 2 + 1
|
| 209 |
+
).to(tl.int32)
|
| 210 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(
|
| 211 |
+
cu_seqlens + i_n + 1
|
| 212 |
+
).to(tl.int32)
|
| 213 |
+
T = eos - bos
|
| 214 |
+
NT = tl.cdiv(T, BT)
|
| 215 |
+
else:
|
| 216 |
+
NT = tl.cdiv(T, BT)
|
| 217 |
+
i_tg = i_b * NT + i_t
|
| 218 |
+
bos, eos = i_b * T, i_b * T + T
|
| 219 |
+
|
| 220 |
+
# offset calculation
|
| 221 |
+
v += (bos * H + i_h) * V
|
| 222 |
+
do += (bos * H + i_h) * V
|
| 223 |
+
h += (i_tg * H + i_h).to(tl.int64) * K * V
|
| 224 |
+
dh += (i_tg * H + i_h).to(tl.int64) * K * V
|
| 225 |
+
q += (bos * H + i_h) * K
|
| 226 |
+
k += (bos * H + i_h) * K
|
| 227 |
+
dq += (bos * H + i_h) * K
|
| 228 |
+
dk += (bos * H + i_h) * K
|
| 229 |
+
|
| 230 |
+
# for delta rule only
|
| 231 |
+
if USE_DW:
|
| 232 |
+
dw += (bos * H + i_h) * K
|
| 233 |
+
dv += (bos * H + i_h) * V
|
| 234 |
+
|
| 235 |
+
if USE_G:
|
| 236 |
+
dg += i_k * all * H
|
| 237 |
+
b_dg_last = tl.zeros([1], dtype=tl.float32) if USE_G else None
|
| 238 |
+
if USE_G_GAMMA:
|
| 239 |
+
b_gamma = tl.load(g_gamma + i_h)
|
| 240 |
+
b_g = b_gamma * (tl.arange(0, BT) + 1)
|
| 241 |
+
b_g_last = b_gamma * min(BT, T - i_t * BT)
|
| 242 |
+
b_dq = tl.zeros([BT, BK], dtype=tl.float32)
|
| 243 |
+
b_dk = tl.zeros([BT, BK], dtype=tl.float32)
|
| 244 |
+
b_ds = tl.zeros([BT, BT], dtype=tl.float32)
|
| 245 |
+
b_dw = tl.zeros([BT, BK], dtype=tl.float32) if USE_DW else None
|
| 246 |
+
|
| 247 |
+
for i_v in range(tl.cdiv(V, BV)):
|
| 248 |
+
p_v = tl.make_block_ptr(
|
| 249 |
+
v, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 250 |
+
)
|
| 251 |
+
p_do = tl.make_block_ptr(
|
| 252 |
+
do, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 253 |
+
)
|
| 254 |
+
p_h = tl.make_block_ptr(
|
| 255 |
+
h, (V, K), (1, V), (i_v * BV, i_k * BK), (BV, BK), (0, 1)
|
| 256 |
+
)
|
| 257 |
+
p_dh = tl.make_block_ptr(
|
| 258 |
+
dh, (V, K), (1, V), (i_v * BV, i_k * BK), (BV, BK), (0, 1)
|
| 259 |
+
)
|
| 260 |
+
# [BT, BV]
|
| 261 |
+
b_v = tl.load(p_v, boundary_check=(0, 1))
|
| 262 |
+
b_do = tl.load(p_do, boundary_check=(0, 1))
|
| 263 |
+
# [BV, BK]
|
| 264 |
+
b_h = tl.load(p_h, boundary_check=(0, 1))
|
| 265 |
+
b_dh = tl.load(p_dh, boundary_check=(0, 1))
|
| 266 |
+
if USE_G:
|
| 267 |
+
b_dg_last += tl.sum(b_h * b_dh)
|
| 268 |
+
# [BT, BV] @ [BV, BT] -> [BT, BT]
|
| 269 |
+
b_ds = tl.dot(b_do, tl.trans(b_v), acc=b_ds)
|
| 270 |
+
# [BT, BV] @ [BV, BK] -> [BT, BK]
|
| 271 |
+
b_dq = tl.dot(b_do, b_h.to(b_do.dtype), acc=b_dq)
|
| 272 |
+
# [BT, BV] @ [BV, BK] -> [BT, BK]
|
| 273 |
+
b_dk = tl.dot(b_v, b_dh.to(b_v.dtype), acc=b_dk)
|
| 274 |
+
if USE_DW:
|
| 275 |
+
p_dv = tl.make_block_ptr(
|
| 276 |
+
dv, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 277 |
+
)
|
| 278 |
+
b_dv = tl.load(p_dv, boundary_check=(0, 1))
|
| 279 |
+
b_dw = tl.dot(b_dv.to(b_v.dtype), b_h.to(b_v.dtype), acc=b_dw)
|
| 280 |
+
|
| 281 |
+
if USE_DW:
|
| 282 |
+
p_dw = tl.make_block_ptr(
|
| 283 |
+
dw, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 284 |
+
)
|
| 285 |
+
tl.store(p_dw, -b_dw.to(p_dw.dtype.element_ty), boundary_check=(0, 1))
|
| 286 |
+
|
| 287 |
+
tl.debug_barrier()
|
| 288 |
+
p_q = tl.make_block_ptr(
|
| 289 |
+
q, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 290 |
+
)
|
| 291 |
+
p_k = tl.make_block_ptr(
|
| 292 |
+
k, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 293 |
+
)
|
| 294 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 295 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 296 |
+
|
| 297 |
+
p_dq = tl.make_block_ptr(
|
| 298 |
+
dq, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 299 |
+
)
|
| 300 |
+
p_dk = tl.make_block_ptr(
|
| 301 |
+
dk, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 305 |
+
m_t = o_t < T
|
| 306 |
+
m_A = (o_t[:, None] >= o_t[None, :]) & (m_t[:, None] & m_t)
|
| 307 |
+
if USE_G:
|
| 308 |
+
b_dg = tl.zeros([BT], dtype=tl.float32)
|
| 309 |
+
g += bos * H + i_h
|
| 310 |
+
dg += bos * H + i_h
|
| 311 |
+
p_g = tl.make_block_ptr(g, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 312 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 313 |
+
b_g_last = tl.load(g + (min(i_t * BT + BT, T) - 1) * H)
|
| 314 |
+
b_dg_last *= exp(b_g_last)
|
| 315 |
+
|
| 316 |
+
b_dq = b_dq * exp(b_g)[:, None] * scale
|
| 317 |
+
b_dg += tl.sum(b_dq * b_q, axis=1)
|
| 318 |
+
|
| 319 |
+
b_dk = b_dk * tl.where(m_t, exp(-b_g + b_g_last), 0)[:, None]
|
| 320 |
+
b_dg -= tl.sum(b_k * b_dk, axis=1)
|
| 321 |
+
b_dg_last += tl.sum(b_dk * b_k)
|
| 322 |
+
|
| 323 |
+
b_ds = tl.where(m_A, b_ds * exp(b_g[:, None] - b_g[None, :]), 0) * scale
|
| 324 |
+
b_ds2 = b_ds * tl.dot(b_q, tl.trans(b_k))
|
| 325 |
+
b_dg += tl.sum(b_ds2, axis=1)
|
| 326 |
+
b_dg -= tl.sum(b_ds2, axis=0)
|
| 327 |
+
|
| 328 |
+
b_ds = b_ds.to(b_k.dtype)
|
| 329 |
+
# [BT, BK]
|
| 330 |
+
b_dq = tl.dot(b_ds, b_k, acc=b_dq)
|
| 331 |
+
b_dk = tl.dot(tl.trans(b_ds), b_q, acc=b_dk)
|
| 332 |
+
p_dg = tl.make_block_ptr(dg, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 333 |
+
# (SY 09/21) revcumsum in a separate kernel due to strange triton compiler issue
|
| 334 |
+
# b_dg = tl.dot(tl.where(o_t[:, None] <= o_t[None, :], 1., 0.), b_dg, allow_tf32=False) + b_dg_last)
|
| 335 |
+
b_dg = tl.where(o_t < min(i_t * BT + BT, T) - 1, b_dg, b_dg + b_dg_last)
|
| 336 |
+
tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), boundary_check=(0, 1))
|
| 337 |
+
tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), boundary_check=(0, 1))
|
| 338 |
+
tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), boundary_check=(0,))
|
| 339 |
+
|
| 340 |
+
elif USE_G_GAMMA:
|
| 341 |
+
b_dq = b_dq * exp(b_g)[:, None] * scale
|
| 342 |
+
b_dk = b_dk * tl.where(m_t, exp(-b_g + b_g_last), 0)[:, None]
|
| 343 |
+
b_ds = tl.where(m_A, b_ds * exp(b_g[:, None] - b_g[None, :]), 0) * scale
|
| 344 |
+
b_ds = b_ds.to(b_k.dtype)
|
| 345 |
+
# [BT, BK]
|
| 346 |
+
b_dq = tl.dot(b_ds, b_k, acc=b_dq)
|
| 347 |
+
b_dk = tl.dot(tl.trans(b_ds), b_q, acc=b_dk)
|
| 348 |
+
tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), boundary_check=(0, 1))
|
| 349 |
+
tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), boundary_check=(0, 1))
|
| 350 |
+
|
| 351 |
+
else:
|
| 352 |
+
b_ds = tl.where(m_A, b_ds, 0)
|
| 353 |
+
b_ds = b_ds.to(b_k.dtype)
|
| 354 |
+
b_dq = tl.dot(b_ds, b_k, acc=b_dq)
|
| 355 |
+
b_dk += tl.dot(tl.trans(b_ds), b_q) * scale
|
| 356 |
+
b_dq *= scale
|
| 357 |
+
tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), boundary_check=(0, 1))
|
| 358 |
+
tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), boundary_check=(0, 1))
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
@triton.heuristics(
|
| 362 |
+
{
|
| 363 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 364 |
+
"USE_G_GAMMA": lambda args: args["g_gamma"] is not None,
|
| 365 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 366 |
+
}
|
| 367 |
+
)
|
| 368 |
+
@triton.autotune(
|
| 369 |
+
configs=gated_delta_rule_autotune_configs(
|
| 370 |
+
[
|
| 371 |
+
triton.Config({}, num_warps=num_warps, num_stages=num_stages)
|
| 372 |
+
for num_warps in NUM_WARPS
|
| 373 |
+
for num_stages in [2, 3, 4]
|
| 374 |
+
]
|
| 375 |
+
),
|
| 376 |
+
key=["H", "K", "V", "BT", "BK", "BV", "USE_G", "USE_G_GAMMA"],
|
| 377 |
+
**autotune_cache_kwargs,
|
| 378 |
+
)
|
| 379 |
+
@triton.jit(do_not_specialize=["T"])
|
| 380 |
+
def chunk_bwd_kernel_dv(
|
| 381 |
+
q,
|
| 382 |
+
k,
|
| 383 |
+
g,
|
| 384 |
+
g_gamma,
|
| 385 |
+
do,
|
| 386 |
+
dv,
|
| 387 |
+
dh,
|
| 388 |
+
cu_seqlens,
|
| 389 |
+
chunk_indices,
|
| 390 |
+
scale,
|
| 391 |
+
T,
|
| 392 |
+
H: tl.constexpr,
|
| 393 |
+
K: tl.constexpr,
|
| 394 |
+
V: tl.constexpr,
|
| 395 |
+
BT: tl.constexpr,
|
| 396 |
+
BK: tl.constexpr,
|
| 397 |
+
BV: tl.constexpr,
|
| 398 |
+
USE_G: tl.constexpr,
|
| 399 |
+
USE_G_GAMMA: tl.constexpr,
|
| 400 |
+
IS_VARLEN: tl.constexpr,
|
| 401 |
+
):
|
| 402 |
+
i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 403 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 404 |
+
if IS_VARLEN:
|
| 405 |
+
i_tg = i_t
|
| 406 |
+
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(
|
| 407 |
+
chunk_indices + i_t * 2 + 1
|
| 408 |
+
).to(tl.int32)
|
| 409 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(
|
| 410 |
+
cu_seqlens + i_n + 1
|
| 411 |
+
).to(tl.int32)
|
| 412 |
+
T = eos - bos
|
| 413 |
+
NT = tl.cdiv(T, BT)
|
| 414 |
+
else:
|
| 415 |
+
NT = tl.cdiv(T, BT)
|
| 416 |
+
i_tg = i_b * NT + i_t
|
| 417 |
+
bos, eos = i_b * T, i_b * T + T
|
| 418 |
+
|
| 419 |
+
b_dv = tl.zeros([BT, BV], dtype=tl.float32)
|
| 420 |
+
|
| 421 |
+
# offset calculation
|
| 422 |
+
q += (bos * H + i_h) * K
|
| 423 |
+
k += (bos * H + i_h) * K
|
| 424 |
+
do += (bos * H + i_h) * V
|
| 425 |
+
dv += (bos * H + i_h) * V
|
| 426 |
+
dh += (i_tg * H + i_h).to(tl.int64) * K * V
|
| 427 |
+
|
| 428 |
+
b_A = tl.zeros([BT, BT], dtype=tl.float32)
|
| 429 |
+
for i_k in range(tl.cdiv(K, BK)):
|
| 430 |
+
p_k = tl.make_block_ptr(
|
| 431 |
+
k, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 432 |
+
)
|
| 433 |
+
p_q = tl.make_block_ptr(
|
| 434 |
+
q, (K, T), (1, H * K), (i_k * BK, i_t * BT), (BK, BT), (0, 1)
|
| 435 |
+
)
|
| 436 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 437 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 438 |
+
b_A = tl.dot(b_k, b_q, acc=b_A)
|
| 439 |
+
p_dh = tl.make_block_ptr(
|
| 440 |
+
dh, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0)
|
| 441 |
+
)
|
| 442 |
+
b_dh = tl.load(p_dh, boundary_check=(0, 1))
|
| 443 |
+
b_dv = tl.dot(b_k, b_dh.to(b_k.dtype), acc=b_dv)
|
| 444 |
+
|
| 445 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 446 |
+
m_t = o_t < T
|
| 447 |
+
if USE_G:
|
| 448 |
+
g += bos * H + i_h
|
| 449 |
+
p_g = tl.make_block_ptr(g, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 450 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 451 |
+
b_g_last = tl.load(g + (min(i_t * BT + BT, T) - 1) * H)
|
| 452 |
+
if USE_G_GAMMA:
|
| 453 |
+
b_gamma = tl.load(g_gamma + i_h)
|
| 454 |
+
b_g = b_gamma * (tl.arange(0, BT) + 1)
|
| 455 |
+
b_g_last = b_gamma * min(BT, T - i_t * BT)
|
| 456 |
+
|
| 457 |
+
m_A = (o_t[:, None] <= o_t[None, :]) & (m_t[:, None] & m_t)
|
| 458 |
+
if USE_G or USE_G_GAMMA:
|
| 459 |
+
b_A = tl.where(m_A, b_A * exp(b_g[None, :] - b_g[:, None]) * scale, 0).to(
|
| 460 |
+
do.dtype.element_ty
|
| 461 |
+
)
|
| 462 |
+
b_dv *= tl.where(m_t, exp(-b_g + b_g_last), 0)[:, None]
|
| 463 |
+
else:
|
| 464 |
+
b_A = tl.where(m_A, b_A * scale, 0).to(do.dtype.element_ty)
|
| 465 |
+
p_do = tl.make_block_ptr(
|
| 466 |
+
do, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 467 |
+
)
|
| 468 |
+
p_dv = tl.make_block_ptr(
|
| 469 |
+
dv, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 470 |
+
)
|
| 471 |
+
b_do = tl.load(p_do, boundary_check=(0, 1))
|
| 472 |
+
b_dv = tl.dot(b_A.to(b_do.dtype), b_do, acc=b_dv)
|
| 473 |
+
tl.store(p_dv, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
@triton.heuristics(
|
| 477 |
+
{
|
| 478 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 479 |
+
"USE_G_GAMMA": lambda args: args["g_gamma"] is not None,
|
| 480 |
+
"USE_A": lambda args: args["A"] is not None,
|
| 481 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 482 |
+
}
|
| 483 |
+
)
|
| 484 |
+
@triton.autotune(
|
| 485 |
+
configs=gated_delta_rule_autotune_configs(
|
| 486 |
+
[
|
| 487 |
+
triton.Config({}, num_warps=num_warps, num_stages=num_stages)
|
| 488 |
+
for num_warps in NUM_WARPS
|
| 489 |
+
for num_stages in [2, 3, 4]
|
| 490 |
+
]
|
| 491 |
+
),
|
| 492 |
+
key=["H", "K", "V", "BT", "BK", "BV", "USE_G"],
|
| 493 |
+
**autotune_cache_kwargs,
|
| 494 |
+
)
|
| 495 |
+
@triton.jit(do_not_specialize=["T"])
|
| 496 |
+
def chunk_bwd_kernel_dv_local(
|
| 497 |
+
q,
|
| 498 |
+
k,
|
| 499 |
+
g,
|
| 500 |
+
g_gamma,
|
| 501 |
+
A,
|
| 502 |
+
do,
|
| 503 |
+
dv,
|
| 504 |
+
cu_seqlens,
|
| 505 |
+
chunk_indices,
|
| 506 |
+
scale,
|
| 507 |
+
T,
|
| 508 |
+
H: tl.constexpr,
|
| 509 |
+
K: tl.constexpr,
|
| 510 |
+
V: tl.constexpr,
|
| 511 |
+
BT: tl.constexpr,
|
| 512 |
+
BK: tl.constexpr,
|
| 513 |
+
BV: tl.constexpr,
|
| 514 |
+
USE_G: tl.constexpr,
|
| 515 |
+
USE_G_GAMMA: tl.constexpr,
|
| 516 |
+
USE_A: tl.constexpr,
|
| 517 |
+
IS_VARLEN: tl.constexpr,
|
| 518 |
+
):
|
| 519 |
+
i_t, i_bh = tl.program_id(0), tl.program_id(1)
|
| 520 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 521 |
+
if IS_VARLEN:
|
| 522 |
+
i_n, i_t = tl.load(chunk_indices + i_t * 2).to(tl.int32), tl.load(
|
| 523 |
+
chunk_indices + i_t * 2 + 1
|
| 524 |
+
).to(tl.int32)
|
| 525 |
+
bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(
|
| 526 |
+
cu_seqlens + i_n + 1
|
| 527 |
+
).to(tl.int32)
|
| 528 |
+
T = eos - bos
|
| 529 |
+
else:
|
| 530 |
+
bos, eos = i_b * T, i_b * T + T
|
| 531 |
+
|
| 532 |
+
# offset calculation
|
| 533 |
+
q += (bos * H + i_h) * K
|
| 534 |
+
k += (bos * H + i_h) * K
|
| 535 |
+
do += (bos * H + i_h) * V
|
| 536 |
+
dv += (bos * H + i_h) * V
|
| 537 |
+
|
| 538 |
+
if USE_A:
|
| 539 |
+
p_A = tl.make_block_ptr(
|
| 540 |
+
A + (bos * H + i_h) * BT,
|
| 541 |
+
(BT, T),
|
| 542 |
+
(1, H * BT),
|
| 543 |
+
(0, i_t * BT),
|
| 544 |
+
(BT, BT),
|
| 545 |
+
(0, 1),
|
| 546 |
+
)
|
| 547 |
+
b_A = tl.load(p_A, boundary_check=(0, 1))
|
| 548 |
+
else:
|
| 549 |
+
if USE_G:
|
| 550 |
+
g += bos * H + i_h
|
| 551 |
+
p_g = tl.make_block_ptr(g, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 552 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 553 |
+
if USE_G_GAMMA:
|
| 554 |
+
b_gamma = tl.load(g_gamma + i_h)
|
| 555 |
+
b_g = b_gamma * (tl.arange(0, BT) + 1)
|
| 556 |
+
|
| 557 |
+
b_A = tl.zeros([BT, BT], dtype=tl.float32)
|
| 558 |
+
for i_k in range(tl.cdiv(K, BK)):
|
| 559 |
+
p_k = tl.make_block_ptr(
|
| 560 |
+
k, (T, K), (H * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 561 |
+
)
|
| 562 |
+
p_q = tl.make_block_ptr(
|
| 563 |
+
q, (K, T), (1, H * K), (i_k * BK, i_t * BT), (BK, BT), (0, 1)
|
| 564 |
+
)
|
| 565 |
+
|
| 566 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 567 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 568 |
+
b_A += tl.dot(b_k, b_q) * scale
|
| 569 |
+
if USE_G or USE_G_GAMMA:
|
| 570 |
+
b_A *= exp(b_g[None, :] - b_g[:, None])
|
| 571 |
+
|
| 572 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 573 |
+
m_t = o_t < T
|
| 574 |
+
m_A = (o_t[:, None] <= o_t[None, :]) & (m_t[:, None] & m_t)
|
| 575 |
+
b_A = tl.where(m_A, b_A, 0).to(do.dtype.element_ty)
|
| 576 |
+
|
| 577 |
+
for i_v in range(tl.cdiv(V, BV)):
|
| 578 |
+
p_do = tl.make_block_ptr(
|
| 579 |
+
do, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 580 |
+
)
|
| 581 |
+
p_dv = tl.make_block_ptr(
|
| 582 |
+
dv, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 583 |
+
)
|
| 584 |
+
b_do = tl.load(p_do, boundary_check=(0, 1))
|
| 585 |
+
b_dv = tl.dot(b_A.to(b_do.dtype), b_do)
|
| 586 |
+
tl.store(p_dv, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
def chunk_fwd_o(
|
| 590 |
+
q: torch.Tensor,
|
| 591 |
+
k: torch.Tensor,
|
| 592 |
+
v: torch.Tensor,
|
| 593 |
+
h: torch.Tensor,
|
| 594 |
+
g: torch.Tensor | None = None,
|
| 595 |
+
g_gamma: torch.Tensor | None = None,
|
| 596 |
+
scale: float | None = None,
|
| 597 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 598 |
+
chunk_size: int = 64,
|
| 599 |
+
) -> torch.Tensor:
|
| 600 |
+
B, T, H, K, V = *q.shape, v.shape[-1]
|
| 601 |
+
BT = chunk_size
|
| 602 |
+
chunk_indices = (
|
| 603 |
+
prepare_chunk_indices(cu_seqlens, BT) if cu_seqlens is not None else None
|
| 604 |
+
)
|
| 605 |
+
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
| 606 |
+
if scale is None:
|
| 607 |
+
scale = k.shape[-1] ** -0.5
|
| 608 |
+
|
| 609 |
+
o = torch.empty_like(v)
|
| 610 |
+
|
| 611 |
+
def grid(meta):
|
| 612 |
+
return (triton.cdiv(V, meta["BV"]), NT, B * H)
|
| 613 |
+
|
| 614 |
+
chunk_fwd_kernel_o[grid](
|
| 615 |
+
q=q,
|
| 616 |
+
k=k,
|
| 617 |
+
v=v,
|
| 618 |
+
h=h,
|
| 619 |
+
g=g,
|
| 620 |
+
g_gamma=g_gamma,
|
| 621 |
+
o=o,
|
| 622 |
+
cu_seqlens=cu_seqlens,
|
| 623 |
+
chunk_indices=chunk_indices,
|
| 624 |
+
scale=scale,
|
| 625 |
+
T=T,
|
| 626 |
+
H=H,
|
| 627 |
+
K=K,
|
| 628 |
+
V=V,
|
| 629 |
+
BT=BT,
|
| 630 |
+
)
|
| 631 |
+
return o
|
| 632 |
+
|
| 633 |
+
|
| 634 |
+
@triton.heuristics(
|
| 635 |
+
{
|
| 636 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 637 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 638 |
+
}
|
| 639 |
+
)
|
| 640 |
+
@triton.autotune(
|
| 641 |
+
configs=gated_delta_rule_autotune_configs(
|
| 642 |
+
[
|
| 643 |
+
triton.Config(
|
| 644 |
+
{"BK": BK, "BV": BV}, num_warps=num_warps, num_stages=num_stages
|
| 645 |
+
)
|
| 646 |
+
for BK in BKV_LIST
|
| 647 |
+
for BV in BKV_LIST
|
| 648 |
+
for num_warps in NUM_WARPS
|
| 649 |
+
for num_stages in [2, 3, 4]
|
| 650 |
+
]
|
| 651 |
+
),
|
| 652 |
+
key=["H", "K", "V", "BT", "IS_VARLEN"],
|
| 653 |
+
**autotune_cache_kwargs,
|
| 654 |
+
)
|
| 655 |
+
@triton.jit(do_not_specialize=["T", "T_flat"])
|
| 656 |
+
def chunk_fwd_kernel_o_opt(
|
| 657 |
+
q,
|
| 658 |
+
k,
|
| 659 |
+
v,
|
| 660 |
+
h,
|
| 661 |
+
g,
|
| 662 |
+
o,
|
| 663 |
+
cu_seqlens,
|
| 664 |
+
chunk_indices,
|
| 665 |
+
scale,
|
| 666 |
+
T,
|
| 667 |
+
T_flat,
|
| 668 |
+
H: tl.constexpr,
|
| 669 |
+
Hg: tl.constexpr,
|
| 670 |
+
K: tl.constexpr,
|
| 671 |
+
V: tl.constexpr,
|
| 672 |
+
BT: tl.constexpr,
|
| 673 |
+
BK: tl.constexpr,
|
| 674 |
+
BV: tl.constexpr,
|
| 675 |
+
USE_G: tl.constexpr,
|
| 676 |
+
IS_VARLEN: tl.constexpr,
|
| 677 |
+
):
|
| 678 |
+
i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 679 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 680 |
+
|
| 681 |
+
if IS_VARLEN:
|
| 682 |
+
i_tg = i_t
|
| 683 |
+
i_n, i_t = (
|
| 684 |
+
tl.load(chunk_indices + i_t * 2).to(tl.int32),
|
| 685 |
+
tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32),
|
| 686 |
+
)
|
| 687 |
+
bos, eos = (
|
| 688 |
+
tl.load(cu_seqlens + i_n).to(tl.int32),
|
| 689 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int32),
|
| 690 |
+
)
|
| 691 |
+
T = eos - bos
|
| 692 |
+
NT = tl.cdiv(T, BT)
|
| 693 |
+
else:
|
| 694 |
+
NT = tl.cdiv(T, BT)
|
| 695 |
+
i_tg = i_b * NT + i_t
|
| 696 |
+
bos = i_b * T
|
| 697 |
+
|
| 698 |
+
q += (bos * Hg + i_h // (H // Hg)) * K
|
| 699 |
+
k += (bos * Hg + i_h // (H // Hg)) * K
|
| 700 |
+
if IS_VARLEN:
|
| 701 |
+
v += ((i_h * T_flat + bos) * V).to(tl.int64)
|
| 702 |
+
o += ((bos * H + i_h) * V).to(tl.int64)
|
| 703 |
+
else:
|
| 704 |
+
v += (((i_b * H + i_h) * T_flat) * V).to(tl.int64)
|
| 705 |
+
o += ((i_b * T * H + i_h) * V).to(tl.int64)
|
| 706 |
+
h += (i_tg * H + i_h).to(tl.int64) * K * V
|
| 707 |
+
|
| 708 |
+
b_o = tl.zeros([BT, BV], dtype=tl.float32)
|
| 709 |
+
b_A = tl.zeros([BT, BT], dtype=tl.float32)
|
| 710 |
+
|
| 711 |
+
for i_k in range(tl.cdiv(K, BK)):
|
| 712 |
+
p_q = tl.make_block_ptr(
|
| 713 |
+
q, (T, K), (Hg * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 714 |
+
)
|
| 715 |
+
p_k = tl.make_block_ptr(
|
| 716 |
+
k, (K, T), (1, Hg * K), (i_k * BK, i_t * BT), (BK, BT), (0, 1)
|
| 717 |
+
)
|
| 718 |
+
p_h = tl.make_block_ptr(
|
| 719 |
+
h, (K, V), (V, 1), (i_k * BK, i_v * BV), (BK, BV), (1, 0)
|
| 720 |
+
)
|
| 721 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 722 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 723 |
+
b_h = tl.load(p_h, boundary_check=(0, 1))
|
| 724 |
+
|
| 725 |
+
b_o = tl.dot(b_q, b_h, acc=b_o)
|
| 726 |
+
b_A = tl.dot(b_q, b_k, acc=b_A)
|
| 727 |
+
|
| 728 |
+
if USE_G:
|
| 729 |
+
g += bos * H + i_h
|
| 730 |
+
p_g = tl.make_block_ptr(g, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 731 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 732 |
+
b_o = b_o * exp(b_g)[:, None]
|
| 733 |
+
b_A = b_A * exp(b_g[:, None] - b_g[None, :])
|
| 734 |
+
|
| 735 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 736 |
+
m_t = o_t < T
|
| 737 |
+
m_A = (o_t[:, None] >= o_t[None, :]) & (m_t[:, None] & m_t)
|
| 738 |
+
b_A = tl.where(m_A, b_A, 0)
|
| 739 |
+
|
| 740 |
+
p_v = tl.make_block_ptr(v, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
|
| 741 |
+
p_o = tl.make_block_ptr(
|
| 742 |
+
o, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 743 |
+
)
|
| 744 |
+
b_v = tl.load(p_v, boundary_check=(0, 1))
|
| 745 |
+
|
| 746 |
+
b_o = b_o * scale + tl.dot(b_A.to(b_v.dtype), b_v) * scale
|
| 747 |
+
tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
def chunk_fwd_o_opt(
|
| 751 |
+
q: torch.Tensor,
|
| 752 |
+
k: torch.Tensor,
|
| 753 |
+
v: torch.Tensor,
|
| 754 |
+
h: torch.Tensor,
|
| 755 |
+
g: torch.Tensor | None = None,
|
| 756 |
+
scale: float | None = None,
|
| 757 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 758 |
+
chunk_size: int = 64,
|
| 759 |
+
) -> torch.Tensor:
|
| 760 |
+
"""
|
| 761 |
+
Optimized output forward with transposed v layout and Hg-aware q/k strides.
|
| 762 |
+
|
| 763 |
+
Args:
|
| 764 |
+
q: [B, T, Hg, K]
|
| 765 |
+
k: [B, T, Hg, K]
|
| 766 |
+
v: [B, H, T, V]
|
| 767 |
+
h: [B, NT, H, K, V]
|
| 768 |
+
g: [B*T, H] FP32
|
| 769 |
+
scale: float
|
| 770 |
+
cu_seqlens: [N+1]
|
| 771 |
+
chunk_size: int
|
| 772 |
+
|
| 773 |
+
Returns:
|
| 774 |
+
o: [B, T, H, V]
|
| 775 |
+
"""
|
| 776 |
+
B, T, Hg, K = q.shape
|
| 777 |
+
H = v.shape[1]
|
| 778 |
+
T_flat = v.shape[2]
|
| 779 |
+
V = v.shape[-1]
|
| 780 |
+
BT = chunk_size
|
| 781 |
+
chunk_indices = (
|
| 782 |
+
prepare_chunk_indices(cu_seqlens, BT) if cu_seqlens is not None else None
|
| 783 |
+
)
|
| 784 |
+
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
| 785 |
+
if scale is None:
|
| 786 |
+
scale = k.shape[-1] ** -0.5
|
| 787 |
+
|
| 788 |
+
o = v.new_empty(B, T, H, V)
|
| 789 |
+
|
| 790 |
+
def grid(meta):
|
| 791 |
+
return (triton.cdiv(V, meta["BV"]), NT, B * H)
|
| 792 |
+
|
| 793 |
+
chunk_fwd_kernel_o_opt[grid](
|
| 794 |
+
q=q,
|
| 795 |
+
k=k,
|
| 796 |
+
v=v,
|
| 797 |
+
h=h,
|
| 798 |
+
g=g,
|
| 799 |
+
o=o,
|
| 800 |
+
cu_seqlens=cu_seqlens,
|
| 801 |
+
chunk_indices=chunk_indices,
|
| 802 |
+
scale=scale,
|
| 803 |
+
T=T,
|
| 804 |
+
T_flat=T_flat,
|
| 805 |
+
H=H,
|
| 806 |
+
Hg=Hg,
|
| 807 |
+
K=K,
|
| 808 |
+
V=V,
|
| 809 |
+
BT=BT,
|
| 810 |
+
)
|
| 811 |
+
return o
|
| 812 |
+
|
| 813 |
+
|
| 814 |
+
# =====================================================================
|
| 815 |
+
# opt_vk variant: h layout [V, K] (transposed from opt's [K, V])
|
| 816 |
+
# All other layouts identical to opt.
|
| 817 |
+
# =====================================================================
|
| 818 |
+
|
| 819 |
+
|
| 820 |
+
@triton.heuristics(
|
| 821 |
+
{
|
| 822 |
+
"USE_G": lambda args: args["g"] is not None,
|
| 823 |
+
"IS_VARLEN": lambda args: args["cu_seqlens"] is not None,
|
| 824 |
+
}
|
| 825 |
+
)
|
| 826 |
+
@triton.autotune(
|
| 827 |
+
configs=gated_delta_rule_autotune_configs(
|
| 828 |
+
[
|
| 829 |
+
triton.Config(
|
| 830 |
+
{"BK": BK, "BV": BV}, num_warps=num_warps, num_stages=num_stages
|
| 831 |
+
)
|
| 832 |
+
for BK in BKV_LIST
|
| 833 |
+
for BV in BKV_LIST
|
| 834 |
+
for num_warps in NUM_WARPS
|
| 835 |
+
for num_stages in [2, 3, 4]
|
| 836 |
+
]
|
| 837 |
+
),
|
| 838 |
+
key=["H", "K", "V", "BT", "IS_VARLEN"],
|
| 839 |
+
**autotune_cache_kwargs,
|
| 840 |
+
)
|
| 841 |
+
@triton.jit(do_not_specialize=["T", "T_flat"])
|
| 842 |
+
def chunk_fwd_kernel_o_opt_vk(
|
| 843 |
+
q,
|
| 844 |
+
k,
|
| 845 |
+
v,
|
| 846 |
+
h,
|
| 847 |
+
g,
|
| 848 |
+
o,
|
| 849 |
+
cu_seqlens,
|
| 850 |
+
chunk_indices,
|
| 851 |
+
scale,
|
| 852 |
+
T,
|
| 853 |
+
T_flat,
|
| 854 |
+
H: tl.constexpr,
|
| 855 |
+
Hg: tl.constexpr,
|
| 856 |
+
K: tl.constexpr,
|
| 857 |
+
V: tl.constexpr,
|
| 858 |
+
BT: tl.constexpr,
|
| 859 |
+
BK: tl.constexpr,
|
| 860 |
+
BV: tl.constexpr,
|
| 861 |
+
USE_G: tl.constexpr,
|
| 862 |
+
IS_VARLEN: tl.constexpr,
|
| 863 |
+
USE_EXP2: tl.constexpr = False,
|
| 864 |
+
):
|
| 865 |
+
i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
|
| 866 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 867 |
+
|
| 868 |
+
if IS_VARLEN:
|
| 869 |
+
i_tg = i_t
|
| 870 |
+
i_n, i_t = (
|
| 871 |
+
tl.load(chunk_indices + i_t * 2).to(tl.int32),
|
| 872 |
+
tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32),
|
| 873 |
+
)
|
| 874 |
+
bos, eos = (
|
| 875 |
+
tl.load(cu_seqlens + i_n).to(tl.int32),
|
| 876 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int32),
|
| 877 |
+
)
|
| 878 |
+
T = eos - bos
|
| 879 |
+
NT = tl.cdiv(T, BT)
|
| 880 |
+
else:
|
| 881 |
+
NT = tl.cdiv(T, BT)
|
| 882 |
+
i_tg = i_b * NT + i_t
|
| 883 |
+
bos = i_b * T
|
| 884 |
+
|
| 885 |
+
q += (bos * Hg + i_h // (H // Hg)) * K
|
| 886 |
+
k += (bos * Hg + i_h // (H // Hg)) * K
|
| 887 |
+
if IS_VARLEN:
|
| 888 |
+
v += ((i_h * T_flat + bos) * V).to(tl.int64)
|
| 889 |
+
o += ((bos * H + i_h) * V).to(tl.int64)
|
| 890 |
+
else:
|
| 891 |
+
v += (((i_b * H + i_h) * T_flat) * V).to(tl.int64)
|
| 892 |
+
o += ((i_b * T * H + i_h) * V).to(tl.int64)
|
| 893 |
+
h += (i_tg * H + i_h).to(tl.int64) * V * K
|
| 894 |
+
|
| 895 |
+
if USE_G:
|
| 896 |
+
if IS_VARLEN:
|
| 897 |
+
g += (i_h * T_flat + bos).to(tl.int64)
|
| 898 |
+
else:
|
| 899 |
+
g += (((i_b * H + i_h) * T_flat)).to(tl.int64)
|
| 900 |
+
|
| 901 |
+
b_o = tl.zeros([BT, BV], dtype=tl.float32)
|
| 902 |
+
b_A = tl.zeros([BT, BT], dtype=tl.float32)
|
| 903 |
+
|
| 904 |
+
for i_k in range(tl.cdiv(K, BK)):
|
| 905 |
+
p_q = tl.make_block_ptr(
|
| 906 |
+
q, (T, K), (Hg * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0)
|
| 907 |
+
)
|
| 908 |
+
p_k = tl.make_block_ptr(
|
| 909 |
+
k, (K, T), (1, Hg * K), (i_k * BK, i_t * BT), (BK, BT), (0, 1)
|
| 910 |
+
)
|
| 911 |
+
p_h = tl.make_block_ptr(
|
| 912 |
+
h, (V, K), (K, 1), (i_v * BV, i_k * BK), (BV, BK), (1, 0)
|
| 913 |
+
)
|
| 914 |
+
b_q = tl.load(p_q, boundary_check=(0, 1))
|
| 915 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 916 |
+
b_h = tl.load(p_h, boundary_check=(0, 1))
|
| 917 |
+
|
| 918 |
+
b_o = tl.dot(b_q, tl.trans(b_h), acc=b_o)
|
| 919 |
+
b_A = tl.dot(b_q, b_k, acc=b_A)
|
| 920 |
+
|
| 921 |
+
if USE_G:
|
| 922 |
+
p_g = tl.make_block_ptr(g, (T,), (1,), (i_t * BT,), (BT,), (0,))
|
| 923 |
+
b_g = tl.load(p_g, boundary_check=(0,))
|
| 924 |
+
if USE_EXP2:
|
| 925 |
+
b_o = b_o * tl.math.exp2(b_g)[:, None]
|
| 926 |
+
b_A = b_A * tl.math.exp2(b_g[:, None] - b_g[None, :])
|
| 927 |
+
else:
|
| 928 |
+
b_o = b_o * exp(b_g)[:, None]
|
| 929 |
+
b_A = b_A * exp(b_g[:, None] - b_g[None, :])
|
| 930 |
+
|
| 931 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 932 |
+
m_t = o_t < T
|
| 933 |
+
m_A = (o_t[:, None] >= o_t[None, :]) & (m_t[:, None] & m_t)
|
| 934 |
+
b_A = tl.where(m_A, b_A, 0)
|
| 935 |
+
|
| 936 |
+
p_v = tl.make_block_ptr(v, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
|
| 937 |
+
p_o = tl.make_block_ptr(
|
| 938 |
+
o, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0)
|
| 939 |
+
)
|
| 940 |
+
b_v = tl.load(p_v, boundary_check=(0, 1))
|
| 941 |
+
|
| 942 |
+
b_o = b_o * scale + tl.dot(b_A.to(b_v.dtype), b_v) * scale
|
| 943 |
+
tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1))
|
| 944 |
+
|
| 945 |
+
|
| 946 |
+
def chunk_fwd_o_opt_vk(
|
| 947 |
+
q: torch.Tensor,
|
| 948 |
+
k: torch.Tensor,
|
| 949 |
+
v: torch.Tensor,
|
| 950 |
+
o: torch.Tensor,
|
| 951 |
+
h: torch.Tensor,
|
| 952 |
+
g: torch.Tensor | None = None,
|
| 953 |
+
scale: float | None = None,
|
| 954 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 955 |
+
chunk_size: int = 64,
|
| 956 |
+
use_exp2: bool = True,
|
| 957 |
+
num_decodes: int = 0,
|
| 958 |
+
num_decode_tokens: int = 0,
|
| 959 |
+
) -> torch.Tensor:
|
| 960 |
+
"""
|
| 961 |
+
Optimized output forward with h layout [V, K].
|
| 962 |
+
|
| 963 |
+
Args:
|
| 964 |
+
q: [B, T, Hg, K]
|
| 965 |
+
k: [B, T, Hg, K]
|
| 966 |
+
v: [B, H, T, V] (token-major from opt_vk)
|
| 967 |
+
h: [B, NT, H, V, K] (h layout [V, K])
|
| 968 |
+
g: [B, H, T] FP32 cumulative gate tensor
|
| 969 |
+
scale: float
|
| 970 |
+
cu_seqlens: [N+1]
|
| 971 |
+
chunk_size: int
|
| 972 |
+
use_exp2: when True, interpret g in log2 space
|
| 973 |
+
|
| 974 |
+
Returns:
|
| 975 |
+
o: [B, T, H, V]
|
| 976 |
+
"""
|
| 977 |
+
B, T, Hg, K = q.shape
|
| 978 |
+
H = v.shape[1]
|
| 979 |
+
T_flat = v.shape[2]
|
| 980 |
+
V = v.shape[-1]
|
| 981 |
+
BT = chunk_size
|
| 982 |
+
# Chunk indices from the ORIGINAL (cache-stable) cu_seqlens + decode ints
|
| 983 |
+
# (cached, no per-forward D2H); the kernel walks pre-sliced prefill data
|
| 984 |
+
# via the rebased cu_seqlens.
|
| 985 |
+
if cu_seqlens is not None:
|
| 986 |
+
chunk_indices = prepare_chunk_indices(
|
| 987 |
+
cu_seqlens, BT, num_decodes, num_decode_tokens
|
| 988 |
+
)
|
| 989 |
+
kernel_cu_seqlens = prepare_rebased_cu_seqlens(
|
| 990 |
+
cu_seqlens, num_decodes, num_decode_tokens
|
| 991 |
+
)
|
| 992 |
+
else:
|
| 993 |
+
chunk_indices = None
|
| 994 |
+
kernel_cu_seqlens = None
|
| 995 |
+
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
| 996 |
+
if scale is None:
|
| 997 |
+
scale = k.shape[-1] ** -0.5
|
| 998 |
+
|
| 999 |
+
# o = v.new_empty(B, T, H, V)
|
| 1000 |
+
|
| 1001 |
+
def grid(meta):
|
| 1002 |
+
return (triton.cdiv(V, meta["BV"]), NT, B * H)
|
| 1003 |
+
|
| 1004 |
+
chunk_fwd_kernel_o_opt_vk[grid](
|
| 1005 |
+
q=q,
|
| 1006 |
+
k=k,
|
| 1007 |
+
v=v,
|
| 1008 |
+
h=h,
|
| 1009 |
+
g=g,
|
| 1010 |
+
o=o,
|
| 1011 |
+
cu_seqlens=kernel_cu_seqlens,
|
| 1012 |
+
chunk_indices=chunk_indices,
|
| 1013 |
+
scale=scale,
|
| 1014 |
+
T=T,
|
| 1015 |
+
T_flat=T_flat,
|
| 1016 |
+
H=H,
|
| 1017 |
+
Hg=Hg,
|
| 1018 |
+
K=K,
|
| 1019 |
+
V=V,
|
| 1020 |
+
BT=BT,
|
| 1021 |
+
USE_EXP2=use_exp2,
|
| 1022 |
+
)
|
| 1023 |
+
return o
|
| 1024 |
+
|
| 1025 |
+
|
| 1026 |
+
def chunk_bwd_dv(
|
| 1027 |
+
q: torch.Tensor,
|
| 1028 |
+
k: torch.Tensor,
|
| 1029 |
+
do: torch.Tensor,
|
| 1030 |
+
dh: torch.Tensor,
|
| 1031 |
+
g: torch.Tensor | None = None,
|
| 1032 |
+
g_gamma: torch.Tensor | None = None,
|
| 1033 |
+
scale: float | None = None,
|
| 1034 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 1035 |
+
chunk_size: int = 64,
|
| 1036 |
+
) -> torch.Tensor:
|
| 1037 |
+
B, T, H, K, V = *k.shape, do.shape[-1]
|
| 1038 |
+
BT = chunk_size
|
| 1039 |
+
chunk_indices = (
|
| 1040 |
+
prepare_chunk_indices(cu_seqlens, BT) if cu_seqlens is not None else None
|
| 1041 |
+
)
|
| 1042 |
+
# H100 can have larger block size
|
| 1043 |
+
if check_shared_mem("hopper", k.device.index):
|
| 1044 |
+
CONST_TILING = 128
|
| 1045 |
+
elif check_shared_mem():
|
| 1046 |
+
CONST_TILING = 64
|
| 1047 |
+
else:
|
| 1048 |
+
CONST_TILING = 32
|
| 1049 |
+
BK = min(max(triton.next_power_of_2(K), 16), CONST_TILING)
|
| 1050 |
+
BV = min(max(triton.next_power_of_2(V), 16), CONST_TILING)
|
| 1051 |
+
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
| 1052 |
+
NV = triton.cdiv(V, BV)
|
| 1053 |
+
if scale is None:
|
| 1054 |
+
scale = k.shape[-1] ** -0.5
|
| 1055 |
+
|
| 1056 |
+
dv = torch.empty_like(do)
|
| 1057 |
+
grid = (NV, NT, B * H)
|
| 1058 |
+
chunk_bwd_kernel_dv[grid](
|
| 1059 |
+
q=q,
|
| 1060 |
+
k=k,
|
| 1061 |
+
g=g,
|
| 1062 |
+
g_gamma=g_gamma,
|
| 1063 |
+
do=do,
|
| 1064 |
+
dv=dv,
|
| 1065 |
+
dh=dh,
|
| 1066 |
+
cu_seqlens=cu_seqlens,
|
| 1067 |
+
chunk_indices=chunk_indices,
|
| 1068 |
+
scale=scale,
|
| 1069 |
+
T=T,
|
| 1070 |
+
H=H,
|
| 1071 |
+
K=K,
|
| 1072 |
+
V=V,
|
| 1073 |
+
BT=BT,
|
| 1074 |
+
BK=BK,
|
| 1075 |
+
BV=BV,
|
| 1076 |
+
)
|
| 1077 |
+
return dv
|
| 1078 |
+
|
| 1079 |
+
|
| 1080 |
+
def chunk_bwd_dv_local(
|
| 1081 |
+
q: torch.Tensor,
|
| 1082 |
+
k: torch.Tensor,
|
| 1083 |
+
do: torch.Tensor,
|
| 1084 |
+
g: torch.Tensor | None = None,
|
| 1085 |
+
g_gamma: torch.Tensor | None = None,
|
| 1086 |
+
A: torch.Tensor | None = None,
|
| 1087 |
+
scale: float = None,
|
| 1088 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 1089 |
+
chunk_size: int = 64,
|
| 1090 |
+
chunk_indices: torch.LongTensor | None = None,
|
| 1091 |
+
) -> torch.Tensor:
|
| 1092 |
+
B, T, H, K, V = *k.shape, do.shape[-1]
|
| 1093 |
+
BT = chunk_size
|
| 1094 |
+
if chunk_indices is None and cu_seqlens is not None:
|
| 1095 |
+
chunk_indices = prepare_chunk_indices(cu_seqlens, BT)
|
| 1096 |
+
# H100 can have larger block size
|
| 1097 |
+
if check_shared_mem("hopper", k.device.index):
|
| 1098 |
+
CONST_TILING = 128
|
| 1099 |
+
elif check_shared_mem():
|
| 1100 |
+
CONST_TILING = 64
|
| 1101 |
+
else:
|
| 1102 |
+
CONST_TILING = 32
|
| 1103 |
+
BK = min(max(triton.next_power_of_2(K), 16), CONST_TILING)
|
| 1104 |
+
BV = min(max(triton.next_power_of_2(V), 16), CONST_TILING)
|
| 1105 |
+
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
| 1106 |
+
|
| 1107 |
+
dv = torch.empty_like(do)
|
| 1108 |
+
grid = (NT, B * H)
|
| 1109 |
+
chunk_bwd_kernel_dv_local[grid](
|
| 1110 |
+
q=q,
|
| 1111 |
+
k=k,
|
| 1112 |
+
g=g,
|
| 1113 |
+
g_gamma=g_gamma,
|
| 1114 |
+
A=A,
|
| 1115 |
+
do=do,
|
| 1116 |
+
dv=dv,
|
| 1117 |
+
cu_seqlens=cu_seqlens,
|
| 1118 |
+
chunk_indices=chunk_indices,
|
| 1119 |
+
scale=scale,
|
| 1120 |
+
T=T,
|
| 1121 |
+
H=H,
|
| 1122 |
+
K=K,
|
| 1123 |
+
V=V,
|
| 1124 |
+
BT=BT,
|
| 1125 |
+
BK=BK,
|
| 1126 |
+
BV=BV,
|
| 1127 |
+
)
|
| 1128 |
+
return dv
|
| 1129 |
+
|
| 1130 |
+
|
| 1131 |
+
def chunk_bwd_dqkwg(
|
| 1132 |
+
q: torch.Tensor,
|
| 1133 |
+
k: torch.Tensor,
|
| 1134 |
+
v: torch.Tensor,
|
| 1135 |
+
do: torch.Tensor,
|
| 1136 |
+
h: torch.Tensor,
|
| 1137 |
+
dh: torch.Tensor,
|
| 1138 |
+
w: torch.Tensor | None = None,
|
| 1139 |
+
g: torch.Tensor | None = None,
|
| 1140 |
+
g_gamma: torch.Tensor | None = None,
|
| 1141 |
+
dv: torch.Tensor | None = None,
|
| 1142 |
+
scale: float | None = None,
|
| 1143 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 1144 |
+
chunk_size: int = 64,
|
| 1145 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 1146 |
+
|
| 1147 |
+
B, T, H, K, V = *k.shape, v.shape[-1]
|
| 1148 |
+
BT = chunk_size
|
| 1149 |
+
chunk_indices = (
|
| 1150 |
+
prepare_chunk_indices(cu_seqlens, BT) if cu_seqlens is not None else None
|
| 1151 |
+
)
|
| 1152 |
+
NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices)
|
| 1153 |
+
|
| 1154 |
+
CONST_TILING = 64 if check_shared_mem() else 32
|
| 1155 |
+
BK = min(max(triton.next_power_of_2(K), 16), CONST_TILING)
|
| 1156 |
+
BV = min(max(triton.next_power_of_2(V), 16), CONST_TILING)
|
| 1157 |
+
NK = triton.cdiv(K, BK)
|
| 1158 |
+
dq = torch.empty_like(q)
|
| 1159 |
+
dk = torch.empty_like(k)
|
| 1160 |
+
dg = (
|
| 1161 |
+
torch.empty(NK, *g.shape, dtype=torch.float32, device=g.device)
|
| 1162 |
+
if g is not None
|
| 1163 |
+
else None
|
| 1164 |
+
)
|
| 1165 |
+
dw = torch.empty_like(w) if w is not None else None
|
| 1166 |
+
|
| 1167 |
+
grid = (NK, NT, B * H)
|
| 1168 |
+
chunk_bwd_kernel_dqkwg[grid](
|
| 1169 |
+
q=q,
|
| 1170 |
+
k=k,
|
| 1171 |
+
v=v,
|
| 1172 |
+
g=g,
|
| 1173 |
+
g_gamma=g_gamma,
|
| 1174 |
+
h=h,
|
| 1175 |
+
do=do,
|
| 1176 |
+
dh=dh,
|
| 1177 |
+
dw=dw,
|
| 1178 |
+
dq=dq,
|
| 1179 |
+
dk=dk,
|
| 1180 |
+
dv=dv,
|
| 1181 |
+
dg=dg,
|
| 1182 |
+
cu_seqlens=cu_seqlens,
|
| 1183 |
+
chunk_indices=chunk_indices,
|
| 1184 |
+
scale=scale,
|
| 1185 |
+
B=B,
|
| 1186 |
+
T=T,
|
| 1187 |
+
H=H,
|
| 1188 |
+
K=K,
|
| 1189 |
+
V=V,
|
| 1190 |
+
BT=BT,
|
| 1191 |
+
BK=BK,
|
| 1192 |
+
BV=BV,
|
| 1193 |
+
)
|
| 1194 |
+
|
| 1195 |
+
if dg is not None:
|
| 1196 |
+
dg = dg.sum(0)
|
| 1197 |
+
return dq, dk, dw, dg
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/fused_cumsum_kkt.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
from ..gated_delta_rule_utils import (
|
| 6 |
+
RCP_LN2,
|
| 7 |
+
IS_AMD,
|
| 8 |
+
autotune_cache_kwargs,
|
| 9 |
+
gated_delta_rule_autotune_configs,
|
| 10 |
+
)
|
| 11 |
+
from ..utils import prepare_chunk_indices, prepare_rebased_cu_seqlens
|
| 12 |
+
from ..utils.op import exp
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@triton.jit
|
| 16 |
+
def safe_exp(x):
|
| 17 |
+
return tl.exp(tl.where(x <= 0, x, float("-inf")))
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@triton.heuristics({"IS_VARLEN": lambda args: args["cu_seqlens"] is not None})
|
| 21 |
+
@triton.jit(do_not_specialize=["T"])
|
| 22 |
+
def _fused_cumsum_kkt_kernel(
|
| 23 |
+
g_ptr,
|
| 24 |
+
k_ptr,
|
| 25 |
+
beta_ptr,
|
| 26 |
+
g_cumsum_ptr,
|
| 27 |
+
A_ptr,
|
| 28 |
+
cu_seqlens,
|
| 29 |
+
chunk_indices,
|
| 30 |
+
T,
|
| 31 |
+
H: tl.constexpr,
|
| 32 |
+
Hg: tl.constexpr,
|
| 33 |
+
K: tl.constexpr,
|
| 34 |
+
BT: tl.constexpr,
|
| 35 |
+
IS_VARLEN: tl.constexpr,
|
| 36 |
+
):
|
| 37 |
+
i_t, i_bh = tl.program_id(0), tl.program_id(1)
|
| 38 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 39 |
+
|
| 40 |
+
if IS_VARLEN:
|
| 41 |
+
i_n = tl.load(chunk_indices + i_t * 2).to(tl.int32)
|
| 42 |
+
i_t_local = tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32)
|
| 43 |
+
bos = tl.load(cu_seqlens + i_n).to(tl.int32)
|
| 44 |
+
eos = tl.load(cu_seqlens + i_n + 1).to(tl.int32)
|
| 45 |
+
T_seq = eos - bos
|
| 46 |
+
i_t = i_t_local
|
| 47 |
+
else:
|
| 48 |
+
bos = i_b * T
|
| 49 |
+
T_seq = T
|
| 50 |
+
|
| 51 |
+
o_t = tl.arange(0, BT)
|
| 52 |
+
|
| 53 |
+
p_g = tl.make_block_ptr(
|
| 54 |
+
g_ptr + bos * H + i_h, (T_seq,), (H,), (i_t * BT,), (BT,), (0,)
|
| 55 |
+
)
|
| 56 |
+
b_g = tl.load(p_g, boundary_check=(0,)).to(tl.float32)
|
| 57 |
+
b_g_cumsum = tl.cumsum(b_g, axis=0)
|
| 58 |
+
p_g_out = tl.make_block_ptr(
|
| 59 |
+
g_cumsum_ptr + bos * H + i_h, (T_seq,), (H,), (i_t * BT,), (BT,), (0,)
|
| 60 |
+
)
|
| 61 |
+
tl.store(p_g_out, b_g_cumsum.to(p_g_out.dtype.element_ty), boundary_check=(0,))
|
| 62 |
+
|
| 63 |
+
p_beta = tl.make_block_ptr(
|
| 64 |
+
beta_ptr + bos * H + i_h, (T_seq,), (H,), (i_t * BT,), (BT,), (0,)
|
| 65 |
+
)
|
| 66 |
+
b_beta = tl.load(p_beta, boundary_check=(0,)).to(tl.float32)
|
| 67 |
+
|
| 68 |
+
p_k = tl.make_block_ptr(
|
| 69 |
+
k_ptr + (bos * Hg + i_h // (H // Hg)) * K,
|
| 70 |
+
(T_seq, K),
|
| 71 |
+
(Hg * K, 1),
|
| 72 |
+
(i_t * BT, 0),
|
| 73 |
+
(BT, K),
|
| 74 |
+
(1, 0),
|
| 75 |
+
)
|
| 76 |
+
b_k = tl.load(p_k, boundary_check=(0, 1)).to(tl.float32)
|
| 77 |
+
|
| 78 |
+
b_A = tl.dot(b_k, tl.trans(b_k))
|
| 79 |
+
b_g_diff = b_g_cumsum[:, None] - b_g_cumsum[None, :]
|
| 80 |
+
b_A = b_A * safe_exp(b_g_diff) * b_beta[:, None]
|
| 81 |
+
b_A = tl.where(o_t[:, None] > o_t[None, :], b_A, 0.0)
|
| 82 |
+
|
| 83 |
+
p_A = tl.make_block_ptr(
|
| 84 |
+
A_ptr + (bos * H + i_h) * BT,
|
| 85 |
+
(T_seq, BT),
|
| 86 |
+
(BT * H, 1),
|
| 87 |
+
(i_t * BT, 0),
|
| 88 |
+
(BT, BT),
|
| 89 |
+
(1, 0),
|
| 90 |
+
)
|
| 91 |
+
tl.store(p_A, b_A.to(A_ptr.dtype.element_ty), boundary_check=(0, 1))
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def fused_cumsum_kkt(
|
| 95 |
+
g: torch.Tensor,
|
| 96 |
+
k: torch.Tensor,
|
| 97 |
+
beta: torch.Tensor,
|
| 98 |
+
chunk_size: int = 64,
|
| 99 |
+
cu_seqlens: torch.Tensor | None = None,
|
| 100 |
+
):
|
| 101 |
+
"""
|
| 102 |
+
Fused cumsum + KKT.
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
g: [B, T, H]
|
| 106 |
+
k: [B, T, Hg, K]
|
| 107 |
+
beta: [B, T, H]
|
| 108 |
+
|
| 109 |
+
Returns:
|
| 110 |
+
g_cumsum: [B, H, T]
|
| 111 |
+
A: [B, T, H, chunk_size], strictly lower triangular
|
| 112 |
+
"""
|
| 113 |
+
B, T, H = g.shape
|
| 114 |
+
Hg, K = k.shape[2], k.shape[3]
|
| 115 |
+
|
| 116 |
+
if cu_seqlens is not None:
|
| 117 |
+
chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size)
|
| 118 |
+
NT = len(chunk_indices)
|
| 119 |
+
else:
|
| 120 |
+
chunk_indices = None
|
| 121 |
+
NT = triton.cdiv(T, chunk_size)
|
| 122 |
+
|
| 123 |
+
g_cumsum = torch.empty(B, T, H, device=g.device, dtype=torch.float32)
|
| 124 |
+
A = torch.empty(B, T, H, chunk_size, device=k.device, dtype=torch.float32)
|
| 125 |
+
|
| 126 |
+
_fused_cumsum_kkt_kernel[(NT, B * H)](
|
| 127 |
+
g,
|
| 128 |
+
k,
|
| 129 |
+
beta,
|
| 130 |
+
g_cumsum,
|
| 131 |
+
A,
|
| 132 |
+
cu_seqlens,
|
| 133 |
+
chunk_indices,
|
| 134 |
+
T,
|
| 135 |
+
H,
|
| 136 |
+
Hg,
|
| 137 |
+
K,
|
| 138 |
+
chunk_size,
|
| 139 |
+
num_warps=4,
|
| 140 |
+
num_stages=3,
|
| 141 |
+
)
|
| 142 |
+
return g_cumsum, A
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
if IS_AMD:
|
| 146 |
+
_CUMSUM_KKT_CONFIGS = [
|
| 147 |
+
triton.Config({"BK": 32}, num_warps=4, num_stages=2),
|
| 148 |
+
triton.Config({"BK": 32}, num_warps=2, num_stages=2),
|
| 149 |
+
triton.Config({"BK": 32}, num_warps=8, num_stages=2),
|
| 150 |
+
triton.Config({"BK": 32}, num_warps=4, num_stages=3),
|
| 151 |
+
triton.Config({"BK": 32}, num_warps=2, num_stages=3),
|
| 152 |
+
triton.Config({"BK": 64}, num_warps=4, num_stages=2),
|
| 153 |
+
]
|
| 154 |
+
else:
|
| 155 |
+
_CUMSUM_KKT_CONFIGS = [
|
| 156 |
+
triton.Config({"BK": BK}, num_warps=nw, num_stages=ns)
|
| 157 |
+
for BK in [32, 64]
|
| 158 |
+
for nw in [2, 4]
|
| 159 |
+
for ns in ([2, 3] if IS_AMD else [2, 3, 4])
|
| 160 |
+
]
|
| 161 |
+
|
| 162 |
+
_CUMSUM_KKT_DEFAULT_CONFIG = triton.Config({"BK": 32}, num_warps=4, num_stages=2)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
@triton.heuristics({"IS_VARLEN": lambda args: args["cu_seqlens"] is not None})
|
| 166 |
+
@triton.autotune(
|
| 167 |
+
configs=gated_delta_rule_autotune_configs(
|
| 168 |
+
_CUMSUM_KKT_CONFIGS,
|
| 169 |
+
default_config=_CUMSUM_KKT_DEFAULT_CONFIG,
|
| 170 |
+
),
|
| 171 |
+
key=["H", "K", "BT", "IS_VARLEN"],
|
| 172 |
+
**autotune_cache_kwargs,
|
| 173 |
+
)
|
| 174 |
+
@triton.jit(do_not_specialize=["T"])
|
| 175 |
+
def fused_chunk_local_cumsum_scaled_dot_kkt_fwd_kernel(
|
| 176 |
+
g,
|
| 177 |
+
k,
|
| 178 |
+
beta,
|
| 179 |
+
g_cumsum_out,
|
| 180 |
+
A_out,
|
| 181 |
+
cu_seqlens,
|
| 182 |
+
chunk_indices,
|
| 183 |
+
T,
|
| 184 |
+
H: tl.constexpr,
|
| 185 |
+
Hg: tl.constexpr,
|
| 186 |
+
K: tl.constexpr,
|
| 187 |
+
BT: tl.constexpr,
|
| 188 |
+
BK: tl.constexpr,
|
| 189 |
+
IS_VARLEN: tl.constexpr,
|
| 190 |
+
USE_EXP2: tl.constexpr = False,
|
| 191 |
+
G_SCALE: tl.constexpr = 1.0,
|
| 192 |
+
):
|
| 193 |
+
i_t, i_bh = tl.program_id(0), tl.program_id(1)
|
| 194 |
+
i_b, i_h = i_bh // H, i_bh % H
|
| 195 |
+
T_flat = T
|
| 196 |
+
if IS_VARLEN:
|
| 197 |
+
i_n, i_t = (
|
| 198 |
+
tl.load(chunk_indices + i_t * 2).to(tl.int32),
|
| 199 |
+
tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32),
|
| 200 |
+
)
|
| 201 |
+
bos, eos = (
|
| 202 |
+
tl.load(cu_seqlens + i_n).to(tl.int32),
|
| 203 |
+
tl.load(cu_seqlens + i_n + 1).to(tl.int32),
|
| 204 |
+
)
|
| 205 |
+
T = eos - bos
|
| 206 |
+
else:
|
| 207 |
+
bos = i_b * T
|
| 208 |
+
|
| 209 |
+
o_t = i_t * BT + tl.arange(0, BT)
|
| 210 |
+
m_t = o_t < T
|
| 211 |
+
|
| 212 |
+
p_g = tl.make_block_ptr(g + bos * H + i_h, (T,), (H,), (i_t * BT,), (BT,), (0,))
|
| 213 |
+
b_g = tl.load(p_g, boundary_check=(0,)).to(tl.float32)
|
| 214 |
+
b_g_cumsum = tl.cumsum(b_g, axis=0)
|
| 215 |
+
# Store g_cumsum in log2 space when downstream kernels consume it with exp2:
|
| 216 |
+
# exp2(x * RCP_LN2) == exp(x), keeping results identical. The scale arrives
|
| 217 |
+
# as the constexpr G_SCALE (RCP_LN2 when use_exp2 else 1.0) so the kernel
|
| 218 |
+
# never reads a module-level global; cumsum's linearity makes scaling before
|
| 219 |
+
# or after the cumsum equivalent.
|
| 220 |
+
if G_SCALE != 1.0:
|
| 221 |
+
b_g_cumsum = b_g_cumsum * G_SCALE
|
| 222 |
+
|
| 223 |
+
# g_cumsum is stored head-major [B, H, T] (stride 1 along T) so the
|
| 224 |
+
# downstream solve/recompute, hidden-state and output kernels can read it
|
| 225 |
+
# contiguously per (batch, head).
|
| 226 |
+
if IS_VARLEN:
|
| 227 |
+
g_out_base = g_cumsum_out + i_h * T_flat + bos
|
| 228 |
+
else:
|
| 229 |
+
g_out_base = g_cumsum_out + (i_b * H + i_h) * T_flat
|
| 230 |
+
p_go = tl.make_block_ptr(g_out_base, (T,), (1,), (i_t * BT,), (BT,), (0,))
|
| 231 |
+
tl.store(p_go, b_g_cumsum.to(p_go.dtype.element_ty), boundary_check=(0,))
|
| 232 |
+
|
| 233 |
+
p_beta = tl.make_block_ptr(
|
| 234 |
+
beta + bos * H + i_h, (T,), (H,), (i_t * BT,), (BT,), (0,)
|
| 235 |
+
)
|
| 236 |
+
b_beta = tl.load(p_beta, boundary_check=(0,))
|
| 237 |
+
|
| 238 |
+
b_A = tl.zeros([BT, BT], dtype=tl.float32)
|
| 239 |
+
for i_k in range(tl.cdiv(K, BK)):
|
| 240 |
+
p_k = tl.make_block_ptr(
|
| 241 |
+
k + (bos * Hg + i_h // (H // Hg)) * K,
|
| 242 |
+
(T, K),
|
| 243 |
+
(Hg * K, 1),
|
| 244 |
+
(i_t * BT, i_k * BK),
|
| 245 |
+
(BT, BK),
|
| 246 |
+
(1, 0),
|
| 247 |
+
)
|
| 248 |
+
b_k = tl.load(p_k, boundary_check=(0, 1))
|
| 249 |
+
b_kb = b_k * b_beta[:, None]
|
| 250 |
+
b_A = tl.dot(b_kb.to(b_k.dtype), tl.trans(b_k), acc=b_A)
|
| 251 |
+
|
| 252 |
+
b_g_diff = b_g_cumsum[:, None] - b_g_cumsum[None, :]
|
| 253 |
+
m_A = (o_t[:, None] > o_t[None, :]) & (m_t[:, None] & m_t)
|
| 254 |
+
b_gate = tl.math.exp2(b_g_diff) if USE_EXP2 else exp(b_g_diff)
|
| 255 |
+
b_A = tl.where(m_A, b_A * b_gate, 0.0)
|
| 256 |
+
|
| 257 |
+
p_A = tl.make_block_ptr(
|
| 258 |
+
A_out + (bos * H + i_h) * BT,
|
| 259 |
+
(T, BT),
|
| 260 |
+
(BT * H, 1),
|
| 261 |
+
(i_t * BT, 0),
|
| 262 |
+
(BT, BT),
|
| 263 |
+
(1, 0),
|
| 264 |
+
)
|
| 265 |
+
tl.store(p_A, b_A.to(A_out.dtype.element_ty), boundary_check=(0, 1))
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def fused_chunk_local_cumsum_scaled_dot_kkt_fwd(
|
| 269 |
+
k: torch.Tensor,
|
| 270 |
+
beta: torch.Tensor,
|
| 271 |
+
g: torch.Tensor,
|
| 272 |
+
cu_seqlens: torch.LongTensor | None = None,
|
| 273 |
+
chunk_size: int = 64,
|
| 274 |
+
g_output_dtype: torch.dtype = torch.float32,
|
| 275 |
+
A_output_dtype: torch.dtype = torch.float32,
|
| 276 |
+
use_exp2: bool = True,
|
| 277 |
+
num_decodes: int = 0,
|
| 278 |
+
num_decode_tokens: int = 0,
|
| 279 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 280 |
+
"""
|
| 281 |
+
Fused cumsum + scaled dot KKT (optimized, with autotuning).
|
| 282 |
+
|
| 283 |
+
Args:
|
| 284 |
+
k: [B, T, Hg, K]
|
| 285 |
+
beta: [B, T, H]
|
| 286 |
+
g: [B, T, H], raw forget gate increments
|
| 287 |
+
cu_seqlens: [N+1]
|
| 288 |
+
chunk_size: int (must be 64)
|
| 289 |
+
g_output_dtype: dtype for g_cumsum (default fp32)
|
| 290 |
+
A_output_dtype: dtype for A_raw (default fp32)
|
| 291 |
+
use_exp2: when True, store g_cumsum in log2 space (scaled by RCP_LN2)
|
| 292 |
+
so downstream kernels can use exp2; A_raw is unaffected.
|
| 293 |
+
|
| 294 |
+
Returns:
|
| 295 |
+
g_cumsum: [B, H, T], head-major
|
| 296 |
+
A_raw: [B, T, H, 64]
|
| 297 |
+
"""
|
| 298 |
+
B, T, Hg, K = k.shape
|
| 299 |
+
H = beta.shape[-1]
|
| 300 |
+
BT = chunk_size
|
| 301 |
+
|
| 302 |
+
# Pass the ORIGINAL (cache-stable) cu_seqlens to prepare_chunk_indices
|
| 303 |
+
# together with num_decodes/num_decode_tokens, so the chunk-index build
|
| 304 |
+
# caches on the stable tensor identity and never re-fires the .tolist()
|
| 305 |
+
# D2H across forward calls. The kernel walks the pre-sliced prefill data
|
| 306 |
+
# via the rebased cu_seqlens.
|
| 307 |
+
if cu_seqlens is not None:
|
| 308 |
+
chunk_indices = prepare_chunk_indices(
|
| 309 |
+
cu_seqlens, BT, num_decodes, num_decode_tokens
|
| 310 |
+
)
|
| 311 |
+
kernel_cu_seqlens = prepare_rebased_cu_seqlens(
|
| 312 |
+
cu_seqlens, num_decodes, num_decode_tokens
|
| 313 |
+
)
|
| 314 |
+
NT = len(chunk_indices)
|
| 315 |
+
else:
|
| 316 |
+
chunk_indices = None
|
| 317 |
+
kernel_cu_seqlens = None
|
| 318 |
+
NT = triton.cdiv(T, BT)
|
| 319 |
+
|
| 320 |
+
g_cumsum_out = torch.empty(B, H, T, device=g.device, dtype=g_output_dtype)
|
| 321 |
+
A_out = torch.empty(B, T, H, BT, device=k.device, dtype=A_output_dtype)
|
| 322 |
+
|
| 323 |
+
fused_chunk_local_cumsum_scaled_dot_kkt_fwd_kernel[(NT, B * H)](
|
| 324 |
+
g,
|
| 325 |
+
k,
|
| 326 |
+
beta,
|
| 327 |
+
g_cumsum_out,
|
| 328 |
+
A_out,
|
| 329 |
+
kernel_cu_seqlens,
|
| 330 |
+
chunk_indices,
|
| 331 |
+
T=T,
|
| 332 |
+
H=H,
|
| 333 |
+
Hg=Hg,
|
| 334 |
+
K=K,
|
| 335 |
+
BT=BT,
|
| 336 |
+
USE_EXP2=use_exp2,
|
| 337 |
+
G_SCALE=RCP_LN2 if use_exp2 else 1.0,
|
| 338 |
+
)
|
| 339 |
+
return g_cumsum_out, A_out
|
build/torch-rocm/_triton_kernels/gated_delta_rule/prefill/fused_gdn_gating_prefill.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Fused sigmoid + gdn_gating kernel for prefill."""
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@triton.jit
|
| 9 |
+
def fused_gdn_gating_sigmoid_kernel(
|
| 10 |
+
g_ptr,
|
| 11 |
+
beta_ptr,
|
| 12 |
+
A_log_ptr,
|
| 13 |
+
a_ptr,
|
| 14 |
+
b_ptr,
|
| 15 |
+
dt_bias_ptr,
|
| 16 |
+
NUM_HEADS: tl.constexpr,
|
| 17 |
+
softplus_beta: tl.constexpr,
|
| 18 |
+
softplus_threshold: tl.constexpr,
|
| 19 |
+
BLK_HEADS: tl.constexpr,
|
| 20 |
+
):
|
| 21 |
+
"""Fused: g = -exp(A_log) * softplus(a + dt_bias), beta = sigmoid(b)."""
|
| 22 |
+
i_s = tl.program_id(0)
|
| 23 |
+
|
| 24 |
+
head_off = tl.arange(0, BLK_HEADS)
|
| 25 |
+
mask = head_off < NUM_HEADS
|
| 26 |
+
off = i_s * NUM_HEADS + head_off
|
| 27 |
+
|
| 28 |
+
blk_A_log = tl.load(A_log_ptr + head_off, mask=mask)
|
| 29 |
+
blk_dt_bias = tl.load(dt_bias_ptr + head_off, mask=mask)
|
| 30 |
+
blk_a = tl.load(a_ptr + off, mask=mask)
|
| 31 |
+
blk_b = tl.load(b_ptr + off, mask=mask)
|
| 32 |
+
|
| 33 |
+
# g = -exp(A_log) * softplus(a + dt_bias)
|
| 34 |
+
x = blk_a.to(tl.float32) + blk_dt_bias.to(tl.float32)
|
| 35 |
+
beta_x = softplus_beta * x
|
| 36 |
+
softplus_x = tl.where(
|
| 37 |
+
beta_x <= softplus_threshold,
|
| 38 |
+
(1.0 / softplus_beta) * tl.log(1.0 + tl.exp(beta_x)),
|
| 39 |
+
x,
|
| 40 |
+
)
|
| 41 |
+
blk_g = -tl.exp(blk_A_log.to(tl.float32)) * softplus_x
|
| 42 |
+
|
| 43 |
+
# beta = sigmoid(b)
|
| 44 |
+
blk_beta = 1.0 / (1.0 + tl.exp(-blk_b.to(tl.float32)))
|
| 45 |
+
|
| 46 |
+
tl.store(g_ptr + off, blk_g.to(g_ptr.dtype.element_ty), mask=mask)
|
| 47 |
+
tl.store(beta_ptr + off, blk_beta.to(beta_ptr.dtype.element_ty), mask=mask)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def fused_gdn_gating_and_sigmoid(
|
| 51 |
+
A_log: torch.Tensor,
|
| 52 |
+
a: torch.Tensor,
|
| 53 |
+
b: torch.Tensor,
|
| 54 |
+
dt_bias: torch.Tensor,
|
| 55 |
+
softplus_beta: float = 1.0,
|
| 56 |
+
softplus_threshold: float = 20.0,
|
| 57 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 58 |
+
"""Fused g and beta computation in single kernel."""
|
| 59 |
+
seq_len, num_heads = a.shape
|
| 60 |
+
g = torch.empty_like(a, dtype=torch.float32)
|
| 61 |
+
beta = torch.empty_like(b, dtype=torch.float32)
|
| 62 |
+
|
| 63 |
+
BLK_HEADS = triton.next_power_of_2(num_heads)
|
| 64 |
+
grid = (seq_len,)
|
| 65 |
+
|
| 66 |
+
fused_gdn_gating_sigmoid_kernel[grid](
|
| 67 |
+
g,
|
| 68 |
+
beta,
|
| 69 |
+
A_log,
|
| 70 |
+
a,
|
| 71 |
+
b,
|
| 72 |
+
dt_bias,
|
| 73 |
+
num_heads,
|
| 74 |
+
softplus_beta,
|
| 75 |
+
softplus_threshold,
|
| 76 |
+
BLK_HEADS,
|
| 77 |
+
num_warps=2,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
return g, beta
|