liangsu9988 commited on
Commit
02c100f
·
verified ·
1 Parent(s): 3b968ce

Promote latest kernel artifacts to main

Browse files
CARD.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FlashRT Adaptive Norms
2
+
3
+ This package provides FlashRT adaptive normalization kernels for Hugging Face
4
+ Kernel Hub.
5
+
6
+ It is intended for DiT, VLA, video, and world-model blocks that use RMSNorm
7
+ plus per-row or single-row-broadcast style scale/shift/gate parameters.
8
+
9
+ ## Kernels
10
+
11
+ - `ada_rms_norm_style_bf16`: RMSNorm + style scale/shift, with BF16 output and
12
+ BF16 gate extraction.
13
+ - `gate_residual_ada_norm_fp8_static_bf16`: in-place gated residual update,
14
+ AdaRMSNorm, static FP8 E4M3 output, and BF16 gate extraction.
15
+
16
+ ## Hardware
17
+
18
+ - CUDA 12.8+
19
+ - BF16-capable NVIDIA GPUs
20
+ - FP8-capable NVIDIA GPUs for the FP8 output API
21
+
22
+ The per-row and broadcast contracts are source-validated on RTX 5090 and
23
+ Jetson AGX Thor SM110.
24
+
25
+ ## Upstream
26
+
27
+ The serving source of truth remains FlashRT:
28
+
29
+ https://github.com/LiangSu8899/FlashRT
README.md CHANGED
@@ -1,9 +1,83 @@
1
- # flashrt/flashrt-adaptive-norms
2
 
3
- This repository is a compatibility mirror for older `kernels` clients
4
- that resolve repositories through the default Hugging Face model repo API.
5
 
6
- Canonical Kernel Hub repo: https://huggingface.co/kernels/flashrt/flashrt-adaptive-norms
 
7
 
8
- Do not edit this mirror by hand. It is generated from the Kernel Hub
9
- `vN` branches and contains the same `build/**` artifacts.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # flashrt-adaptive-norms
2
 
3
+ Tensor-facing FlashRT adaptive normalization kernels for Hugging Face `kernels`.
 
4
 
5
+ This package targets DiT, VLA, video, and world-model blocks that combine
6
+ RMSNorm with per-row style scale/shift/gate parameters:
7
 
8
+ ```text
9
+ AdaRMSNorm: x -> RMSNorm(x, weight) -> norm * (1 + style_scale) + style_shift
10
+ Fused gate path: residual += x * gate -> AdaRMSNorm(residual) -> static FP8
11
+ ```
12
+
13
+ ## Exported APIs
14
+
15
+ - `ada_rms_norm_style_bf16(x, weight, style, eps=1e-6, out=None, gate_out=None)`
16
+ - `gate_residual_ada_norm_fp8_static_bf16(residual, x, gate, weight, style, scale, eps=1e-6, out=None, gate_out=None)`
17
+
18
+ ## Tensor Conventions
19
+
20
+ - `x`, `residual`, `gate`: contiguous BF16 matrices, shape `(rows, dim)`
21
+ - `weight`: contiguous BF16 vector, shape `(dim,)`
22
+ - `style`: contiguous BF16 matrix, shape `(rows, 3 * dim)` or `(1, 3 * dim)`
23
+ for a style row broadcast across all rows
24
+ - first `dim`: style scale
25
+ - second `dim`: style shift
26
+ - third `dim`: gate output
27
+ - `scale`: CUDA FP32 scalar tensor for static FP8 quantization
28
+ - FP8 output dtype: `torch.float8_e4m3fn`
29
+ - `dim` must be even.
30
+
31
+ ## Minimal Usage
32
+
33
+ ```python
34
+ from kernels import get_kernel
35
+ import torch
36
+
37
+ ops = get_kernel(
38
+ "flashrt/flashrt-adaptive-norms",
39
+ version=1,
40
+ trust_remote_code=True,
41
+ )
42
+
43
+ rows, dim = 2520, 3072
44
+ x = torch.randn((rows, dim), device="cuda", dtype=torch.bfloat16)
45
+ residual = torch.randn_like(x)
46
+ gate = torch.randn_like(x)
47
+ weight = torch.ones((dim,), device="cuda", dtype=torch.bfloat16)
48
+ style = torch.randn((rows, 3 * dim), device="cuda", dtype=torch.bfloat16)
49
+ scale = torch.tensor([0.04], device="cuda", dtype=torch.float32)
50
+
51
+ out_bf16, gate_out = ops.ada_rms_norm_style_bf16(x, weight, style)
52
+ residual, out_fp8, gate_out = ops.gate_residual_ada_norm_fp8_static_bf16(
53
+ residual,
54
+ x,
55
+ gate,
56
+ weight,
57
+ style,
58
+ scale,
59
+ )
60
+ ```
61
+
62
+ For prompt/style values shared by every token, pass a single row directly;
63
+ the kernel broadcasts it without materializing a `rows x 3*dim` tensor:
64
+
65
+ ```python
66
+ shared_style = torch.randn((1, 3 * dim), device="cuda", dtype=torch.bfloat16)
67
+ out_bf16, gate_out = ops.ada_rms_norm_style_bf16(
68
+ x, weight, shared_style
69
+ )
70
+ ```
71
+
72
+ ## Validation
73
+
74
+ ```bash
75
+ python flashrt-adaptive-norms/tests/test_adaptive_norms.py --backend source --mode full
76
+ python flashrt-adaptive-norms/benchmarks/benchmark.py --backend source --shapes all
77
+ ```
78
+
79
+ RTX 5090 and Thor SM110 source-extension validation passes the full source
80
+ grid for both per-row and single-row broadcast style.
81
+ Residual and gate outputs are bit-level exact. FP8 output uses a boundary-aware
82
+ gate because PyTorch and CUDA FP8 casters can choose adjacent FP8 values for
83
+ rare tie cases; p99_abs is zero across the source grid.
SYNC.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Source Sync
2
+
3
+ Synced from `official/FlashRT`:
4
+
5
+ - `csrc/kernels/norm.cu`: `ada_rms_norm_style` and `ada_rms_norm_style_fp8`
6
+ math.
7
+ - `csrc/kernels/fusion.cu`: `gate_residual_ada_norm_fp8` fused residual,
8
+ AdaRMSNorm, and FP8 output path.
9
+
10
+ Local adaptation:
11
+
12
+ - Public package/API names use generic adaptive-norm terminology.
13
+ - Only BF16 input and static FP8 output paths are included in this first public
14
+ package version.
15
+ - Tensor-facing `torch.ops` bindings validate dtype, shape, contiguity, device,
16
+ and even hidden dimensions.
17
+ - CUDA math uses explicit round-to-nearest add/multiply operations in key
18
+ BF16-reference paths for stable public correctness behavior.
19
+ - FP8 validation gates on p99-zero and sparse boundary differences because
20
+ PyTorch and CUDA FP8 casters can select adjacent FP8 grid points on rare ties.
VALIDATION.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Validation: flashrt-adaptive-norms
2
+
3
+ Required before publishing this package:
4
+
5
+ 1. Source-extension correctness:
6
+
7
+ ```bash
8
+ python flashrt-adaptive-norms/tests/test_adaptive_norms.py \
9
+ --backend source \
10
+ --mode full
11
+ ```
12
+
13
+ 2. Source-extension benchmark:
14
+
15
+ ```bash
16
+ python flashrt-adaptive-norms/benchmarks/benchmark.py \
17
+ --backend source \
18
+ --shapes all
19
+ ```
20
+
21
+ 3. Kernel-builder artifact build:
22
+
23
+ ```bash
24
+ kernel-builder build-and-copy flashrt-adaptive-norms
25
+ ```
26
+
27
+ 4. Built-artifact correctness:
28
+
29
+ ```bash
30
+ PYTHONPATH=<artifact-path> \
31
+ python flashrt-adaptive-norms/tests/test_adaptive_norms.py \
32
+ --backend installed \
33
+ --mode full
34
+ ```
35
+
36
+ 5. Built-artifact benchmark:
37
+
38
+ ```bash
39
+ python flashrt-adaptive-norms/benchmarks/benchmark.py \
40
+ --backend installed \
41
+ --artifact <artifact-path> \
42
+ --shapes all
43
+ ```
44
+
45
+ 6. Multi-hardware matrix:
46
+
47
+ Add hardware claims only after the same correctness and benchmark commands
48
+ pass on that machine.
49
+
50
+ ## Style Broadcast Gate
51
+
52
+ Both APIs accept `style` as either `(rows, 3*dim)` or `(1, 3*dim)`. The full
53
+ source and installed-artifact runs must execute both layouts at rows
54
+ `64/2520/4096`. The broadcast path additionally requires bit-identical gate
55
+ output, exact residual update, FP8 p99 absolute error zero, CUDA Graph replay,
56
+ and installed-wrapper `torch.compile(fullgraph=True)` parity.
benchmarks/README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # benchmarks
2
+
3
+ Package-level microbenchmarks for adaptive norm APIs.
4
+
5
+ Run:
6
+
7
+ ```bash
8
+ python flashrt-adaptive-norms/benchmarks/benchmark.py --backend source --shapes all
9
+ ```
10
+
11
+ The benchmark compares the fused FlashRT Tensor API against a PyTorch eager
12
+ reference chain that performs the same adaptive norm and FP8 quantization math.
benchmarks/RESULTS.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Source Benchmark Results
2
+
3
+ Environment: NVIDIA GeForce RTX 5090 local source-extension build.
4
+ Baseline: PyTorch eager tensor reference with matching BF16/FP8 math contract.
5
+
6
+ | Shape | Rows,Dim | Kernel | FlashRT us | Eager us | vs eager | p99 abs | Cosine | Status |
7
+ |---|---:|---|---:|---:|---:|---:|---:|---|
8
+ | small | 64,1024 | ada_rms_norm_style_bf16 | 4.172 | 61.407 | 14.72x | 0.000000 | 0.99999988 | PASS |
9
+ | small | 64,1024 | gate_residual_ada_norm_fp8_static_bf16 | 4.150 | 81.997 | 19.76x | 0.000000 | 1.00000000 | PASS |
10
+ | vla_2k | 2520,3072 | ada_rms_norm_style_bf16 | 15.404 | 265.453 | 17.23x | 0.000000 | 0.99999994 | PASS |
11
+ | vla_2k | 2520,3072 | gate_residual_ada_norm_fp8_static_bf16 | 58.822 | 431.002 | 7.33x | 0.000000 | 1.00000000 | PASS |
12
+ | vla_4k | 4096,3072 | ada_rms_norm_style_bf16 | 79.392 | 594.854 | 7.49x | 0.000000 | 1.00000000 | PASS |
13
+ | vla_4k | 4096,3072 | gate_residual_ada_norm_fp8_static_bf16 | 119.475 | 901.242 | 7.54x | 0.000000 | 1.00000000 | PASS |
14
+
15
+ ## Thor Style-Broadcast Update
16
+
17
+ Source benchmark on NVIDIA Thor, PyTorch 2.13.0+cu130, August 8, 2026. The
18
+ single-row style path removes the expanded style tensor while preserving the
19
+ same public operation.
20
+
21
+ | Shape | Kernel | Per-row us | Broadcast us | Broadcast vs eager | P99 abs |
22
+ |---|---|---:|---:|---:|---:|
23
+ | `2520x3072` | AdaRMS style | 407.806 | 202.440 | 15.11x | 0 |
24
+ | `2520x3072` | gate+residual+AdaRMS to FP8 | 592.928 | 398.299 | 12.58x | 0 |
25
+ | `4096x3072` | AdaRMS style | 604.960 | 306.514 | 14.79x | 0 |
26
+ | `4096x3072` | gate+residual+AdaRMS to FP8 | 864.846 | 553.539 | 13.69x | 0 |
27
+
28
+ The full source gate passed both style layouts. Residual and gate tensors were
29
+ exact; FP8 p99 absolute error was zero across all tested rows.
benchmarks/benchmark.py CHANGED
@@ -6,6 +6,7 @@ from __future__ import annotations
6
  import argparse
7
  import ctypes
8
  import ctypes.util
 
9
  import json
10
  import math
11
  import os
@@ -135,16 +136,22 @@ def rms_norm(x, weight, eps):
135
  def ref_ada(x, weight, style, eps):
136
  dim = x.shape[1]
137
  normed = rms_norm(x, weight, eps)
138
- y = normed * (1.0 + style[:, :dim].float()) + style[:, dim : 2 * dim].float()
139
- return y.to(torch.bfloat16), style[:, 2 * dim :].contiguous().to(torch.bfloat16)
 
 
 
140
 
141
 
142
  def ref_fused(residual, x, gate, weight, style, scale, eps):
143
  updated = (residual.float() + x.float() * gate.float()).to(torch.bfloat16)
144
  dim = updated.shape[1]
145
  normed = rms_norm(updated, weight, eps)
146
- y = (normed * (1.0 + style[:, :dim].float()) + style[:, dim : 2 * dim].float()) / scale.float().reshape(())
147
- return updated, y.to(torch.float8_e4m3fn), style[:, 2 * dim :].contiguous().to(torch.bfloat16)
 
 
 
148
 
149
 
150
  def time_us(fn, warmup, iters):
@@ -174,9 +181,12 @@ def metrics(got, expected):
174
  )
175
 
176
 
177
- def run_one(ops, name, shape, args):
178
  rows, dim = shape
179
  x, residual, gate, weight, style, scale = make_case(rows, dim)
 
 
 
180
  out = torch.empty_like(x)
181
  gate_out = torch.empty_like(x)
182
  fp8_out = torch.empty_like(x, dtype=torch.float8_e4m3fn)
@@ -235,6 +245,10 @@ def main():
235
  parser.add_argument("--warmup", type=int, default=5)
236
  parser.add_argument("--iters", type=int, default=20)
237
  parser.add_argument("--eps", type=float, default=1e-6)
 
 
 
 
238
  parser.add_argument("--output", default=None)
239
  parser.add_argument("--markdown", default=None)
240
  args = parser.parse_args()
@@ -244,7 +258,10 @@ def main():
244
  ops = load_source_ops() if args.backend == "source" else load_installed_ops(args.artifact)
245
  results = []
246
  for name in SHAPE_GROUPS[args.shapes]:
247
- results.extend(run_one(ops, name, SHAPES[name], args))
 
 
 
248
  for r in results:
249
  print(
250
  f"{r.status} {r.shape}/{r.kernel}: flashrt={r.flashrt_us:.3f}us "
 
6
  import argparse
7
  import ctypes
8
  import ctypes.util
9
+ import importlib
10
  import json
11
  import math
12
  import os
 
136
  def ref_ada(x, weight, style, eps):
137
  dim = x.shape[1]
138
  normed = rms_norm(x, weight, eps)
139
+ style_scale = style[:, :dim].float().expand(x.shape[0], -1)
140
+ style_shift = style[:, dim : 2 * dim].float().expand(x.shape[0], -1)
141
+ style_gate = style[:, 2 * dim :].expand(x.shape[0], -1)
142
+ y = normed * (1.0 + style_scale) + style_shift
143
+ return y.to(torch.bfloat16), style_gate.contiguous().to(torch.bfloat16)
144
 
145
 
146
  def ref_fused(residual, x, gate, weight, style, scale, eps):
147
  updated = (residual.float() + x.float() * gate.float()).to(torch.bfloat16)
148
  dim = updated.shape[1]
149
  normed = rms_norm(updated, weight, eps)
150
+ style_scale = style[:, :dim].float().expand(updated.shape[0], -1)
151
+ style_shift = style[:, dim : 2 * dim].float().expand(updated.shape[0], -1)
152
+ style_gate = style[:, 2 * dim :].expand(updated.shape[0], -1)
153
+ y = (normed * (1.0 + style_scale) + style_shift) / scale.float().reshape(())
154
+ return updated, y.to(torch.float8_e4m3fn), style_gate.contiguous().to(torch.bfloat16)
155
 
156
 
157
  def time_us(fn, warmup, iters):
 
181
  )
182
 
183
 
184
+ def run_one(ops, name, shape, args, broadcast: bool):
185
  rows, dim = shape
186
  x, residual, gate, weight, style, scale = make_case(rows, dim)
187
+ if broadcast:
188
+ style = style[:1].contiguous()
189
+ name = f"{name}_broadcast"
190
  out = torch.empty_like(x)
191
  gate_out = torch.empty_like(x)
192
  fp8_out = torch.empty_like(x, dtype=torch.float8_e4m3fn)
 
245
  parser.add_argument("--warmup", type=int, default=5)
246
  parser.add_argument("--iters", type=int, default=20)
247
  parser.add_argument("--eps", type=float, default=1e-6)
248
+ parser.add_argument(
249
+ "--style-mode", choices=["per-row", "broadcast", "both"],
250
+ default="both"
251
+ )
252
  parser.add_argument("--output", default=None)
253
  parser.add_argument("--markdown", default=None)
254
  args = parser.parse_args()
 
258
  ops = load_source_ops() if args.backend == "source" else load_installed_ops(args.artifact)
259
  results = []
260
  for name in SHAPE_GROUPS[args.shapes]:
261
+ if args.style_mode in ("per-row", "both"):
262
+ results.extend(run_one(ops, name, SHAPES[name], args, False))
263
+ if args.style_mode in ("broadcast", "both"):
264
+ results.extend(run_one(ops, name, SHAPES[name], args, True))
265
  for r in results:
266
  print(
267
  f"{r.status} {r.shape}/{r.kernel}: flashrt={r.flashrt_us:.3f}us "
build.toml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [general]
2
+ name = "flashrt-adaptive-norms"
3
+ license = "Apache-2.0"
4
+ version = 1
5
+ backends = ["cuda"]
6
+
7
+ [general.cuda]
8
+ minver = "12.8"
9
+
10
+ [general.hub]
11
+ repo-id = "flashrt/flashrt-adaptive-norms"
12
+
13
+ [torch]
14
+ include = ["csrc"]
15
+ src = [
16
+ "torch-ext/torch_binding.cpp",
17
+ "torch-ext/torch_binding.h",
18
+ ]
19
+
20
+ [kernel.flashrt_adaptive_norms]
21
+ backend = "cuda"
22
+ depends = ["torch"]
23
+ include = ["csrc"]
24
+ cuda-minver = "12.8"
25
+ src = [
26
+ "csrc/adaptive_norms.cu",
27
+ "csrc/adaptive_norms.cuh",
28
+ ]
build/torch213-cxx11-cu130-aarch64-linux/__init__.py CHANGED
@@ -21,8 +21,8 @@ def _check_adaptive_shapes(
21
  raise RuntimeError("x.shape[1] must be even")
22
  if weight.shape != (dim,):
23
  raise RuntimeError("weight must have shape (dim,)")
24
- if style.shape != (rows, 3 * dim):
25
- raise RuntimeError("style must have shape (rows, 3 * dim)")
26
  if out.shape != x.shape or gate_out.shape != x.shape:
27
  raise RuntimeError("out and gate_out must have the same shape as x")
28
 
@@ -68,7 +68,10 @@ def ada_rms_norm_style_bf16(
68
  out: torch.Tensor | None = None,
69
  gate_out: torch.Tensor | None = None,
70
  ) -> tuple[torch.Tensor, torch.Tensor]:
71
- """Apply RMSNorm, style scale/shift, and return the style gate."""
 
 
 
72
 
73
  if out is None:
74
  out = torch.empty_like(x)
@@ -89,7 +92,10 @@ def gate_residual_ada_norm_fp8_static_bf16(
89
  out: torch.Tensor | None = None,
90
  gate_out: torch.Tensor | None = None,
91
  ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
92
- """Update residual in place, apply AdaRMSNorm, and emit static-scale FP8."""
 
 
 
93
 
94
  if out is None:
95
  out = torch.empty_like(residual, dtype=torch.float8_e4m3fn)
 
21
  raise RuntimeError("x.shape[1] must be even")
22
  if weight.shape != (dim,):
23
  raise RuntimeError("weight must have shape (dim,)")
24
+ if style.shape not in ((rows, 3 * dim), (1, 3 * dim)):
25
+ raise RuntimeError("style must have shape (rows, 3 * dim) or (1, 3 * dim)")
26
  if out.shape != x.shape or gate_out.shape != x.shape:
27
  raise RuntimeError("out and gate_out must have the same shape as x")
28
 
 
68
  out: torch.Tensor | None = None,
69
  gate_out: torch.Tensor | None = None,
70
  ) -> tuple[torch.Tensor, torch.Tensor]:
71
+ """Apply RMSNorm and style modulation.
72
+
73
+ ``style`` may be per-row ``(rows, 3 * dim)`` or broadcast ``(1, 3 * dim)``.
74
+ """
75
 
76
  if out is None:
77
  out = torch.empty_like(x)
 
92
  out: torch.Tensor | None = None,
93
  gate_out: torch.Tensor | None = None,
94
  ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
95
+ """Update residual in place, apply AdaRMSNorm, and emit static-scale FP8.
96
+
97
+ ``style`` may be per-row ``(rows, 3 * dim)`` or broadcast ``(1, 3 * dim)``.
98
+ """
99
 
100
  if out is None:
101
  out = torch.empty_like(residual, dtype=torch.float8_e4m3fn)
build/torch213-cxx11-cu130-aarch64-linux/{flashrt_adaptive_norms_test.abi3.so → _flashrt_adaptive_norms_cuda_503100c.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:de09a9bab79d28c6a9dd109c47c8e5792aa773c5d069455bd8fc808afb4c30cb
3
- size 240872
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:68dbeca9872b314f4228db24b9b1b26464742c2c8ad39406ca30083041e6ead4
3
+ size 240944
build/torch213-cxx11-cu130-aarch64-linux/_ops.py CHANGED
@@ -1,6 +1,6 @@
1
  import torch
2
- from . import flashrt_adaptive_norms_test
3
- ops = torch.ops.flashrt_adaptive_norms_test
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
- return f"flashrt_adaptive_norms_test::{op_name}"
 
1
  import torch
2
+ from . import _flashrt_adaptive_norms_cuda_503100c
3
+ ops = torch.ops._flashrt_adaptive_norms_cuda_503100c
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
+ return f"_flashrt_adaptive_norms_cuda_503100c::{op_name}"
build/torch213-cxx11-cu130-aarch64-linux/metadata.json CHANGED
@@ -1,32 +1,28 @@
1
  {
2
  "name": "flashrt-adaptive-norms",
3
- "id": "flashrt_adaptive_norms_test",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
10
- "11.0"
11
  ]
12
  },
13
  "digest": {
14
  "algorithm": "sha256",
15
  "files": {
16
- "__init__.py": "kv6yHmnnIUpOpDGE4dHU9J32michTE3ygV62h/+D+RI=",
17
- "flashrt_adaptive_norms_test.abi3.so": "3gmpuredKMap3RCcR8jleSqnc8XQaUVb2PyAivtMMMs=",
18
- "_ops.py": "JOw3+PHqZUGoT770CFlNJojgiXwMOTvFMrvsxZXy7Xc=",
19
  "flashrt_adaptive_norms/__init__.py": "v6p5XMfQzddhi1fLSAw4HX9CyS0rQsidvu9VsT01xi4="
20
  }
21
  },
22
  "provenance": {
23
  "kernel": {
24
- "sha": "456d297",
25
  "dirty": false
26
- },
27
- "validation": {
28
- "torch": "2.13.0+cu130",
29
- "cuda": "13.0"
30
  }
31
  }
32
  }
 
1
  {
2
  "name": "flashrt-adaptive-norms",
3
+ "id": "_flashrt_adaptive_norms_cuda_503100c",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
10
+ "11.0a"
11
  ]
12
  },
13
  "digest": {
14
  "algorithm": "sha256",
15
  "files": {
16
+ "__init__.py": "gldeiAjCxeb9fN4IzR7vobgvtJVqN6naBsEaE6KpOyQ=",
17
+ "_flashrt_adaptive_norms_cuda_503100c.abi3.so": "aNvsqYcrMU9CKNskubGyZGR0LCyK05QGyjAIMEHm6tQ=",
18
+ "_ops.py": "3DRost+49dPPYAX9CRnUzvmWa/YXC6pp7hI3m5DvNqY=",
19
  "flashrt_adaptive_norms/__init__.py": "v6p5XMfQzddhi1fLSAw4HX9CyS0rQsidvu9VsT01xi4="
20
  }
21
  },
22
  "provenance": {
23
  "kernel": {
24
+ "sha": "503100c",
25
  "dirty": false
 
 
 
 
26
  }
27
  }
28
  }
csrc/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # csrc
2
+
3
+ CUDA source for adaptive norm Tensor API wrappers.
csrc/adaptive_norms.cu ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // Tensor-facing adaptive norm kernels. The math follows FlashRT
4
+ // csrc/kernels/norm.cu::ada_rms_norm_style and
5
+ // csrc/kernels/fusion.cu::gate_residual_ada_norm_fp8.
6
+
7
+ #include "adaptive_norms.cuh"
8
+
9
+ #include <cuda_bf16.h>
10
+ #include <cuda_fp8.h>
11
+ #include <cuda_runtime.h>
12
+
13
+ namespace flash_rt {
14
+ namespace adaptive_norms {
15
+ namespace {
16
+
17
+ __device__ __forceinline__ float bf16_to_f32(__nv_bfloat16 x) {
18
+ return __bfloat162float(x);
19
+ }
20
+
21
+ __device__ __forceinline__ __nv_bfloat16 f32_to_bf16(float x) {
22
+ return __float2bfloat16(x);
23
+ }
24
+
25
+ __device__ __forceinline__ float block_reduce_sum(float value, float* shared) {
26
+ for (int offset = 16; offset > 0; offset >>= 1) {
27
+ value += __shfl_xor_sync(0xffffffffu, value, offset);
28
+ }
29
+ const int lane = threadIdx.x & 31;
30
+ const int wid = threadIdx.x >> 5;
31
+ if (lane == 0) {
32
+ shared[wid] = value;
33
+ }
34
+ __syncthreads();
35
+ if (wid == 0) {
36
+ value = (lane < ((blockDim.x + 31) >> 5)) ? shared[lane] : 0.0f;
37
+ for (int offset = 16; offset > 0; offset >>= 1) {
38
+ value += __shfl_xor_sync(0xffffffffu, value, offset);
39
+ }
40
+ if (lane == 0) {
41
+ shared[0] = value;
42
+ }
43
+ }
44
+ __syncthreads();
45
+ return shared[0];
46
+ }
47
+
48
+ __global__ void ada_rms_norm_style_bf16_kernel(
49
+ const __nv_bfloat16* __restrict__ x,
50
+ const __nv_bfloat16* __restrict__ weight,
51
+ const __nv_bfloat16* __restrict__ style,
52
+ __nv_bfloat16* __restrict__ out,
53
+ __nv_bfloat16* __restrict__ gate_out,
54
+ int style_rows,
55
+ int dim,
56
+ float eps) {
57
+ const int row = blockIdx.x;
58
+ const int dim2 = dim >> 1;
59
+ const __nv_bfloat162* x2 =
60
+ reinterpret_cast<const __nv_bfloat162*>(x + static_cast<long long>(row) * dim);
61
+ const __nv_bfloat162* w2 = reinterpret_cast<const __nv_bfloat162*>(weight);
62
+ const int style_row_index = style_rows == 1 ? 0 : row;
63
+ const __nv_bfloat16* style_row =
64
+ style + static_cast<long long>(style_row_index) * 3 * dim;
65
+ const __nv_bfloat162* scale2 = reinterpret_cast<const __nv_bfloat162*>(style_row);
66
+ const __nv_bfloat162* shift2 = reinterpret_cast<const __nv_bfloat162*>(style_row + dim);
67
+ const __nv_bfloat162* gate2 = reinterpret_cast<const __nv_bfloat162*>(style_row + 2 * dim);
68
+ __nv_bfloat162* out2 =
69
+ reinterpret_cast<__nv_bfloat162*>(out + static_cast<long long>(row) * dim);
70
+ __nv_bfloat162* gate_out2 =
71
+ reinterpret_cast<__nv_bfloat162*>(gate_out + static_cast<long long>(row) * dim);
72
+
73
+ extern __shared__ float shared[];
74
+ float local_sum = 0.0f;
75
+ for (int i = threadIdx.x; i < dim2; i += blockDim.x) {
76
+ const __nv_bfloat162 xv = x2[i];
77
+ const float x0 = bf16_to_f32(xv.x);
78
+ const float x1 = bf16_to_f32(xv.y);
79
+ local_sum = __fadd_rn(local_sum, __fadd_rn(__fmul_rn(x0, x0), __fmul_rn(x1, x1)));
80
+ }
81
+ const float rms = rsqrtf(block_reduce_sum(local_sum, shared) / static_cast<float>(dim) + eps);
82
+
83
+ for (int i = threadIdx.x; i < dim2; i += blockDim.x) {
84
+ const __nv_bfloat162 xv = x2[i];
85
+ const __nv_bfloat162 wv = w2[i];
86
+ const __nv_bfloat162 sv = scale2[i];
87
+ const __nv_bfloat162 hv = shift2[i];
88
+ const __nv_bfloat162 gv = gate2[i];
89
+ const float n0 = __fmul_rn(__fmul_rn(bf16_to_f32(xv.x), rms), bf16_to_f32(wv.x));
90
+ const float n1 = __fmul_rn(__fmul_rn(bf16_to_f32(xv.y), rms), bf16_to_f32(wv.y));
91
+ const float o0 = __fadd_rn(__fmul_rn(n0, __fadd_rn(1.0f, bf16_to_f32(sv.x))), bf16_to_f32(hv.x));
92
+ const float o1 = __fadd_rn(__fmul_rn(n1, __fadd_rn(1.0f, bf16_to_f32(sv.y))), bf16_to_f32(hv.y));
93
+ out2[i] = __halves2bfloat162(f32_to_bf16(o0), f32_to_bf16(o1));
94
+ gate_out2[i] = gv;
95
+ }
96
+ }
97
+
98
+ __global__ void gate_residual_ada_norm_fp8_static_bf16_kernel(
99
+ __nv_bfloat16* __restrict__ residual,
100
+ const __nv_bfloat16* __restrict__ x,
101
+ const __nv_bfloat16* __restrict__ gate,
102
+ const __nv_bfloat16* __restrict__ weight,
103
+ const __nv_bfloat16* __restrict__ style,
104
+ __nv_fp8_e4m3* __restrict__ out,
105
+ __nv_bfloat16* __restrict__ gate_out,
106
+ int style_rows,
107
+ int dim,
108
+ float eps,
109
+ const float* __restrict__ scale) {
110
+ const int row = blockIdx.x;
111
+ const int dim2 = dim >> 1;
112
+ __nv_bfloat162* residual2 =
113
+ reinterpret_cast<__nv_bfloat162*>(residual + static_cast<long long>(row) * dim);
114
+ const __nv_bfloat162* x2 =
115
+ reinterpret_cast<const __nv_bfloat162*>(x + static_cast<long long>(row) * dim);
116
+ const __nv_bfloat162* input_gate2 =
117
+ reinterpret_cast<const __nv_bfloat162*>(gate + static_cast<long long>(row) * dim);
118
+ const __nv_bfloat162* w2 = reinterpret_cast<const __nv_bfloat162*>(weight);
119
+ const int style_row_index = style_rows == 1 ? 0 : row;
120
+ const __nv_bfloat16* style_row =
121
+ style + static_cast<long long>(style_row_index) * 3 * dim;
122
+ const __nv_bfloat162* style_scale2 = reinterpret_cast<const __nv_bfloat162*>(style_row);
123
+ const __nv_bfloat162* shift2 = reinterpret_cast<const __nv_bfloat162*>(style_row + dim);
124
+ const __nv_bfloat162* style_gate2 = reinterpret_cast<const __nv_bfloat162*>(style_row + 2 * dim);
125
+ __nv_fp8_e4m3* out_row = out + static_cast<long long>(row) * dim;
126
+ __nv_bfloat162* gate_out2 =
127
+ reinterpret_cast<__nv_bfloat162*>(gate_out + static_cast<long long>(row) * dim);
128
+
129
+ extern __shared__ float shared[];
130
+ float local_sum = 0.0f;
131
+ for (int i = threadIdx.x; i < dim2; i += blockDim.x) {
132
+ const __nv_bfloat162 rv = residual2[i];
133
+ const __nv_bfloat162 xv = x2[i];
134
+ const __nv_bfloat162 gv = input_gate2[i];
135
+ const float r0 = __fadd_rn(bf16_to_f32(rv.x), __fmul_rn(bf16_to_f32(xv.x), bf16_to_f32(gv.x)));
136
+ const float r1 = __fadd_rn(bf16_to_f32(rv.y), __fmul_rn(bf16_to_f32(xv.y), bf16_to_f32(gv.y)));
137
+ const __nv_bfloat16 r0_b = f32_to_bf16(r0);
138
+ const __nv_bfloat16 r1_b = f32_to_bf16(r1);
139
+ residual2[i] = __halves2bfloat162(r0_b, r1_b);
140
+ const float rr0 = bf16_to_f32(r0_b);
141
+ const float rr1 = bf16_to_f32(r1_b);
142
+ local_sum = __fadd_rn(local_sum, __fadd_rn(__fmul_rn(rr0, rr0), __fmul_rn(rr1, rr1)));
143
+ }
144
+ const float rms = rsqrtf(block_reduce_sum(local_sum, shared) / static_cast<float>(dim) + eps);
145
+ const float inv_scale = 1.0f / (*scale);
146
+
147
+ for (int i = threadIdx.x; i < dim2; i += blockDim.x) {
148
+ const __nv_bfloat162 rv = residual2[i];
149
+ const __nv_bfloat162 wv = w2[i];
150
+ const __nv_bfloat162 sv = style_scale2[i];
151
+ const __nv_bfloat162 hv = shift2[i];
152
+ const __nv_bfloat162 gv = style_gate2[i];
153
+ const float n0 = __fmul_rn(__fmul_rn(bf16_to_f32(rv.x), rms), bf16_to_f32(wv.x));
154
+ const float n1 = __fmul_rn(__fmul_rn(bf16_to_f32(rv.y), rms), bf16_to_f32(wv.y));
155
+ const float v0 = __fmul_rn(__fadd_rn(__fmul_rn(n0, __fadd_rn(1.0f, bf16_to_f32(sv.x))), bf16_to_f32(hv.x)), inv_scale);
156
+ const float v1 = __fmul_rn(__fadd_rn(__fmul_rn(n1, __fadd_rn(1.0f, bf16_to_f32(sv.y))), bf16_to_f32(hv.y)), inv_scale);
157
+ out_row[2 * i] = __nv_fp8_e4m3(fminf(fmaxf(v0, -448.0f), 448.0f));
158
+ out_row[2 * i + 1] = __nv_fp8_e4m3(fminf(fmaxf(v1, -448.0f), 448.0f));
159
+ gate_out2[i] = gv;
160
+ }
161
+ }
162
+
163
+ } // namespace
164
+
165
+ void ada_rms_norm_style_bf16(
166
+ const void* x,
167
+ const void* weight,
168
+ const void* style,
169
+ void* out,
170
+ void* gate_out,
171
+ int rows,
172
+ int style_rows,
173
+ int dim,
174
+ float eps,
175
+ cudaStream_t stream) {
176
+ if (rows <= 0 || dim <= 0) return;
177
+ ada_rms_norm_style_bf16_kernel<<<rows, 256, 256 * sizeof(float), stream>>>(
178
+ reinterpret_cast<const __nv_bfloat16*>(x),
179
+ reinterpret_cast<const __nv_bfloat16*>(weight),
180
+ reinterpret_cast<const __nv_bfloat16*>(style),
181
+ reinterpret_cast<__nv_bfloat16*>(out),
182
+ reinterpret_cast<__nv_bfloat16*>(gate_out),
183
+ style_rows,
184
+ dim,
185
+ eps);
186
+ }
187
+
188
+ void gate_residual_ada_norm_fp8_static_bf16(
189
+ void* residual,
190
+ const void* x,
191
+ const void* gate,
192
+ const void* weight,
193
+ const void* style,
194
+ void* out,
195
+ void* gate_out,
196
+ int rows,
197
+ int style_rows,
198
+ int dim,
199
+ float eps,
200
+ const float* scale,
201
+ cudaStream_t stream) {
202
+ if (rows <= 0 || dim <= 0) return;
203
+ gate_residual_ada_norm_fp8_static_bf16_kernel<<<rows, 256, 256 * sizeof(float), stream>>>(
204
+ reinterpret_cast<__nv_bfloat16*>(residual),
205
+ reinterpret_cast<const __nv_bfloat16*>(x),
206
+ reinterpret_cast<const __nv_bfloat16*>(gate),
207
+ reinterpret_cast<const __nv_bfloat16*>(weight),
208
+ reinterpret_cast<const __nv_bfloat16*>(style),
209
+ reinterpret_cast<__nv_fp8_e4m3*>(out),
210
+ reinterpret_cast<__nv_bfloat16*>(gate_out),
211
+ style_rows,
212
+ dim,
213
+ eps,
214
+ scale);
215
+ }
216
+
217
+ } // namespace adaptive_norms
218
+ } // namespace flash_rt
csrc/adaptive_norms.cuh ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ #pragma once
3
+
4
+ #include <cuda_runtime.h>
5
+
6
+ namespace flash_rt {
7
+ namespace adaptive_norms {
8
+
9
+ void ada_rms_norm_style_bf16(
10
+ const void* x,
11
+ const void* weight,
12
+ const void* style,
13
+ void* out,
14
+ void* gate_out,
15
+ int rows,
16
+ int style_rows,
17
+ int dim,
18
+ float eps,
19
+ cudaStream_t stream);
20
+
21
+ void gate_residual_ada_norm_fp8_static_bf16(
22
+ void* residual,
23
+ const void* x,
24
+ const void* gate,
25
+ const void* weight,
26
+ const void* style,
27
+ void* out,
28
+ void* gate_out,
29
+ int rows,
30
+ int style_rows,
31
+ int dim,
32
+ float eps,
33
+ const float* scale,
34
+ cudaStream_t stream);
35
+
36
+ } // namespace adaptive_norms
37
+ } // namespace flash_rt
examples/README.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # examples
2
+
3
+ Run the minimal adaptive-norm example after installing `kernels` or after
4
+ adding a built artifact to `PYTHONPATH`:
5
+
6
+ ```bash
7
+ python flashrt-adaptive-norms/examples/adaptive_norms.py
8
+ python flashrt-adaptive-norms/examples/adaptive_norms.py --compile
9
+ ```
examples/adaptive_norms.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Minimal HF Hub-style adaptive norms example."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+
8
+ import torch
9
+
10
+ try:
11
+ from kernels import get_kernel
12
+ except ModuleNotFoundError:
13
+ get_kernel = None
14
+
15
+
16
+ def main() -> None:
17
+ parser = argparse.ArgumentParser()
18
+ parser.add_argument("--repo-id", default="flashrt/flashrt-adaptive-norms")
19
+ parser.add_argument("--version", type=int, default=1)
20
+ parser.add_argument("--rows", type=int, default=2520)
21
+ parser.add_argument("--dim", type=int, default=3072)
22
+ parser.add_argument("--compile", action="store_true")
23
+ args = parser.parse_args()
24
+
25
+ if not torch.cuda.is_available():
26
+ raise SystemExit("CUDA is required")
27
+
28
+ if get_kernel is not None:
29
+ ops = get_kernel(args.repo_id, version=args.version, trust_remote_code=True)
30
+ else:
31
+ import flashrt_adaptive_norms as ops
32
+
33
+ x = torch.randn((args.rows, args.dim), device="cuda", dtype=torch.bfloat16)
34
+ residual = torch.randn_like(x)
35
+ gate = torch.randn_like(x)
36
+ weight = torch.ones((args.dim,), device="cuda", dtype=torch.bfloat16)
37
+ style = torch.randn((args.rows, 3 * args.dim), device="cuda", dtype=torch.bfloat16)
38
+ scale = torch.tensor([0.04], device="cuda", dtype=torch.float32)
39
+
40
+ fn = ops.gate_residual_ada_norm_fp8_static_bf16
41
+ if args.compile:
42
+ fn = torch.compile(fn, fullgraph=True, mode="reduce-overhead")
43
+
44
+ residual, out_fp8, gate_out = fn(residual, x, gate, weight, style, scale)
45
+ torch.cuda.synchronize()
46
+ mode = "torch.compile(fullgraph=True)" if args.compile else "eager"
47
+ print(
48
+ f"{mode} residual={tuple(residual.shape)} out={tuple(out_fp8.shape)} "
49
+ f"gate={tuple(gate_out.shape)} dtype={out_fp8.dtype}"
50
+ )
51
+
52
+
53
+ if __name__ == "__main__":
54
+ main()
flake.lock ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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": 1780582651,
45
+ "narHash": "sha256-WLVlcWJONDeYTw/m4/mbouy0yBqM1LRKGRrKwN6JTJ0=",
46
+ "owner": "huggingface",
47
+ "repo": "kernels",
48
+ "rev": "432702bfbfbb17d3a1bd2c2743d004e21e769ab7",
49
+ "type": "github"
50
+ },
51
+ "original": {
52
+ "owner": "huggingface",
53
+ "repo": "kernels",
54
+ "rev": "432702bfbfbb17d3a1bd2c2743d004e21e769ab7",
55
+ "type": "github"
56
+ }
57
+ },
58
+ "nixpkgs": {
59
+ "locked": {
60
+ "lastModified": 1776927958,
61
+ "narHash": "sha256-XOzEtft7E0P6TgQViLUOQeGHlEYiQ0+FY24BPEksj6s=",
62
+ "owner": "NixOS",
63
+ "repo": "nixpkgs",
64
+ "rev": "fec2c46cca5bf9767486a290abae51200b656d69",
65
+ "type": "github"
66
+ },
67
+ "original": {
68
+ "owner": "NixOS",
69
+ "ref": "nixos-unstable-small",
70
+ "repo": "nixpkgs",
71
+ "type": "github"
72
+ }
73
+ },
74
+ "root": {
75
+ "inputs": {
76
+ "kernel-builder": "kernel-builder"
77
+ }
78
+ },
79
+ "rust-overlay": {
80
+ "inputs": {
81
+ "nixpkgs": [
82
+ "kernel-builder",
83
+ "nixpkgs"
84
+ ]
85
+ },
86
+ "locked": {
87
+ "lastModified": 1776914043,
88
+ "narHash": "sha256-qug5r56yW1qOsjSI99l3Jm15JNT9CvS2otkXNRNtrPI=",
89
+ "owner": "oxalica",
90
+ "repo": "rust-overlay",
91
+ "rev": "2d35c4358d7de3a0e606a6e8b27925d981c01cc3",
92
+ "type": "github"
93
+ },
94
+ "original": {
95
+ "owner": "oxalica",
96
+ "repo": "rust-overlay",
97
+ "type": "github"
98
+ }
99
+ },
100
+ "systems": {
101
+ "locked": {
102
+ "lastModified": 1681028828,
103
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
104
+ "owner": "nix-systems",
105
+ "repo": "default",
106
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
107
+ "type": "github"
108
+ },
109
+ "original": {
110
+ "owner": "nix-systems",
111
+ "repo": "default",
112
+ "type": "github"
113
+ }
114
+ }
115
+ },
116
+ "root": "root",
117
+ "version": 7
118
+ }
flake.nix ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ description = "Flake for FlashRT adaptive norm kernels";
3
+
4
+ inputs = {
5
+ kernel-builder.url = "github:huggingface/kernels/432702bfbfbb17d3a1bd2c2743d004e21e769ab7";
6
+ };
7
+
8
+ outputs =
9
+ {
10
+ self,
11
+ kernel-builder,
12
+ }:
13
+ kernel-builder.lib.genKernelFlakeOutputs {
14
+ inherit self;
15
+ path = ./.;
16
+ };
17
+ }
scripts/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # scripts
2
+
3
+ Package-local sync or audit scripts can live here.
tests/README.md ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # tests
2
+
3
+ Correctness tests for adaptive norm APIs, including BF16 AdaRMSNorm/style and
4
+ fused residual/AdaRMSNorm/static-FP8 output.
tests/test_adaptive_norms.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Correctness tests for flashrt-adaptive-norms."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import ctypes
8
+ import ctypes.util
9
+ import importlib
10
+ import math
11
+ import os
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ import torch
16
+
17
+
18
+ ROOT = Path(__file__).resolve().parents[2]
19
+ PACKAGE = ROOT / "flashrt-adaptive-norms"
20
+ REGISTRATION_INCLUDE = (
21
+ ROOT.parent
22
+ / "kernels"
23
+ / "kernel-builder"
24
+ / "src"
25
+ / "pyproject"
26
+ / "templates"
27
+ / "torch"
28
+ )
29
+
30
+
31
+ class SourceOps:
32
+ def __init__(self, namespace: str) -> None:
33
+ self._ops = getattr(torch.ops, namespace)
34
+
35
+ def ada_rms_norm_style_bf16(self, x, weight, style, eps=1e-6, out=None, gate_out=None):
36
+ if out is None:
37
+ out = torch.empty_like(x)
38
+ if gate_out is None:
39
+ gate_out = torch.empty_like(x)
40
+ self._ops.ada_rms_norm_style_bf16(x, weight, style, float(eps), out, gate_out)
41
+ return out, gate_out
42
+
43
+ def gate_residual_ada_norm_fp8_static_bf16(
44
+ self, residual, x, gate, weight, style, scale, eps=1e-6, out=None, gate_out=None
45
+ ):
46
+ if out is None:
47
+ out = torch.empty_like(residual, dtype=torch.float8_e4m3fn)
48
+ if gate_out is None:
49
+ gate_out = torch.empty_like(residual)
50
+ self._ops.gate_residual_ada_norm_fp8_static_bf16(
51
+ residual, x, gate, weight, style, scale, float(eps), out, gate_out
52
+ )
53
+ return residual, out, gate_out
54
+
55
+
56
+ def _preload_cublaslt() -> None:
57
+ for parent in Path(torch.__file__).resolve().parents:
58
+ candidate = parent / "nvidia" / "cublas" / "lib" / "libcublasLt.so.12"
59
+ if candidate.exists():
60
+ ctypes.CDLL(str(candidate), mode=ctypes.RTLD_GLOBAL)
61
+ return
62
+ library = ctypes.util.find_library("cublasLt")
63
+ if library:
64
+ ctypes.CDLL(library, mode=ctypes.RTLD_GLOBAL)
65
+
66
+
67
+ def _current_arch_list() -> str:
68
+ major, minor = torch.cuda.get_device_capability(0)
69
+ return f"{major}.{minor}"
70
+
71
+
72
+ def load_source_ops() -> SourceOps:
73
+ from torch.utils.cpp_extension import load
74
+
75
+ if not REGISTRATION_INCLUDE.is_dir():
76
+ raise RuntimeError(f"missing kernel-builder registration include: {REGISTRATION_INCLUDE}")
77
+ _preload_cublaslt()
78
+ os.environ.setdefault("TORCH_CUDA_ARCH_LIST", _current_arch_list())
79
+ namespace = "flashrt_adaptive_norms_test"
80
+ load(
81
+ name=namespace,
82
+ sources=[
83
+ str(PACKAGE / "torch-ext" / "torch_binding.cpp"),
84
+ str(PACKAGE / "csrc" / "adaptive_norms.cu"),
85
+ ],
86
+ extra_include_paths=[str(PACKAGE / "csrc"), str(REGISTRATION_INCLUDE)],
87
+ extra_cflags=["-O3", "-DCUDA_KERNEL"],
88
+ extra_cuda_cflags=["-O3", "--expt-relaxed-constexpr", "-DCUDA_KERNEL"],
89
+ verbose=False,
90
+ )
91
+ return SourceOps(namespace)
92
+
93
+
94
+ def load_installed_ops(artifact: str | None):
95
+ if artifact:
96
+ sys.path.insert(0, artifact)
97
+ try:
98
+ return importlib.import_module("flashrt_adaptive_norms")
99
+ finally:
100
+ if artifact:
101
+ sys.path.remove(artifact)
102
+
103
+
104
+ def make_case(rows: int, dim: int):
105
+ x = torch.randn((rows, dim), device="cuda", dtype=torch.bfloat16)
106
+ residual = torch.randn_like(x)
107
+ gate = torch.randn_like(x)
108
+ weight = (1.0 + 0.1 * torch.randn((dim,), device="cuda", dtype=torch.bfloat16)).contiguous()
109
+ style = torch.randn((rows, 3 * dim), device="cuda", dtype=torch.bfloat16) * 0.05
110
+ scale = torch.tensor([0.04], device="cuda", dtype=torch.float32)
111
+ return x, residual, gate, weight, style.contiguous(), scale
112
+
113
+
114
+ def rms_norm(x: torch.Tensor, weight: torch.Tensor, eps: float):
115
+ rms = torch.rsqrt(torch.mean(x.float() * x.float(), dim=-1, keepdim=True) + eps)
116
+ return x.float() * rms * weight.float()
117
+
118
+
119
+ def ref_ada(x, weight, style, eps):
120
+ dim = x.shape[1]
121
+ scale = style[:, :dim].float()
122
+ shift = style[:, dim : 2 * dim].float()
123
+ gate = style[:, 2 * dim :].expand(x.shape[0], -1).contiguous()
124
+ normed = rms_norm(x, weight, eps)
125
+ return (normed * (1.0 + scale) + shift).to(torch.bfloat16), gate.to(torch.bfloat16)
126
+
127
+
128
+ def ref_gate_residual_fp8(residual, x, gate, weight, style, scale, eps):
129
+ updated = (residual.float() + x.float() * gate.float()).to(torch.bfloat16)
130
+ dim = updated.shape[1]
131
+ style_scale = style[:, :dim].float()
132
+ shift = style[:, dim : 2 * dim].float()
133
+ gate_out = (
134
+ style[:, 2 * dim :]
135
+ .expand(updated.shape[0], -1)
136
+ .contiguous()
137
+ .to(torch.bfloat16)
138
+ )
139
+ normed = rms_norm(updated, weight, eps)
140
+ fp8 = ((normed * (1.0 + style_scale) + shift) / scale.float().reshape(())).to(torch.float8_e4m3fn)
141
+ return updated, fp8, gate_out
142
+
143
+
144
+ def percentile(x: torch.Tensor, q: float) -> torch.Tensor:
145
+ flat = x.flatten()
146
+ k = max(1, min(flat.numel(), math.ceil(q * flat.numel())))
147
+ return flat.kthvalue(k).values
148
+
149
+
150
+ def assert_close_distribution(name: str, got: torch.Tensor, expected: torch.Tensor, p99_limit: float, max_limit: float) -> None:
151
+ diff = (got.float() - expected.float()).abs().flatten()
152
+ max_abs = float(diff.max().item())
153
+ mean_abs = float(diff.mean().item())
154
+ p99_abs = float(percentile(diff, 0.99).item())
155
+ cosine = float(torch.nn.functional.cosine_similarity(got.float().flatten(), expected.float().flatten(), dim=0).item())
156
+ if p99_abs > p99_limit or max_abs > max_limit:
157
+ raise AssertionError(
158
+ f"{name} failed: max_abs={max_abs} mean_abs={mean_abs} p99_abs={p99_abs} cosine={cosine}"
159
+ )
160
+ print(
161
+ f"PASS {name}: max_abs={max_abs:.6f} mean_abs={mean_abs:.6f} "
162
+ f"p99_abs={p99_abs:.6f} cosine={cosine:.8f}"
163
+ )
164
+
165
+
166
+ def assert_fp8_boundary_distribution(name: str, got: torch.Tensor, expected: torch.Tensor) -> None:
167
+ diff = (got.float() - expected.float()).abs().flatten()
168
+ nonzero = int((diff != 0).sum().item())
169
+ max_nonzero = max(8, diff.numel() // 100000)
170
+ max_abs = float(diff.max().item())
171
+ mean_abs = float(diff.mean().item())
172
+ p99_abs = float(percentile(diff, 0.99).item())
173
+ cosine = float(torch.nn.functional.cosine_similarity(got.float().flatten(), expected.float().flatten(), dim=0).item())
174
+ if p99_abs > 0.0 or nonzero > max_nonzero:
175
+ raise AssertionError(
176
+ f"{name} failed: max_abs={max_abs} mean_abs={mean_abs} p99_abs={p99_abs} "
177
+ f"nonzero={nonzero}/{diff.numel()} cosine={cosine}"
178
+ )
179
+ print(
180
+ f"PASS {name}: max_abs={max_abs:.6f} mean_abs={mean_abs:.6f} "
181
+ f"p99_abs={p99_abs:.6f} nonzero={nonzero}/{diff.numel()} cosine={cosine:.8f}"
182
+ )
183
+
184
+
185
+ def run_shape(ops, label: str, rows: int, dim: int, eps: float, broadcast: bool = False) -> None:
186
+ x, residual, gate, weight, style, scale = make_case(rows, dim)
187
+ if broadcast:
188
+ style = style[:1].contiguous()
189
+ got, got_gate = ops.ada_rms_norm_style_bf16(x, weight, style, eps)
190
+ exp, exp_gate = ref_ada(x, weight, style, eps)
191
+ assert_close_distribution(f"{label}/ada_out", got, exp, 0.0, 0.015625)
192
+ assert_close_distribution(f"{label}/ada_gate", got_gate, exp_gate, 0.0, 0.0)
193
+
194
+ residual_input = residual.clone()
195
+ got_residual, got_fp8, got_gate2 = ops.gate_residual_ada_norm_fp8_static_bf16(
196
+ residual, x, gate, weight, style, scale, eps
197
+ )
198
+ exp_residual, exp_fp8, exp_gate2 = ref_gate_residual_fp8(
199
+ residual_input, x, gate, weight, style, scale, eps
200
+ )
201
+ assert_close_distribution(f"{label}/fused_residual", got_residual, exp_residual, 0.0, 0.0)
202
+ assert_fp8_boundary_distribution(f"{label}/fused_fp8", got_fp8.float(), exp_fp8.float())
203
+ assert_close_distribution(f"{label}/fused_gate", got_gate2, exp_gate2, 0.0, 0.0)
204
+
205
+
206
+ def run(args) -> None:
207
+ if not torch.cuda.is_available():
208
+ raise SystemExit("CUDA is required")
209
+ torch.manual_seed(47)
210
+ ops = load_source_ops() if args.backend == "source" else load_installed_ops(args.artifact)
211
+ shapes = {
212
+ "small": (64, 1024),
213
+ "vla_2k": (2520, 3072),
214
+ "vla_4k": (4096, 3072),
215
+ }
216
+ if args.mode == "smoke":
217
+ shapes = {"small": shapes["small"]}
218
+ for label, (rows, dim) in shapes.items():
219
+ run_shape(ops, label, rows, dim, args.eps)
220
+ run_shape(ops, f"{label}_broadcast", rows, dim, args.eps, broadcast=True)
221
+
222
+ x, residual, gate, weight, style, scale = make_case(64, 1024)
223
+ style = style[:1].contiguous()
224
+ eager_out, eager_gate = ops.ada_rms_norm_style_bf16(
225
+ x, weight, style, args.eps
226
+ )
227
+ graph_out = torch.empty_like(x)
228
+ graph_gate = torch.empty_like(x)
229
+ graph = torch.cuda.CUDAGraph()
230
+ with torch.cuda.graph(graph):
231
+ ops.ada_rms_norm_style_bf16(
232
+ x, weight, style, args.eps, graph_out, graph_gate
233
+ )
234
+ graph.replay()
235
+ if not torch.equal(graph_out, eager_out) or not torch.equal(
236
+ graph_gate, eager_gate
237
+ ):
238
+ raise AssertionError("broadcast AdaRMS CUDA Graph mismatch")
239
+
240
+ if args.backend == "installed":
241
+ def invoke(value, norm_weight, style_value):
242
+ return ops.ada_rms_norm_style_bf16(
243
+ value, norm_weight, style_value, args.eps
244
+ )
245
+
246
+ compiled_out, compiled_gate = torch.compile(
247
+ invoke, fullgraph=True
248
+ )(x, weight, style)
249
+ if not torch.equal(compiled_out, eager_out) or not torch.equal(
250
+ compiled_gate, eager_gate
251
+ ):
252
+ raise AssertionError("broadcast AdaRMS torch.compile mismatch")
253
+
254
+
255
+ def main() -> None:
256
+ parser = argparse.ArgumentParser()
257
+ parser.add_argument("--backend", choices=["source", "installed"], default="source")
258
+ parser.add_argument("--artifact", default=None)
259
+ parser.add_argument("--mode", choices=["smoke", "full"], default="full")
260
+ parser.add_argument("--eps", type=float, default=1e-6)
261
+ args = parser.parse_args()
262
+ run(args)
263
+
264
+
265
+ if __name__ == "__main__":
266
+ main()
torch-ext/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # torch-ext
2
+
3
+ PyTorch Tensor bindings and Python wrappers for `flashrt-adaptive-norms`.
torch-ext/flashrt_adaptive_norms/__init__.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT adaptive norm kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import torch
6
+
7
+ from ._ops import add_op_namespace_prefix, ops
8
+
9
+
10
+ def _check_adaptive_shapes(
11
+ x: torch.Tensor,
12
+ weight: torch.Tensor,
13
+ style: torch.Tensor,
14
+ out: torch.Tensor,
15
+ gate_out: torch.Tensor,
16
+ ) -> None:
17
+ if x.dim() != 2:
18
+ raise RuntimeError("x must have shape (rows, dim)")
19
+ rows, dim = x.shape
20
+ if dim % 2 != 0:
21
+ raise RuntimeError("x.shape[1] must be even")
22
+ if weight.shape != (dim,):
23
+ raise RuntimeError("weight must have shape (dim,)")
24
+ if style.shape not in ((rows, 3 * dim), (1, 3 * dim)):
25
+ raise RuntimeError("style must have shape (rows, 3 * dim) or (1, 3 * dim)")
26
+ if out.shape != x.shape or gate_out.shape != x.shape:
27
+ raise RuntimeError("out and gate_out must have the same shape as x")
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("ada_rms_norm_style_bf16"))
31
+ def _ada_rms_norm_style_bf16_fake(
32
+ x: torch.Tensor,
33
+ weight: torch.Tensor,
34
+ style: torch.Tensor,
35
+ eps: float,
36
+ out: torch.Tensor,
37
+ gate_out: torch.Tensor,
38
+ ) -> None:
39
+ _check_adaptive_shapes(x, weight, style, out, gate_out)
40
+ return None
41
+
42
+
43
+ @torch.library.register_fake(add_op_namespace_prefix("gate_residual_ada_norm_fp8_static_bf16"))
44
+ def _gate_residual_ada_norm_fp8_static_bf16_fake(
45
+ residual: torch.Tensor,
46
+ x: torch.Tensor,
47
+ gate: torch.Tensor,
48
+ weight: torch.Tensor,
49
+ style: torch.Tensor,
50
+ scale: torch.Tensor,
51
+ eps: float,
52
+ out: torch.Tensor,
53
+ gate_out: torch.Tensor,
54
+ ) -> None:
55
+ _check_adaptive_shapes(residual, weight, style, out, gate_out)
56
+ if x.shape != residual.shape or gate.shape != residual.shape:
57
+ raise RuntimeError("x and gate must have the same shape as residual")
58
+ if scale.numel() != 1:
59
+ raise RuntimeError("scale must be a scalar tensor")
60
+ return None
61
+
62
+
63
+ def ada_rms_norm_style_bf16(
64
+ x: torch.Tensor,
65
+ weight: torch.Tensor,
66
+ style: torch.Tensor,
67
+ eps: float = 1e-6,
68
+ out: torch.Tensor | None = None,
69
+ gate_out: torch.Tensor | None = None,
70
+ ) -> tuple[torch.Tensor, torch.Tensor]:
71
+ """Apply RMSNorm and style modulation.
72
+
73
+ ``style`` may be per-row ``(rows, 3 * dim)`` or broadcast ``(1, 3 * dim)``.
74
+ """
75
+
76
+ if out is None:
77
+ out = torch.empty_like(x)
78
+ if gate_out is None:
79
+ gate_out = torch.empty_like(x)
80
+ ops.ada_rms_norm_style_bf16(x, weight, style, float(eps), out, gate_out)
81
+ return out, gate_out
82
+
83
+
84
+ def gate_residual_ada_norm_fp8_static_bf16(
85
+ residual: torch.Tensor,
86
+ x: torch.Tensor,
87
+ gate: torch.Tensor,
88
+ weight: torch.Tensor,
89
+ style: torch.Tensor,
90
+ scale: torch.Tensor,
91
+ eps: float = 1e-6,
92
+ out: torch.Tensor | None = None,
93
+ gate_out: torch.Tensor | None = None,
94
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
95
+ """Update residual in place, apply AdaRMSNorm, and emit static-scale FP8.
96
+
97
+ ``style`` may be per-row ``(rows, 3 * dim)`` or broadcast ``(1, 3 * dim)``.
98
+ """
99
+
100
+ if out is None:
101
+ out = torch.empty_like(residual, dtype=torch.float8_e4m3fn)
102
+ if gate_out is None:
103
+ gate_out = torch.empty_like(residual)
104
+ ops.gate_residual_ada_norm_fp8_static_bf16(
105
+ residual,
106
+ x,
107
+ gate,
108
+ weight,
109
+ style,
110
+ scale,
111
+ float(eps),
112
+ out,
113
+ gate_out,
114
+ )
115
+ return residual, out, gate_out
116
+
117
+
118
+ __all__ = [
119
+ "ada_rms_norm_style_bf16",
120
+ "gate_residual_ada_norm_fp8_static_bf16",
121
+ ]
torch-ext/torch_binding.cpp ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ #include <torch/all.h>
4
+ #include <torch/library.h>
5
+
6
+ #if defined(CUDA_KERNEL)
7
+ #include <ATen/cuda/CUDAContext.h>
8
+ #include <c10/cuda/CUDAGuard.h>
9
+ #endif
10
+
11
+ #include "adaptive_norms.cuh"
12
+ #include "registration.h"
13
+ #include "torch_binding.h"
14
+
15
+ namespace {
16
+
17
+ void check_cuda_contiguous(torch::Tensor const& tensor, const char* name) {
18
+ TORCH_CHECK(tensor.is_cuda(), name, " must be a CUDA tensor");
19
+ TORCH_CHECK(tensor.is_contiguous(), name, " must be contiguous");
20
+ }
21
+
22
+ void check_bf16(torch::Tensor const& tensor, const char* name) {
23
+ check_cuda_contiguous(tensor, name);
24
+ TORCH_CHECK(tensor.scalar_type() == torch::kBFloat16,
25
+ name, " must have dtype torch.bfloat16");
26
+ }
27
+
28
+ void check_fp32(torch::Tensor const& tensor, const char* name) {
29
+ check_cuda_contiguous(tensor, name);
30
+ TORCH_CHECK(tensor.scalar_type() == torch::kFloat32,
31
+ name, " must have dtype torch.float32");
32
+ }
33
+
34
+ void check_fp8(torch::Tensor const& tensor, const char* name) {
35
+ check_cuda_contiguous(tensor, name);
36
+ TORCH_CHECK(tensor.scalar_type() == torch::kFloat8_e4m3fn,
37
+ name, " must have dtype torch.float8_e4m3fn");
38
+ }
39
+
40
+ void check_same_device(torch::Tensor const& a,
41
+ torch::Tensor const& b,
42
+ const char* a_name,
43
+ const char* b_name) {
44
+ TORCH_CHECK(a.get_device() == b.get_device(),
45
+ a_name, " and ", b_name, " must be on the same CUDA device");
46
+ }
47
+
48
+ void check_matrix(torch::Tensor const& tensor, const char* name) {
49
+ check_bf16(tensor, name);
50
+ TORCH_CHECK(tensor.dim() == 2, name, " must have shape (rows, dim)");
51
+ TORCH_CHECK(tensor.size(0) > 0 && tensor.size(1) > 0,
52
+ name, " rows and dim must be positive");
53
+ TORCH_CHECK((tensor.size(1) % 2) == 0, name, ".shape[1] must be even");
54
+ }
55
+
56
+ void check_common(
57
+ torch::Tensor const& x,
58
+ torch::Tensor const& weight,
59
+ torch::Tensor const& style,
60
+ torch::Tensor const& out,
61
+ torch::Tensor const& gate_out) {
62
+ check_matrix(x, "x");
63
+ check_bf16(weight, "weight");
64
+ check_bf16(style, "style");
65
+ check_bf16(gate_out, "gate_out");
66
+ const int64_t rows = x.size(0);
67
+ const int64_t dim = x.size(1);
68
+ TORCH_CHECK(weight.dim() == 1 && weight.size(0) == dim,
69
+ "weight must have shape (dim,)");
70
+ TORCH_CHECK(style.dim() == 2 && style.size(1) == 3 * dim &&
71
+ (style.size(0) == 1 || style.size(0) == rows),
72
+ "style must have shape (rows, 3 * dim) or (1, 3 * dim)");
73
+ TORCH_CHECK(gate_out.sizes() == x.sizes(),
74
+ "gate_out must have the same shape as x");
75
+ check_same_device(x, weight, "x", "weight");
76
+ check_same_device(x, style, "x", "style");
77
+ check_same_device(x, out, "x", "out");
78
+ check_same_device(x, gate_out, "x", "gate_out");
79
+ }
80
+
81
+ } // namespace
82
+
83
+ void ada_rms_norm_style_bf16(
84
+ torch::Tensor const& x,
85
+ torch::Tensor const& weight,
86
+ torch::Tensor const& style,
87
+ double eps,
88
+ torch::Tensor& out,
89
+ torch::Tensor& gate_out) {
90
+ check_bf16(out, "out");
91
+ check_common(x, weight, style, out, gate_out);
92
+ TORCH_CHECK(out.sizes() == x.sizes(), "out must have the same shape as x");
93
+
94
+ #if defined(CUDA_KERNEL)
95
+ at::cuda::CUDAGuard device_guard(x.device());
96
+ auto stream = at::cuda::getCurrentCUDAStream(x.get_device()).stream();
97
+ flash_rt::adaptive_norms::ada_rms_norm_style_bf16(
98
+ x.data_ptr(),
99
+ weight.data_ptr(),
100
+ style.data_ptr(),
101
+ out.data_ptr(),
102
+ gate_out.data_ptr(),
103
+ static_cast<int>(x.size(0)),
104
+ static_cast<int>(style.size(0)),
105
+ static_cast<int>(x.size(1)),
106
+ static_cast<float>(eps),
107
+ stream);
108
+ #else
109
+ TORCH_CHECK(false, "flashrt-adaptive-norms was not built with CUDA support");
110
+ #endif
111
+ }
112
+
113
+ void gate_residual_ada_norm_fp8_static_bf16(
114
+ torch::Tensor& residual,
115
+ torch::Tensor const& x,
116
+ torch::Tensor const& gate,
117
+ torch::Tensor const& weight,
118
+ torch::Tensor const& style,
119
+ torch::Tensor const& scale,
120
+ double eps,
121
+ torch::Tensor& out,
122
+ torch::Tensor& gate_out) {
123
+ check_matrix(residual, "residual");
124
+ check_matrix(x, "x");
125
+ check_bf16(gate, "gate");
126
+ check_fp32(scale, "scale");
127
+ check_fp8(out, "out");
128
+ check_common(residual, weight, style, out, gate_out);
129
+ TORCH_CHECK(x.sizes() == residual.sizes(), "x must have the same shape as residual");
130
+ TORCH_CHECK(gate.sizes() == residual.sizes(), "gate must have the same shape as residual");
131
+ TORCH_CHECK(out.sizes() == residual.sizes(), "out must have the same shape as residual");
132
+ TORCH_CHECK(scale.numel() == 1, "scale must be a scalar tensor");
133
+ check_same_device(residual, x, "residual", "x");
134
+ check_same_device(residual, gate, "residual", "gate");
135
+ check_same_device(residual, scale, "residual", "scale");
136
+
137
+ #if defined(CUDA_KERNEL)
138
+ at::cuda::CUDAGuard device_guard(residual.device());
139
+ auto stream = at::cuda::getCurrentCUDAStream(residual.get_device()).stream();
140
+ flash_rt::adaptive_norms::gate_residual_ada_norm_fp8_static_bf16(
141
+ residual.data_ptr(),
142
+ x.data_ptr(),
143
+ gate.data_ptr(),
144
+ weight.data_ptr(),
145
+ style.data_ptr(),
146
+ out.data_ptr(),
147
+ gate_out.data_ptr(),
148
+ static_cast<int>(residual.size(0)),
149
+ static_cast<int>(style.size(0)),
150
+ static_cast<int>(residual.size(1)),
151
+ static_cast<float>(eps),
152
+ reinterpret_cast<const float*>(scale.data_ptr()),
153
+ stream);
154
+ #else
155
+ TORCH_CHECK(false, "flashrt-adaptive-norms was not built with CUDA support");
156
+ #endif
157
+ }
158
+
159
+ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) {
160
+ ops.def("ada_rms_norm_style_bf16("
161
+ "Tensor x, Tensor weight, Tensor style, float eps, Tensor! out, Tensor! gate_out) -> ()");
162
+ ops.def("gate_residual_ada_norm_fp8_static_bf16("
163
+ "Tensor! residual, Tensor x, Tensor gate, Tensor weight, Tensor style, Tensor scale, "
164
+ "float eps, Tensor! out, Tensor! gate_out) -> ()");
165
+ #if defined(CUDA_KERNEL)
166
+ ops.impl("ada_rms_norm_style_bf16",
167
+ torch::kCUDA,
168
+ &ada_rms_norm_style_bf16);
169
+ ops.impl("gate_residual_ada_norm_fp8_static_bf16",
170
+ torch::kCUDA,
171
+ &gate_residual_ada_norm_fp8_static_bf16);
172
+ #endif
173
+ }
174
+
175
+ REGISTER_EXTENSION(TORCH_EXTENSION_NAME)
torch-ext/torch_binding.h ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ #pragma once
3
+
4
+ #include <torch/all.h>
5
+
6
+ void ada_rms_norm_style_bf16(
7
+ torch::Tensor const& x,
8
+ torch::Tensor const& weight,
9
+ torch::Tensor const& style,
10
+ double eps,
11
+ torch::Tensor& out,
12
+ torch::Tensor& gate_out);
13
+
14
+ void gate_residual_ada_norm_fp8_static_bf16(
15
+ torch::Tensor& residual,
16
+ torch::Tensor const& x,
17
+ torch::Tensor const& gate,
18
+ torch::Tensor const& weight,
19
+ torch::Tensor const& style,
20
+ torch::Tensor const& scale,
21
+ double eps,
22
+ torch::Tensor& out,
23
+ torch::Tensor& gate_out);