Initial kernel bundle upload: selective-update
Browse files- .gitattributes +2 -34
- .gitignore +2 -0
- CARD.md +44 -9
- build.toml +13 -0
- example.py +42 -0
- flake.lock +117 -0
- flake.nix +11 -0
- selective_update_cuda/selective_update.cu +33 -0
- tests/__init__.py +0 -0
- tests/test_selective_update.py +21 -0
- torch-ext/selective_update/__init__.py +16 -0
- torch-ext/selective_update/_autotune_configs.py +101 -0
- torch-ext/selective_update/functional.py +165 -0
- torch-ext/selective_update/triton_kernels.py +127 -0
- torch-ext/torch_binding.cpp +19 -0
- torch-ext/torch_binding.h +5 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,3 @@
|
|
| 1 |
-
*.
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
*.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
build
|
| 2 |
+
*pyc
|
CARD.md
CHANGED
|
@@ -1,26 +1,61 @@
|
|
| 1 |
---
|
| 2 |
library_name: kernels
|
| 3 |
-
license:
|
| 4 |
-
---
|
| 5 |
|
| 6 |
-
This is the repository card of
|
| 7 |
|
| 8 |
## How to use
|
|
|
|
| 9 |
|
| 10 |
```python
|
| 11 |
# make sure `kernels` is installed: `pip install -U kernels`
|
| 12 |
from kernels import get_kernel
|
| 13 |
|
| 14 |
-
kernel_module = get_kernel("
|
| 15 |
-
|
| 16 |
|
| 17 |
-
|
| 18 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
## Available functions
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
## Benchmarks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
library_name: kernels
|
| 3 |
+
{% if license %}license: {{ license }}
|
| 4 |
+
{% endif %}---
|
| 5 |
|
| 6 |
+
This is the repository card of {{ repo_id }} that has been pushed on the Hub. It was built to be used with the [`kernels` library](https://github.com/huggingface/kernels). This card was automatically generated.
|
| 7 |
|
| 8 |
## How to use
|
| 9 |
+
{% if functions %}
|
| 10 |
|
| 11 |
```python
|
| 12 |
# make sure `kernels` is installed: `pip install -U kernels`
|
| 13 |
from kernels import get_kernel
|
| 14 |
|
| 15 |
+
kernel_module = get_kernel("{{ repo_id }}", version={{ version }})
|
| 16 |
+
{{ functions[0] }} = kernel_module.{{ functions[0] }}
|
| 17 |
|
| 18 |
+
{{ functions[0] }}(...)
|
| 19 |
```
|
| 20 |
+
{% else %}
|
| 21 |
+
|
| 22 |
+
Usage example not available.
|
| 23 |
+
{% endif %}
|
| 24 |
|
| 25 |
## Available functions
|
| 26 |
+
{% if functions %}
|
| 27 |
+
{% for func in functions %}
|
| 28 |
+
- `{{ func }}`
|
| 29 |
+
{% endfor %}
|
| 30 |
+
{% else %}
|
| 31 |
+
|
| 32 |
+
Function list not available.
|
| 33 |
+
{% endif %}
|
| 34 |
+
{% if layers %}
|
| 35 |
+
|
| 36 |
+
## Available layers
|
| 37 |
+
{% for layer in layers %}
|
| 38 |
+
- `{{ layer }}`
|
| 39 |
+
{% endfor %}
|
| 40 |
+
{% endif %}
|
| 41 |
|
| 42 |
## Benchmarks
|
| 43 |
+
{% if has_benchmark %}
|
| 44 |
+
|
| 45 |
+
Benchmarking script is available for this kernel. Run `kernels benchmark {{ repo_id }} --version {{ version }}`.
|
| 46 |
+
{% else %}
|
| 47 |
+
|
| 48 |
+
No benchmark available yet.
|
| 49 |
+
{% endif %}
|
| 50 |
+
{% if upstream %}
|
| 51 |
+
|
| 52 |
+
## Upstream
|
| 53 |
+
|
| 54 |
+
The original source code for this kernel comes from {{ upstream }}.
|
| 55 |
+
{% endif %}
|
| 56 |
+
{% if source %}
|
| 57 |
+
|
| 58 |
+
## Source
|
| 59 |
|
| 60 |
+
The kernel-builder formatted source for this kernel is available at {{ source }}.
|
| 61 |
+
{% endif %}
|
build.toml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[general]
|
| 2 |
+
name = "selective-update"
|
| 3 |
+
license = "Apache-2.0"
|
| 4 |
+
backends = [
|
| 5 |
+
"rocm",
|
| 6 |
+
]
|
| 7 |
+
version = 1
|
| 8 |
+
edition = 5
|
| 9 |
+
|
| 10 |
+
[general.hub]
|
| 11 |
+
repo-id = "Ashiedu/selective-update"
|
| 12 |
+
|
| 13 |
+
[torch-noarch]
|
example.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.13"
|
| 3 |
+
# dependencies = [
|
| 4 |
+
# "kernels",
|
| 5 |
+
# "numpy",
|
| 6 |
+
# "torch",
|
| 7 |
+
# ]
|
| 8 |
+
# ///
|
| 9 |
+
|
| 10 |
+
import platform
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
import kernels
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
# Load the locally built kernel
|
| 17 |
+
kernel = kernels.get_local_kernel(Path("build"), "selective_update")
|
| 18 |
+
|
| 19 |
+
# Select device
|
| 20 |
+
if platform.system() == "Darwin":
|
| 21 |
+
device = torch.device("mps")
|
| 22 |
+
elif hasattr(torch, "xpu") and torch.xpu.is_available():
|
| 23 |
+
device = torch.device("xpu")
|
| 24 |
+
elif torch.version.cuda is not None and torch.cuda.is_available():
|
| 25 |
+
device = torch.device("cuda")
|
| 26 |
+
else:
|
| 27 |
+
device = torch.device("cpu")
|
| 28 |
+
|
| 29 |
+
print(f"Using device: {device}")
|
| 30 |
+
|
| 31 |
+
# Create input tensor
|
| 32 |
+
x = torch.tensor([1.0, 2.0, 3.0], device=device)
|
| 33 |
+
print(f"Input: {x}")
|
| 34 |
+
|
| 35 |
+
# Run kernel (adds 1 to each element)
|
| 36 |
+
result = kernel.selective_update(x)
|
| 37 |
+
print(f"Output: {result}")
|
| 38 |
+
|
| 39 |
+
# Verify result
|
| 40 |
+
expected = x + 1.0
|
| 41 |
+
assert torch.allclose(result, expected), "Kernel output doesn't match expected!"
|
| 42 |
+
print("Success!")
|
flake.lock
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"nodes": {
|
| 3 |
+
"flake-compat": {
|
| 4 |
+
"locked": {
|
| 5 |
+
"lastModified": 1767039857,
|
| 6 |
+
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
| 7 |
+
"owner": "edolstra",
|
| 8 |
+
"repo": "flake-compat",
|
| 9 |
+
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
| 10 |
+
"type": "github"
|
| 11 |
+
},
|
| 12 |
+
"original": {
|
| 13 |
+
"owner": "edolstra",
|
| 14 |
+
"repo": "flake-compat",
|
| 15 |
+
"type": "github"
|
| 16 |
+
}
|
| 17 |
+
},
|
| 18 |
+
"flake-utils": {
|
| 19 |
+
"inputs": {
|
| 20 |
+
"systems": "systems"
|
| 21 |
+
},
|
| 22 |
+
"locked": {
|
| 23 |
+
"lastModified": 1731533236,
|
| 24 |
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
| 25 |
+
"owner": "numtide",
|
| 26 |
+
"repo": "flake-utils",
|
| 27 |
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
| 28 |
+
"type": "github"
|
| 29 |
+
},
|
| 30 |
+
"original": {
|
| 31 |
+
"owner": "numtide",
|
| 32 |
+
"repo": "flake-utils",
|
| 33 |
+
"type": "github"
|
| 34 |
+
}
|
| 35 |
+
},
|
| 36 |
+
"kernel-builder": {
|
| 37 |
+
"inputs": {
|
| 38 |
+
"flake-compat": "flake-compat",
|
| 39 |
+
"flake-utils": "flake-utils",
|
| 40 |
+
"nixpkgs": "nixpkgs",
|
| 41 |
+
"rust-overlay": "rust-overlay"
|
| 42 |
+
},
|
| 43 |
+
"locked": {
|
| 44 |
+
"lastModified": 1783738448,
|
| 45 |
+
"narHash": "sha256-vNb0C3LOAPlDPJH2OHbrVND5KrfvNXcl66nQaVp8rRU=",
|
| 46 |
+
"owner": "huggingface",
|
| 47 |
+
"repo": "kernels",
|
| 48 |
+
"rev": "570dcf4a810e6bd9d2433716c2bc6962c6ae46bb",
|
| 49 |
+
"type": "github"
|
| 50 |
+
},
|
| 51 |
+
"original": {
|
| 52 |
+
"owner": "huggingface",
|
| 53 |
+
"repo": "kernels",
|
| 54 |
+
"type": "github"
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
"nixpkgs": {
|
| 58 |
+
"locked": {
|
| 59 |
+
"lastModified": 1783284758,
|
| 60 |
+
"narHash": "sha256-tiQ8/qi8I45OOaBBYlVbXoAVkeQzvvTQOv5I45rMw5o=",
|
| 61 |
+
"owner": "NixOS",
|
| 62 |
+
"repo": "nixpkgs",
|
| 63 |
+
"rev": "ec1a11210589d294f0ac99d3290a27e6c73dfa1d",
|
| 64 |
+
"type": "github"
|
| 65 |
+
},
|
| 66 |
+
"original": {
|
| 67 |
+
"owner": "NixOS",
|
| 68 |
+
"repo": "nixpkgs",
|
| 69 |
+
"rev": "ec1a11210589d294f0ac99d3290a27e6c73dfa1d",
|
| 70 |
+
"type": "github"
|
| 71 |
+
}
|
| 72 |
+
},
|
| 73 |
+
"root": {
|
| 74 |
+
"inputs": {
|
| 75 |
+
"kernel-builder": "kernel-builder"
|
| 76 |
+
}
|
| 77 |
+
},
|
| 78 |
+
"rust-overlay": {
|
| 79 |
+
"inputs": {
|
| 80 |
+
"nixpkgs": [
|
| 81 |
+
"kernel-builder",
|
| 82 |
+
"nixpkgs"
|
| 83 |
+
]
|
| 84 |
+
},
|
| 85 |
+
"locked": {
|
| 86 |
+
"lastModified": 1783320166,
|
| 87 |
+
"narHash": "sha256-l7C/OsjcnWDOk2K3ssj+SBduwL67LashjBqis9+t468=",
|
| 88 |
+
"owner": "oxalica",
|
| 89 |
+
"repo": "rust-overlay",
|
| 90 |
+
"rev": "20ee15370c9256669d66968b89ee20a4b0a4e673",
|
| 91 |
+
"type": "github"
|
| 92 |
+
},
|
| 93 |
+
"original": {
|
| 94 |
+
"owner": "oxalica",
|
| 95 |
+
"repo": "rust-overlay",
|
| 96 |
+
"type": "github"
|
| 97 |
+
}
|
| 98 |
+
},
|
| 99 |
+
"systems": {
|
| 100 |
+
"locked": {
|
| 101 |
+
"lastModified": 1681028828,
|
| 102 |
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
| 103 |
+
"owner": "nix-systems",
|
| 104 |
+
"repo": "default",
|
| 105 |
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
| 106 |
+
"type": "github"
|
| 107 |
+
},
|
| 108 |
+
"original": {
|
| 109 |
+
"owner": "nix-systems",
|
| 110 |
+
"repo": "default",
|
| 111 |
+
"type": "github"
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
},
|
| 115 |
+
"root": "root",
|
| 116 |
+
"version": 7
|
| 117 |
+
}
|
flake.nix
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
inputs = {
|
| 3 |
+
kernel-builder.url = "github:huggingface/kernels";
|
| 4 |
+
};
|
| 5 |
+
outputs =
|
| 6 |
+
{ self, kernel-builder, ... }:
|
| 7 |
+
kernel-builder.lib.genKernelFlakeOutputs {
|
| 8 |
+
inherit self;
|
| 9 |
+
path = ./.;
|
| 10 |
+
};
|
| 11 |
+
}
|
selective_update_cuda/selective_update.cu
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <ATen/cuda/CUDAContext.h>
|
| 2 |
+
#include <c10/cuda/CUDAGuard.h>
|
| 3 |
+
#include <torch/all.h>
|
| 4 |
+
|
| 5 |
+
__global__ void selective_update_kernel(float *__restrict__ out,
|
| 6 |
+
float const *__restrict__ input, const int n) {
|
| 7 |
+
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
| 8 |
+
if (idx < n) {
|
| 9 |
+
out[idx] = input[idx] + 1.0f;
|
| 10 |
+
}
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
void selective_update(torch::Tensor &out, torch::Tensor const &input) {
|
| 14 |
+
TORCH_CHECK(input.device().is_cuda(), "input must be a CUDA tensor");
|
| 15 |
+
TORCH_CHECK(input.is_contiguous(), "input must be contiguous");
|
| 16 |
+
TORCH_CHECK(input.scalar_type() == at::ScalarType::Float,
|
| 17 |
+
"selective_update only supports float32");
|
| 18 |
+
TORCH_CHECK(input.sizes() == out.sizes(),
|
| 19 |
+
"Tensors must have the same shape");
|
| 20 |
+
TORCH_CHECK(input.scalar_type() == out.scalar_type(),
|
| 21 |
+
"Tensors must have the same dtype");
|
| 22 |
+
TORCH_CHECK(input.device() == out.device(),
|
| 23 |
+
"Tensors must be on the same device");
|
| 24 |
+
|
| 25 |
+
int n = input.numel();
|
| 26 |
+
int threads = 256;
|
| 27 |
+
int blocks = (n + threads - 1) / threads;
|
| 28 |
+
|
| 29 |
+
const at::cuda::OptionalCUDAGuard device_guard(device_of(input));
|
| 30 |
+
const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
|
| 31 |
+
selective_update_kernel<<<blocks, threads, 0, stream>>>(
|
| 32 |
+
out.data_ptr<float>(), input.data_ptr<float>(), n);
|
| 33 |
+
}
|
tests/__init__.py
ADDED
|
File without changes
|
tests/test_selective_update.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import platform
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
import selective_update
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_selective_update():
|
| 9 |
+
if platform.system() == "Darwin":
|
| 10 |
+
device = torch.device("mps")
|
| 11 |
+
elif hasattr(torch, "xpu") and torch.xpu.is_available():
|
| 12 |
+
device = torch.device("xpu")
|
| 13 |
+
elif torch.version.cuda is not None and torch.cuda.is_available():
|
| 14 |
+
device = torch.device("cuda")
|
| 15 |
+
else:
|
| 16 |
+
device = torch.device("cpu")
|
| 17 |
+
|
| 18 |
+
x = torch.randn(1024, 1024, dtype=torch.float32, device=device)
|
| 19 |
+
expected = x + 1.0
|
| 20 |
+
result = selective_update.selective_update(x)
|
| 21 |
+
torch.testing.assert_close(result, expected)
|
torch-ext/selective_update/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from .functional import selective_state_update
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def selective_update(x: torch.Tensor, out: Optional[torch.Tensor] = None) -> torch.Tensor:
|
| 9 |
+
"""Legacy scaffold kernel — adds 1.0 to each element."""
|
| 10 |
+
if out is None:
|
| 11 |
+
out = torch.empty_like(x)
|
| 12 |
+
out.copy_(x + 1.0)
|
| 13 |
+
return out
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
__all__ = ["selective_update", "selective_state_update"]
|
torch-ext/selective_update/_autotune_configs.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import warnings
|
| 3 |
+
from packaging import version
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
import triton
|
| 9 |
+
TRITON_VERSION = version.parse(triton.__version__)
|
| 10 |
+
except ImportError:
|
| 11 |
+
TRITON_VERSION = version.parse("0.0.0")
|
| 12 |
+
|
| 13 |
+
TRITON_HAS_CACHE_RESULTS = TRITON_VERSION >= version.parse("3.4.0")
|
| 14 |
+
_autotune_warning_issued = False
|
| 15 |
+
_deterministic_override = None
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def use_deterministic_mode():
|
| 19 |
+
if _deterministic_override is not None:
|
| 20 |
+
return _deterministic_override
|
| 21 |
+
env = os.environ.get("MAMBA_DETERMINISTIC")
|
| 22 |
+
if env:
|
| 23 |
+
return env[0] == "1"
|
| 24 |
+
return torch.are_deterministic_algorithms_enabled()
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def set_deterministic_mode(value):
|
| 28 |
+
global _deterministic_override
|
| 29 |
+
_deterministic_override = value
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def is_gfx1030():
|
| 33 |
+
"""Identifies the HIP gfx1030 hardware."""
|
| 34 |
+
if not torch.cuda.is_available():
|
| 35 |
+
return False
|
| 36 |
+
try:
|
| 37 |
+
device_cap = torch.cuda.get_device_capability()
|
| 38 |
+
return device_cap == (10, 3)
|
| 39 |
+
except Exception:
|
| 40 |
+
return False
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def gfx1030_selective_update_configs(configs):
|
| 44 |
+
"""Pin safe tile configs for selective-update on gfx1030."""
|
| 45 |
+
if not is_gfx1030():
|
| 46 |
+
return configs
|
| 47 |
+
|
| 48 |
+
expected = {
|
| 49 |
+
"BLOCK_SIZE_M": 32,
|
| 50 |
+
"BLOCK_SIZE_DSTATE": 64,
|
| 51 |
+
}
|
| 52 |
+
selected = [
|
| 53 |
+
config
|
| 54 |
+
for config in configs
|
| 55 |
+
if all(config.kwargs.get(key) == value for key, value in expected.items())
|
| 56 |
+
]
|
| 57 |
+
if not selected:
|
| 58 |
+
return [configs[0]]
|
| 59 |
+
return selected[:1]
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def _estimate_config_cost(cfg):
|
| 63 |
+
block_product = 1
|
| 64 |
+
for key, val in cfg.kwargs.items():
|
| 65 |
+
if key.startswith("BLOCK_SIZE_"):
|
| 66 |
+
block_product *= val
|
| 67 |
+
return block_product * (getattr(cfg, "num_stages", 1) or 1)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _filter_configs_by_block_sizes(configs):
|
| 71 |
+
env_filters = {}
|
| 72 |
+
for suffix in ("M", "N", "K", "DSTATE"):
|
| 73 |
+
env_val = os.environ.get(f"TRITON_AUTOTUNE_BLOCK_SIZE_{suffix}")
|
| 74 |
+
if env_val is not None:
|
| 75 |
+
env_filters[f"BLOCK_SIZE_{suffix}"] = int(env_val)
|
| 76 |
+
if not env_filters:
|
| 77 |
+
return None
|
| 78 |
+
matching = configs
|
| 79 |
+
for key, target in env_filters.items():
|
| 80 |
+
matching = [c for c in matching if c.kwargs.get(key) == target]
|
| 81 |
+
return matching[:1] if matching else None
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def autotune_configs(configs):
|
| 85 |
+
if not configs or not use_deterministic_mode():
|
| 86 |
+
return configs
|
| 87 |
+
if TRITON_HAS_CACHE_RESULTS and os.environ.get("TRITON_CACHE_AUTOTUNING") == "1":
|
| 88 |
+
return configs
|
| 89 |
+
global _autotune_warning_issued
|
| 90 |
+
if not _autotune_warning_issued:
|
| 91 |
+
_autotune_warning_issued = True
|
| 92 |
+
msg = (
|
| 93 |
+
"Deterministic mode: set TRITON_CACHE_AUTOTUNING=1 for cached autotuning."
|
| 94 |
+
if TRITON_HAS_CACHE_RESULTS
|
| 95 |
+
else "Deterministic mode: upgrade to Triton >= 3.4.0 for cached autotuning."
|
| 96 |
+
)
|
| 97 |
+
warnings.warn(msg)
|
| 98 |
+
filtered = _filter_configs_by_block_sizes(configs)
|
| 99 |
+
if filtered:
|
| 100 |
+
return filtered
|
| 101 |
+
return [min(configs, key=_estimate_config_cost)]
|
torch-ext/selective_update/functional.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Python autograd wrapper for selective-update Triton kernel.
|
| 2 |
+
|
| 3 |
+
Matches upstream ``mamba_ssm.ops.triton.selective_state_update`` ABI exactly:
|
| 4 |
+
state: (B, D, N) or (B, H, D, N)
|
| 5 |
+
x: (B, D) or (B, H, D)
|
| 6 |
+
dt: (B, D) or (B, H, D)
|
| 7 |
+
A: (D, N) or (H, D, N)
|
| 8 |
+
B: (B, N) or (B, G, N)
|
| 9 |
+
C: (B, N) or (B, G, N)
|
| 10 |
+
D: (D,) or (H, D) or None
|
| 11 |
+
z: (B, D) or (B, H, D) or None
|
| 12 |
+
dt_bias: (D,) or (H, D) or None
|
| 13 |
+
state_batch_indices: (B,) or None
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
from __future__ import annotations
|
| 17 |
+
|
| 18 |
+
import importlib.util
|
| 19 |
+
from pathlib import Path
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
import triton
|
| 23 |
+
|
| 24 |
+
_TRITON_KERNELS_PATH = (
|
| 25 |
+
Path(__file__).resolve().parent / "triton_kernels.py"
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _load_triton_kernel():
|
| 30 |
+
spec = importlib.util.spec_from_file_location(
|
| 31 |
+
"selective_update.triton_kernels",
|
| 32 |
+
_TRITON_KERNELS_PATH,
|
| 33 |
+
)
|
| 34 |
+
module = importlib.util.module_from_spec(spec)
|
| 35 |
+
spec.loader.exec_module(module)
|
| 36 |
+
return module.selective_state_update_kernel
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
selective_state_update_kernel = None # populated on first call
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _get_kernel():
|
| 43 |
+
global selective_state_update_kernel
|
| 44 |
+
if selective_state_update_kernel is None:
|
| 45 |
+
selective_state_update_kernel = _load_triton_kernel()
|
| 46 |
+
return selective_state_update_kernel
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _resolve_tie_hdim(A, dt, dt_bias):
|
| 50 |
+
if A.dim() == 2:
|
| 51 |
+
a0 = A[0]
|
| 52 |
+
return a0.stride(0) == 0 and a0.stride(1) == 0 and dt.stride(-1) == 0 and (dt_bias.stride(-1) == 0 if dt_bias is not None else True)
|
| 53 |
+
return A.stride(-1) == 0 and A.stride(-2) == 0 and dt.stride(-1) == 0 and (dt_bias.stride(-1) == 0 if dt_bias is not None else True)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class SelectiveUpdateFunction(torch.autograd.Function):
|
| 57 |
+
"""Differentiable single-token selective state update.
|
| 58 |
+
|
| 59 |
+
Mirrors upstream ``selective_state_update`` shape contract exactly.
|
| 60 |
+
Backward raises NotImplementedError — training uses the patched
|
| 61 |
+
selective_scan reverse path instead.
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
@staticmethod
|
| 65 |
+
def forward(ctx, state, x, dt, A, B, C, D=None, z=None, dt_bias=None, dt_softplus=False, state_batch_indices=None):
|
| 66 |
+
orig_state = state
|
| 67 |
+
has_heads = state.dim() > 3
|
| 68 |
+
if state.dim() == 3:
|
| 69 |
+
state = state.unsqueeze(1)
|
| 70 |
+
if x.dim() == 2:
|
| 71 |
+
x = x.unsqueeze(1)
|
| 72 |
+
if dt.dim() == 2:
|
| 73 |
+
dt = dt.unsqueeze(1)
|
| 74 |
+
if A.dim() == 2:
|
| 75 |
+
A = A.unsqueeze(0)
|
| 76 |
+
if B.dim() == 2:
|
| 77 |
+
B = B.unsqueeze(1)
|
| 78 |
+
if C.dim() == 2:
|
| 79 |
+
C = C.unsqueeze(1)
|
| 80 |
+
if D is not None and D.dim() == 1:
|
| 81 |
+
D = D.unsqueeze(0)
|
| 82 |
+
if z is not None and z.dim() == 2:
|
| 83 |
+
z = z.unsqueeze(1)
|
| 84 |
+
if dt_bias is not None and dt_bias.dim() == 1:
|
| 85 |
+
dt_bias = dt_bias.unsqueeze(0)
|
| 86 |
+
|
| 87 |
+
B_sz, H, D_sz, N_sz = state.shape
|
| 88 |
+
x_sz = x.shape[0]
|
| 89 |
+
if x.shape != (x_sz, H, D_sz):
|
| 90 |
+
raise ValueError(f"x shape {x.shape} does not match state batch/heads/dim")
|
| 91 |
+
if dt.shape != (x_sz, H, D_sz):
|
| 92 |
+
raise ValueError(f"dt shape {dt.shape} does not match x shape")
|
| 93 |
+
if A.shape != (H, D_sz, N_sz):
|
| 94 |
+
raise ValueError(f"A shape {A.shape} does not match (H, D, N)")
|
| 95 |
+
ngroups = B.shape[1]
|
| 96 |
+
if H % ngroups != 0:
|
| 97 |
+
raise ValueError(f"nheads {H} must be divisible by ngroups {ngroups}")
|
| 98 |
+
if B.shape[0] != x_sz or B.shape[2] != N_sz or C.shape[0] != x_sz or C.shape[2] != N_sz:
|
| 99 |
+
raise ValueError(f"B/C batch or dstate mismatch")
|
| 100 |
+
if D is not None and D.shape not in {(H, D_sz), (H,)}:
|
| 101 |
+
raise ValueError(f"D shape {D.shape} does not match (H, D) or (H,)")
|
| 102 |
+
if z is not None and z.shape != (x_sz, H, D_sz):
|
| 103 |
+
raise ValueError(f"z shape {z.shape} does not match x shape")
|
| 104 |
+
if dt_bias is not None and dt_bias.shape not in {(H, D_sz), (H,)}:
|
| 105 |
+
raise ValueError(f"dt_bias shape {dt_bias.shape} does not match (H, D) or (H,)")
|
| 106 |
+
if state_batch_indices is not None and state_batch_indices.shape != (x_sz,):
|
| 107 |
+
raise ValueError(f"state_batch_indices shape {state_batch_indices.shape} does not match (B,)")
|
| 108 |
+
|
| 109 |
+
tie_hdim = _resolve_tie_hdim(A, dt, dt_bias)
|
| 110 |
+
nheads_ratio = H // ngroups
|
| 111 |
+
|
| 112 |
+
out = torch.empty_like(x)
|
| 113 |
+
grid = lambda META: (triton.cdiv(D_sz, META["BLOCK_SIZE_M"]), x_sz, H)
|
| 114 |
+
|
| 115 |
+
BLOCK_M = 32 if N_sz <= 16 else (16 if N_sz <= 32 else (8 if N_sz <= 64 else (4 if N_sz <= 128 else 4)))
|
| 116 |
+
num_warps = 4 if N_sz <= 64 else 8
|
| 117 |
+
|
| 118 |
+
z_strides = ((z.stride(0), z.stride(1), z.stride(2)) if z is not None else (0, 0, 0))
|
| 119 |
+
|
| 120 |
+
_get_kernel()[grid](
|
| 121 |
+
state, x, dt, dt_bias, A, B, C, D, z, out, state_batch_indices,
|
| 122 |
+
x_sz, H, D_sz, N_sz, nheads_ratio,
|
| 123 |
+
state.stride(0), state.stride(1), state.stride(2), state.stride(3),
|
| 124 |
+
x.stride(0), x.stride(1), x.stride(2),
|
| 125 |
+
dt.stride(0), dt.stride(1), dt.stride(2),
|
| 126 |
+
dt_bias.stride(0) if dt_bias is not None else 0, dt_bias.stride(1) if dt_bias is not None else 0,
|
| 127 |
+
A.stride(0), A.stride(1), A.stride(2),
|
| 128 |
+
B.stride(0), B.stride(1), B.stride(2),
|
| 129 |
+
C.stride(0), C.stride(1), C.stride(2),
|
| 130 |
+
D.stride(0) if D is not None else 0, D.stride(1) if D is not None else 0,
|
| 131 |
+
z_strides[0], z_strides[1], z_strides[2],
|
| 132 |
+
out.stride(0), out.stride(1), out.stride(2),
|
| 133 |
+
dt_softplus,
|
| 134 |
+
tie_hdim,
|
| 135 |
+
BLOCK_M,
|
| 136 |
+
num_warps=num_warps,
|
| 137 |
+
num_stages=2,
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
if not has_heads:
|
| 141 |
+
out = out.squeeze(1)
|
| 142 |
+
state = state.squeeze(1)
|
| 143 |
+
ctx.save_for_backward(orig_state, x, dt, A, B, C, D, z, dt_bias, state, state_batch_indices)
|
| 144 |
+
ctx.tie_hdim = tie_hdim
|
| 145 |
+
ctx.dt_softplus = dt_softplus
|
| 146 |
+
ctx.has_heads = has_heads
|
| 147 |
+
ctx.nheads_ratio = nheads_ratio
|
| 148 |
+
return out
|
| 149 |
+
|
| 150 |
+
@staticmethod
|
| 151 |
+
def backward(ctx, grad_out):
|
| 152 |
+
raise NotImplementedError(
|
| 153 |
+
"SelectiveUpdateFunction backward is not implemented. "
|
| 154 |
+
"Use autograd through the patched selective_scan path for training."
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def selective_state_update(state, x, dt, A, B, C, D=None, z=None, dt_bias=None, dt_softplus=False, state_batch_indices=None, **kwargs):
|
| 159 |
+
"""Dispatches single-token state updates to the custom Triton backend.
|
| 160 |
+
|
| 161 |
+
Shapes mirror upstream ``mamba_ssm.ops.triton.selective_state_update`` exactly.
|
| 162 |
+
"""
|
| 163 |
+
return SelectiveUpdateFunction.apply(
|
| 164 |
+
state, x, dt, A, B, C, D, z, dt_bias, dt_softplus, state_batch_indices
|
| 165 |
+
)
|
torch-ext/selective_update/triton_kernels.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Triton kernels for Mamba-2 single-token selective state updates.
|
| 2 |
+
|
| 3 |
+
Matches upstream ``mamba_ssm.ops.triton.selective_state_update`` ABI exactly:
|
| 4 |
+
state: (B, H, D, N) x/dt: (B, H, D) A: (H, D, N) B/C: (B, G, N)
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import triton
|
| 8 |
+
import triton.language as tl
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@triton.jit
|
| 12 |
+
def _softplus(dt):
|
| 13 |
+
return tl.math.log(tl.math.exp(dt) + 1)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@triton.heuristics({"HAS_DT_BIAS": lambda args: args["dt_bias_ptr"] is not None})
|
| 17 |
+
@triton.heuristics({"HAS_D": lambda args: args["D_ptr"] is not None})
|
| 18 |
+
@triton.heuristics({"HAS_Z": lambda args: args["z_ptr"] is not None})
|
| 19 |
+
@triton.heuristics({"HAS_STATE_BATCH_INDICES": lambda args: args["state_batch_indices_ptr"] is not None})
|
| 20 |
+
@triton.heuristics({"BLOCK_SIZE_DSTATE": lambda args: triton.next_power_of_2(args["dstate"])})
|
| 21 |
+
@triton.jit
|
| 22 |
+
def selective_state_update_kernel(
|
| 23 |
+
state_ptr, x_ptr, dt_ptr, dt_bias_ptr, A_ptr, B_ptr, C_ptr, D_ptr, z_ptr, out_ptr, state_batch_indices_ptr,
|
| 24 |
+
batch, nheads, dim, dstate, nheads_ngroups_ratio,
|
| 25 |
+
s_state_batch, s_state_head, s_state_dim, s_state_dstate,
|
| 26 |
+
s_x_batch, s_x_head, s_x_dim,
|
| 27 |
+
s_dt_batch, s_dt_head, s_dt_dim,
|
| 28 |
+
s_dt_bias_head, s_dt_bias_dim,
|
| 29 |
+
s_A_head, s_A_dim, s_A_dstate,
|
| 30 |
+
s_B_batch, s_B_group, s_B_dstate,
|
| 31 |
+
s_C_batch, s_C_group, s_C_dstate,
|
| 32 |
+
s_D_head, s_D_dim,
|
| 33 |
+
s_z_batch, s_z_head, s_z_dim,
|
| 34 |
+
s_out_batch, s_out_head, s_out_dim,
|
| 35 |
+
DT_SOFTPLUS: tl.constexpr,
|
| 36 |
+
TIE_HDIM: tl.constexpr,
|
| 37 |
+
BLOCK_SIZE_M: tl.constexpr,
|
| 38 |
+
HAS_DT_BIAS: tl.constexpr,
|
| 39 |
+
HAS_D: tl.constexpr,
|
| 40 |
+
HAS_Z: tl.constexpr,
|
| 41 |
+
HAS_STATE_BATCH_INDICES: tl.constexpr,
|
| 42 |
+
BLOCK_SIZE_DSTATE: tl.constexpr,
|
| 43 |
+
):
|
| 44 |
+
pid_m = tl.program_id(axis=0)
|
| 45 |
+
pid_b = tl.program_id(axis=1)
|
| 46 |
+
pid_h = tl.program_id(axis=2)
|
| 47 |
+
|
| 48 |
+
offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
|
| 49 |
+
out_ptr += pid_b * s_out_batch + pid_h * s_out_head
|
| 50 |
+
out_ptrs = out_ptr + offs_m * s_out_dim
|
| 51 |
+
|
| 52 |
+
if HAS_STATE_BATCH_INDICES:
|
| 53 |
+
state_batch_indices_ptr += pid_b
|
| 54 |
+
state_batch_idx = tl.load(state_batch_indices_ptr)
|
| 55 |
+
if state_batch_idx < 0:
|
| 56 |
+
tl.store(out_ptrs, 0.0, mask=offs_m < dim)
|
| 57 |
+
return
|
| 58 |
+
state_ptr += state_batch_idx * s_state_batch + pid_h * s_state_head
|
| 59 |
+
else:
|
| 60 |
+
state_ptr += pid_b * s_state_batch + pid_h * s_state_head
|
| 61 |
+
|
| 62 |
+
x_ptr += pid_b * s_x_batch + pid_h * s_x_head
|
| 63 |
+
dt_ptr += pid_b * s_dt_batch + pid_h * s_dt_head
|
| 64 |
+
if HAS_DT_BIAS:
|
| 65 |
+
dt_bias_ptr += pid_h * s_dt_bias_head
|
| 66 |
+
A_ptr += pid_h * s_A_head
|
| 67 |
+
B_ptr += pid_b * s_B_batch + (pid_h // nheads_ngroups_ratio) * s_B_group
|
| 68 |
+
C_ptr += pid_b * s_C_batch + (pid_h // nheads_ngroups_ratio) * s_C_group
|
| 69 |
+
if HAS_Z:
|
| 70 |
+
z_ptr += pid_b * s_z_batch + pid_h * s_z_head
|
| 71 |
+
|
| 72 |
+
offs_n = tl.arange(0, BLOCK_SIZE_DSTATE)
|
| 73 |
+
state_ptrs = state_ptr + (offs_m[:, None] * s_state_dim + offs_n[None, :] * s_state_dstate)
|
| 74 |
+
x_ptrs = x_ptr + offs_m * s_x_dim
|
| 75 |
+
dt_ptrs = dt_ptr + offs_m * s_dt_dim
|
| 76 |
+
if HAS_DT_BIAS:
|
| 77 |
+
dt_bias_ptrs = dt_bias_ptr + offs_m * s_dt_bias_dim
|
| 78 |
+
if HAS_D:
|
| 79 |
+
D_ptr += pid_h * s_D_head
|
| 80 |
+
A_ptrs = A_ptr + (offs_m[:, None] * s_A_dim + offs_n[None, :] * s_A_dstate)
|
| 81 |
+
B_ptrs = B_ptr + offs_n * s_B_dstate
|
| 82 |
+
C_ptrs = C_ptr + offs_n * s_C_dstate
|
| 83 |
+
if HAS_D:
|
| 84 |
+
D_ptrs = D_ptr + offs_m * s_D_dim
|
| 85 |
+
if HAS_Z:
|
| 86 |
+
z_ptrs = z_ptr + offs_m * s_z_dim
|
| 87 |
+
|
| 88 |
+
state = tl.load(state_ptrs, mask=(offs_m[:, None] < dim) & (offs_n[None, :] < dstate), other=0.0)
|
| 89 |
+
x = tl.load(x_ptrs, mask=offs_m < dim, other=0.0).to(tl.float32)
|
| 90 |
+
if not TIE_HDIM:
|
| 91 |
+
dt = tl.load(dt_ptrs, mask=offs_m < dim, other=0.0).to(tl.float32)
|
| 92 |
+
if HAS_DT_BIAS:
|
| 93 |
+
dt += tl.load(dt_bias_ptrs, mask=offs_m < dim, other=0.0).to(tl.float32)
|
| 94 |
+
if DT_SOFTPLUS:
|
| 95 |
+
dt = tl.where(dt <= 20.0, _softplus(dt), dt)
|
| 96 |
+
A = tl.load(A_ptrs, mask=(offs_m[:, None] < dim) & (offs_n[None, :] < dstate), other=0.0).to(tl.float32)
|
| 97 |
+
dA = tl.exp(A * dt[:, None])
|
| 98 |
+
else:
|
| 99 |
+
dt = tl.load(dt_ptr).to(tl.float32)
|
| 100 |
+
if HAS_DT_BIAS:
|
| 101 |
+
dt += tl.load(dt_bias_ptr).to(tl.float32)
|
| 102 |
+
if DT_SOFTPLUS:
|
| 103 |
+
dt = tl.where(dt <= 20.0, _softplus(dt), dt)
|
| 104 |
+
A = tl.load(A_ptr).to(tl.float32)
|
| 105 |
+
dA = tl.exp(A * dt)
|
| 106 |
+
|
| 107 |
+
B = tl.load(B_ptrs, mask=offs_n < dstate, other=0.0).to(tl.float32)
|
| 108 |
+
C = tl.load(C_ptrs, mask=offs_n < dstate, other=0.0).to(tl.float32)
|
| 109 |
+
if HAS_D:
|
| 110 |
+
D = tl.load(D_ptrs, mask=offs_m < dim, other=0.0).to(tl.float32)
|
| 111 |
+
if HAS_Z:
|
| 112 |
+
z = tl.load(z_ptrs, mask=offs_m < dim, other=0.0).to(tl.float32)
|
| 113 |
+
|
| 114 |
+
if not TIE_HDIM:
|
| 115 |
+
dB = B[None, :] * dt[:, None]
|
| 116 |
+
else:
|
| 117 |
+
dB = B * dt
|
| 118 |
+
|
| 119 |
+
state = state * dA + dB * x[:, None]
|
| 120 |
+
tl.store(state_ptrs, state, mask=(offs_m[:, None] < dim) & (offs_n[None, :] < dstate))
|
| 121 |
+
out = tl.sum(state * C[None, :], axis=1)
|
| 122 |
+
if HAS_D:
|
| 123 |
+
out += x * D
|
| 124 |
+
if HAS_Z:
|
| 125 |
+
out *= z * tl.sigmoid(z)
|
| 126 |
+
|
| 127 |
+
tl.store(out_ptrs, out, mask=offs_m < dim)
|
torch-ext/torch_binding.cpp
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <torch/library.h>
|
| 2 |
+
|
| 3 |
+
#include "registration.h"
|
| 4 |
+
#include "torch_binding.h"
|
| 5 |
+
|
| 6 |
+
TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
|
| 7 |
+
ops.def("selective_update(Tensor! out, Tensor input) -> ()");
|
| 8 |
+
#if defined(CPU_KERNEL)
|
| 9 |
+
ops.impl("selective_update", torch::kCPU, &selective_update);
|
| 10 |
+
#elif defined(CUDA_KERNEL) || defined(ROCM_KERNEL)
|
| 11 |
+
ops.impl("selective_update", torch::kCUDA, &selective_update);
|
| 12 |
+
#elif defined(METAL_KERNEL)
|
| 13 |
+
ops.impl("selective_update", torch::kMPS, selective_update);
|
| 14 |
+
#elif defined(XPU_KERNEL)
|
| 15 |
+
ops.impl("selective_update", torch::kXPU, &selective_update);
|
| 16 |
+
#endif
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
REGISTER_EXTENSION(TORCH_EXTENSION_NAME)
|
torch-ext/torch_binding.h
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include <torch/torch.h>
|
| 4 |
+
|
| 5 |
+
void selective_update(torch::Tensor &out, torch::Tensor const &input);
|