liangsu9988 commited on
Commit
56405b8
·
verified ·
1 Parent(s): 0d0feba

Uploaded using `kernel-builder`.

Browse files
Files changed (31) hide show
  1. benchmarks/benchmark.py +686 -0
  2. build/torch210-cxx11-cu128-x86_64-linux/__init__.py +496 -0
  3. build/torch210-cxx11-cu128-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so +3 -0
  4. build/torch210-cxx11-cu128-x86_64-linux/_ops.py +9 -0
  5. build/torch210-cxx11-cu128-x86_64-linux/flashrt_qkv_cache_rope/__init__.py +26 -0
  6. build/torch210-cxx11-cu128-x86_64-linux/metadata.json +23 -0
  7. build/torch210-cxx11-cu130-x86_64-linux/__init__.py +496 -0
  8. build/torch210-cxx11-cu130-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so +3 -0
  9. build/torch210-cxx11-cu130-x86_64-linux/_ops.py +9 -0
  10. build/torch210-cxx11-cu130-x86_64-linux/flashrt_qkv_cache_rope/__init__.py +26 -0
  11. build/torch210-cxx11-cu130-x86_64-linux/metadata.json +21 -0
  12. build/torch211-cxx11-cu128-x86_64-linux/__init__.py +496 -0
  13. build/torch211-cxx11-cu128-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so +3 -0
  14. build/torch211-cxx11-cu128-x86_64-linux/_ops.py +9 -0
  15. build/torch211-cxx11-cu128-x86_64-linux/flashrt_qkv_cache_rope/__init__.py +26 -0
  16. build/torch211-cxx11-cu128-x86_64-linux/metadata.json +23 -0
  17. build/torch211-cxx11-cu130-x86_64-linux/__init__.py +496 -0
  18. build/torch211-cxx11-cu130-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so +3 -0
  19. build/torch211-cxx11-cu130-x86_64-linux/_ops.py +9 -0
  20. build/torch211-cxx11-cu130-x86_64-linux/flashrt_qkv_cache_rope/__init__.py +26 -0
  21. build/torch211-cxx11-cu130-x86_64-linux/metadata.json +21 -0
  22. build/torch212-cxx11-cu130-x86_64-linux/__init__.py +496 -0
  23. build/torch212-cxx11-cu130-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so +3 -0
  24. build/torch212-cxx11-cu130-x86_64-linux/_ops.py +9 -0
  25. build/torch212-cxx11-cu130-x86_64-linux/flashrt_qkv_cache_rope/__init__.py +26 -0
  26. build/torch212-cxx11-cu130-x86_64-linux/metadata.json +21 -0
  27. build/torch212-cxx11-cu132-x86_64-linux/__init__.py +496 -0
  28. build/torch212-cxx11-cu132-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so +3 -0
  29. build/torch212-cxx11-cu132-x86_64-linux/_ops.py +9 -0
  30. build/torch212-cxx11-cu132-x86_64-linux/flashrt_qkv_cache_rope/__init__.py +26 -0
  31. build/torch212-cxx11-cu132-x86_64-linux/metadata.json +21 -0
benchmarks/benchmark.py ADDED
@@ -0,0 +1,686 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Benchmark flashrt-qkv-cache-rope against a PyTorch eager postprocess chain."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import ctypes
8
+ import ctypes.util
9
+ import importlib
10
+ import json
11
+ import math
12
+ import os
13
+ import sys
14
+ from dataclasses import asdict, dataclass
15
+ from pathlib import Path
16
+
17
+ import torch
18
+
19
+
20
+ ROOT = Path(__file__).resolve().parents[2]
21
+ PACKAGE = ROOT / "flashrt-qkv-cache-rope"
22
+ REGISTRATION_INCLUDE = (
23
+ ROOT.parent
24
+ / "kernels"
25
+ / "kernel-builder"
26
+ / "src"
27
+ / "pyproject"
28
+ / "templates"
29
+ / "torch"
30
+ )
31
+
32
+ SHAPES = {
33
+ "small": (1, 64, 8, 128),
34
+ "wan_1k": (1, 1024, 24, 128),
35
+ "wan_2520": (1, 2520, 24, 128),
36
+ "wan_4096": (1, 4096, 24, 128),
37
+ "vl_512": (1, 512, 16, 128),
38
+ }
39
+ SHAPE_GROUPS = {
40
+ "smoke": ["small"],
41
+ "headline": ["wan_1k", "wan_2520", "vl_512"],
42
+ "all": list(SHAPES.keys()),
43
+ }
44
+
45
+
46
+ @dataclass
47
+ class Result:
48
+ shape: str
49
+ batch: int
50
+ seq_len: int
51
+ heads: int
52
+ head_dim: int
53
+ flashrt_us: float
54
+ torch_eager_us: float
55
+ speedup_vs_eager: float
56
+ q_p99_abs: float
57
+ k_p99_abs: float
58
+ q_cosine: float
59
+ k_cosine: float
60
+ status: str
61
+
62
+
63
+ class SourceOps:
64
+ def __init__(self, namespace: str) -> None:
65
+ self._ops = getattr(torch.ops, namespace)
66
+
67
+ def decode_q_norm_rope_stage_bf16(self, q_pre, q_w, cos, sin, eps=1e-6, q_out=None):
68
+ if q_out is None:
69
+ q_out = torch.empty_like(q_pre)
70
+ self._ops.decode_q_norm_rope_stage_bf16(q_pre, q_w, cos, sin, float(eps), q_out)
71
+ return q_out
72
+
73
+ def decode_k_norm_rope_kvwrite_bf16(self, k_pre, v_pre, k_w, cos, sin, eps=1e-6, k_out=None, v_out=None):
74
+ if k_out is None:
75
+ k_out = torch.empty_like(k_pre)
76
+ if v_out is None:
77
+ v_out = torch.empty_like(v_pre)
78
+ self._ops.decode_k_norm_rope_kvwrite_bf16(k_pre, v_pre, k_w, cos, sin, float(eps), k_out, v_out)
79
+ return k_out, v_out
80
+
81
+ def decode_k_norm_rope_kvwrite_devpos_bf16(self, k_pre, v_pre, k_w, cos, sin, cur_pos, k_cache, v_cache, eps=1e-6):
82
+ self._ops.decode_k_norm_rope_kvwrite_devpos_bf16(k_pre, v_pre, k_w, cos, sin, cur_pos, float(eps), k_cache, v_cache)
83
+ return k_cache, v_cache
84
+
85
+ def qkv_split_norm_rope_bf16(
86
+ self, packed, q_w, k_w, freqs_re, freqs_im, heads, head_dim, rope_seq_len=None, eps=1e-6, q_out=None, k_out=None
87
+ ):
88
+ if rope_seq_len is None:
89
+ rope_seq_len = packed.shape[1]
90
+ if q_out is None:
91
+ q_out = torch.empty((packed.shape[0], packed.shape[1], heads, head_dim), device=packed.device, dtype=torch.bfloat16)
92
+ if k_out is None:
93
+ k_out = torch.empty_like(q_out)
94
+ self._ops.qkv_split_norm_rope_bf16(
95
+ packed, q_w, k_w, freqs_re, freqs_im, int(heads), int(head_dim),
96
+ int(rope_seq_len), float(eps), q_out, k_out
97
+ )
98
+ return q_out, k_out
99
+
100
+ def qkv_split_joint3_cat_bf16(
101
+ self,
102
+ packed_v,
103
+ qkv_v_bias,
104
+ norm_v_q_weight,
105
+ norm_v_k_weight,
106
+ freqs_re,
107
+ freqs_im,
108
+ packed_a,
109
+ norm_a_q_weight,
110
+ norm_a_k_weight,
111
+ packed_u,
112
+ norm_u_q_weight,
113
+ norm_u_k_weight,
114
+ heads,
115
+ head_dim,
116
+ q_cat_out,
117
+ k_cat_out,
118
+ v_cat_out,
119
+ rope_seq_len=None,
120
+ eps_v=1e-6,
121
+ eps_a=1e-6,
122
+ eps_u=1e-6,
123
+ ):
124
+ if rope_seq_len is None:
125
+ rope_seq_len = packed_v.shape[1]
126
+ self._ops.qkv_split_joint3_cat_bf16(
127
+ packed_v,
128
+ qkv_v_bias,
129
+ norm_v_q_weight,
130
+ norm_v_k_weight,
131
+ freqs_re,
132
+ freqs_im,
133
+ packed_a,
134
+ norm_a_q_weight,
135
+ norm_a_k_weight,
136
+ packed_u,
137
+ norm_u_q_weight,
138
+ norm_u_k_weight,
139
+ int(heads),
140
+ int(head_dim),
141
+ int(rope_seq_len),
142
+ float(eps_v),
143
+ float(eps_a),
144
+ float(eps_u),
145
+ q_cat_out,
146
+ k_cat_out,
147
+ v_cat_out,
148
+ )
149
+ return q_cat_out, k_cat_out, v_cat_out
150
+
151
+
152
+ def _preload_cublaslt() -> None:
153
+ for parent in Path(torch.__file__).resolve().parents:
154
+ candidate = parent / "nvidia" / "cublas" / "lib" / "libcublasLt.so.12"
155
+ if candidate.exists():
156
+ ctypes.CDLL(str(candidate), mode=ctypes.RTLD_GLOBAL)
157
+ return
158
+ library = ctypes.util.find_library("cublasLt")
159
+ if library:
160
+ ctypes.CDLL(library, mode=ctypes.RTLD_GLOBAL)
161
+
162
+
163
+ def _current_arch_list() -> str:
164
+ major, minor = torch.cuda.get_device_capability(0)
165
+ return f"{major}.{minor}"
166
+
167
+
168
+ def load_source_ops() -> SourceOps:
169
+ from torch.utils.cpp_extension import load
170
+
171
+ if not REGISTRATION_INCLUDE.is_dir():
172
+ raise RuntimeError(f"missing kernel-builder registration include: {REGISTRATION_INCLUDE}")
173
+ _preload_cublaslt()
174
+ os.environ.setdefault("TORCH_CUDA_ARCH_LIST", _current_arch_list())
175
+ namespace = "flashrt_qkv_cache_rope_benchmark"
176
+ load(
177
+ name=namespace,
178
+ sources=[
179
+ str(PACKAGE / "torch-ext" / "torch_binding.cpp"),
180
+ str(PACKAGE / "csrc" / "qkv_cache_rope.cu"),
181
+ ],
182
+ extra_include_paths=[str(PACKAGE / "csrc"), str(REGISTRATION_INCLUDE)],
183
+ extra_cflags=["-O3", "-DCUDA_KERNEL"],
184
+ extra_cuda_cflags=["-O3", "--expt-relaxed-constexpr", "-DCUDA_KERNEL"],
185
+ verbose=False,
186
+ )
187
+ return SourceOps(namespace)
188
+
189
+
190
+ def load_installed_ops(artifact: str | None):
191
+ if artifact:
192
+ sys.path.insert(0, artifact)
193
+ try:
194
+ return importlib.import_module("flashrt_qkv_cache_rope")
195
+ finally:
196
+ if artifact:
197
+ sys.path.remove(artifact)
198
+
199
+
200
+ def make_freqs(seq_len: int, head_dim: int):
201
+ theta = torch.randn((seq_len, head_dim // 2), device="cuda", dtype=torch.float32)
202
+ return torch.cos(theta).contiguous(), torch.sin(theta).contiguous()
203
+
204
+
205
+ def make_case(batch: int, seq_len: int, heads: int, head_dim: int):
206
+ dim = heads * head_dim
207
+ packed = torch.randn((batch, seq_len, 3 * dim), device="cuda", dtype=torch.bfloat16)
208
+ q_w = (1.0 + 0.1 * torch.randn((dim,), device="cuda", dtype=torch.bfloat16)).contiguous()
209
+ k_w = (1.0 + 0.1 * torch.randn((dim,), device="cuda", dtype=torch.bfloat16)).contiguous()
210
+ freqs_re, freqs_im = make_freqs(seq_len, head_dim)
211
+ q_out = torch.empty((batch, seq_len, heads, head_dim), device="cuda", dtype=torch.bfloat16)
212
+ k_out = torch.empty_like(q_out)
213
+ return packed, q_w, k_w, freqs_re, freqs_im, q_out, k_out
214
+
215
+
216
+ def make_decode_case(heads: int):
217
+ q = torch.randn((heads, 128), device="cuda", dtype=torch.bfloat16)
218
+ k = torch.randn((heads, 128), device="cuda", dtype=torch.bfloat16)
219
+ v = torch.randn((heads, 128), device="cuda", dtype=torch.bfloat16)
220
+ q_w = (1.0 + 0.1 * torch.randn((128,), device="cuda", dtype=torch.bfloat16)).contiguous()
221
+ k_w = (1.0 + 0.1 * torch.randn((128,), device="cuda", dtype=torch.bfloat16)).contiguous()
222
+ theta = torch.randn((64,), device="cuda", dtype=torch.float32)
223
+ cos = torch.cos(theta).to(torch.bfloat16).contiguous()
224
+ sin = torch.sin(theta).to(torch.bfloat16).contiguous()
225
+ return q, k, v, q_w, k_w, cos, sin
226
+
227
+
228
+ def rms_norm(x: torch.Tensor, weight: torch.Tensor, eps: float):
229
+ rms = torch.rsqrt(torch.mean(x.float() * x.float(), dim=-1, keepdim=True) + eps)
230
+ return x.float() * rms * weight.float()
231
+
232
+
233
+ def apply_pair_rope(x: torch.Tensor, freqs_re: torch.Tensor, freqs_im: torch.Tensor):
234
+ batch, seq_len, heads, head_dim = x.shape
235
+ pair = x.float().reshape(batch, seq_len, heads, head_dim // 2, 2)
236
+ re = pair[..., 0]
237
+ im = pair[..., 1]
238
+ fr = freqs_re.view(1, seq_len, 1, head_dim // 2)
239
+ fi = freqs_im.view(1, seq_len, 1, head_dim // 2)
240
+ out = torch.empty_like(pair.float())
241
+ out[..., 0] = re * fr - im * fi
242
+ out[..., 1] = re * fi + im * fr
243
+ return out.reshape(batch, seq_len, heads, head_dim).to(torch.bfloat16)
244
+
245
+
246
+ def apply_rotate_half_rope_128(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor):
247
+ xf = x.float()
248
+ out = torch.empty_like(xf)
249
+ c = cos.float().view(1, 64)
250
+ s = sin.float().view(1, 64)
251
+ out[:, :64] = xf[:, :64] * c - xf[:, 64:] * s
252
+ out[:, 64:] = xf[:, 64:] * c + xf[:, :64] * s
253
+ return out.to(torch.bfloat16)
254
+
255
+
256
+ def torch_ref(packed, q_w, k_w, freqs_re, freqs_im, heads, head_dim, eps):
257
+ batch, seq_len, _ = packed.shape
258
+ dim = heads * head_dim
259
+ q = packed[:, :, :dim]
260
+ k = packed[:, :, dim : 2 * dim]
261
+ qn = rms_norm(q, q_w, eps).to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
262
+ kn = rms_norm(k, k_w, eps).to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
263
+ return apply_pair_rope(qn, freqs_re, freqs_im), apply_pair_rope(kn, freqs_re, freqs_im)
264
+
265
+
266
+ def torch_ref_bias(packed, qkv_bias, q_w, k_w, freqs_re, freqs_im, heads, head_dim, eps):
267
+ batch, seq_len, _ = packed.shape
268
+ dim = heads * head_dim
269
+ biased = packed.float() + qkv_bias.float().view(1, 1, 3 * dim)
270
+ q = biased[:, :, :dim]
271
+ k = biased[:, :, dim : 2 * dim]
272
+ v = biased[:, :, 2 * dim :].to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
273
+ qn = rms_norm(q, q_w, eps).to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
274
+ kn = rms_norm(k, k_w, eps).to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
275
+ return apply_pair_rope(qn, freqs_re, freqs_im), apply_pair_rope(kn, freqs_re, freqs_im), v
276
+
277
+
278
+ def torch_ref_no_rope(packed, q_w, k_w, heads, head_dim, eps):
279
+ batch, seq_len, _ = packed.shape
280
+ dim = heads * head_dim
281
+ q = packed[:, :, :dim]
282
+ k = packed[:, :, dim : 2 * dim]
283
+ v = packed[:, :, 2 * dim :].view(batch, seq_len, heads, head_dim)
284
+ qn = rms_norm(q, q_w, eps).to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
285
+ kn = rms_norm(k, k_w, eps).to(torch.bfloat16).view(batch, seq_len, heads, head_dim)
286
+ return qn, kn, v
287
+
288
+
289
+ def torch_ref_decode(x, weight, cos, sin, eps):
290
+ return apply_rotate_half_rope_128(rms_norm(x, weight, eps).to(torch.bfloat16), cos, sin)
291
+
292
+
293
+ def make_joint3_case(video_len: int, action_len: int, und_len: int, heads: int, head_dim: int):
294
+ packed_v, v_q_w, v_k_w, freqs_re, freqs_im, _, _ = make_case(1, video_len, heads, head_dim)
295
+ packed_a, a_q_w, a_k_w, _, _, _, _ = make_case(1, action_len, heads, head_dim)
296
+ packed_u, u_q_w, u_k_w, _, _, _, _ = make_case(1, und_len, heads, head_dim)
297
+ dim = heads * head_dim
298
+ qkv_v_bias = (0.02 * torch.randn((3 * dim,), device="cuda", dtype=torch.bfloat16)).contiguous()
299
+ total = video_len + action_len + und_len
300
+ q_cat = torch.empty((1, total, heads, head_dim), device="cuda", dtype=torch.bfloat16)
301
+ k_cat = torch.empty_like(q_cat)
302
+ v_cat = torch.empty_like(q_cat)
303
+ return (
304
+ packed_v,
305
+ qkv_v_bias,
306
+ v_q_w,
307
+ v_k_w,
308
+ freqs_re,
309
+ freqs_im,
310
+ packed_a,
311
+ a_q_w,
312
+ a_k_w,
313
+ packed_u,
314
+ u_q_w,
315
+ u_k_w,
316
+ q_cat,
317
+ k_cat,
318
+ v_cat,
319
+ )
320
+
321
+
322
+ def torch_ref_joint3(
323
+ packed_v,
324
+ qkv_v_bias,
325
+ v_q_w,
326
+ v_k_w,
327
+ freqs_re,
328
+ freqs_im,
329
+ packed_a,
330
+ a_q_w,
331
+ a_k_w,
332
+ packed_u,
333
+ u_q_w,
334
+ u_k_w,
335
+ heads,
336
+ head_dim,
337
+ eps,
338
+ ):
339
+ qv, kv, vv = torch_ref_bias(packed_v, qkv_v_bias, v_q_w, v_k_w, freqs_re, freqs_im, heads, head_dim, eps)
340
+ qa, ka, va = torch_ref_no_rope(packed_a, a_q_w, a_k_w, heads, head_dim, eps)
341
+ qu, ku, vu = torch_ref_no_rope(packed_u, u_q_w, u_k_w, heads, head_dim, eps)
342
+ return torch.cat([qv, qa, qu], dim=1), torch.cat([kv, ka, ku], dim=1), torch.cat([vv, va, vu], dim=1)
343
+
344
+
345
+ def time_us(fn, warmup: int, iters: int) -> float:
346
+ for _ in range(warmup):
347
+ fn()
348
+ torch.cuda.synchronize()
349
+ start = torch.cuda.Event(enable_timing=True)
350
+ end = torch.cuda.Event(enable_timing=True)
351
+ start.record()
352
+ for _ in range(iters):
353
+ fn()
354
+ end.record()
355
+ torch.cuda.synchronize()
356
+ return start.elapsed_time(end) * 1000.0 / iters
357
+
358
+
359
+ def percentile(x: torch.Tensor, q: float) -> torch.Tensor:
360
+ flat = x.flatten()
361
+ k = max(1, min(flat.numel(), math.ceil(q * flat.numel())))
362
+ return flat.kthvalue(k).values
363
+
364
+
365
+ def metrics(got, expected):
366
+ diff = (got.float() - expected.float()).abs().flatten()
367
+ return float(percentile(diff, 0.99).item()), float(
368
+ torch.nn.functional.cosine_similarity(got.float().flatten(), expected.float().flatten(), dim=0).item()
369
+ )
370
+
371
+
372
+ def run_one(ops, name: str, shape: tuple[int, int, int, int], args) -> Result:
373
+ batch, seq_len, heads, head_dim = shape
374
+ packed, q_w, k_w, freqs_re, freqs_im, q_out, k_out = make_case(*shape)
375
+ eps = args.eps
376
+ got_q, got_k = ops.qkv_split_norm_rope_bf16(
377
+ packed, q_w, k_w, freqs_re, freqs_im, heads, head_dim, seq_len, eps, q_out, k_out
378
+ )
379
+ exp_q, exp_k = torch_ref(packed, q_w, k_w, freqs_re, freqs_im, heads, head_dim, eps)
380
+ q_p99, q_cos = metrics(got_q, exp_q)
381
+ k_p99, k_cos = metrics(got_k, exp_k)
382
+ flashrt_us = time_us(
383
+ lambda: ops.qkv_split_norm_rope_bf16(
384
+ packed, q_w, k_w, freqs_re, freqs_im, heads, head_dim, seq_len, eps, q_out, k_out
385
+ ),
386
+ args.warmup,
387
+ args.iters,
388
+ )
389
+ eager_us = time_us(
390
+ lambda: torch_ref(packed, q_w, k_w, freqs_re, freqs_im, heads, head_dim, eps),
391
+ args.warmup,
392
+ args.iters,
393
+ )
394
+ status = "PASS" if q_p99 <= args.p99_abs_limit and k_p99 <= args.p99_abs_limit else "FAIL"
395
+ return Result(
396
+ shape=name,
397
+ batch=batch,
398
+ seq_len=seq_len,
399
+ heads=heads,
400
+ head_dim=head_dim,
401
+ flashrt_us=flashrt_us,
402
+ torch_eager_us=eager_us,
403
+ speedup_vs_eager=eager_us / flashrt_us,
404
+ q_p99_abs=q_p99,
405
+ k_p99_abs=k_p99,
406
+ q_cosine=q_cos,
407
+ k_cosine=k_cos,
408
+ status=status,
409
+ )
410
+
411
+
412
+ def run_joint3(ops, name: str, video_len: int, action_len: int, und_len: int, heads: int, head_dim: int, args) -> Result:
413
+ case = make_joint3_case(video_len, action_len, und_len, heads, head_dim)
414
+ (
415
+ packed_v,
416
+ qkv_v_bias,
417
+ v_q_w,
418
+ v_k_w,
419
+ freqs_re,
420
+ freqs_im,
421
+ packed_a,
422
+ a_q_w,
423
+ a_k_w,
424
+ packed_u,
425
+ u_q_w,
426
+ u_k_w,
427
+ q_cat,
428
+ k_cat,
429
+ v_cat,
430
+ ) = case
431
+ eps = args.eps
432
+ got_q, got_k, _ = ops.qkv_split_joint3_cat_bf16(
433
+ packed_v,
434
+ qkv_v_bias,
435
+ v_q_w,
436
+ v_k_w,
437
+ freqs_re,
438
+ freqs_im,
439
+ packed_a,
440
+ a_q_w,
441
+ a_k_w,
442
+ packed_u,
443
+ u_q_w,
444
+ u_k_w,
445
+ heads,
446
+ head_dim,
447
+ q_cat,
448
+ k_cat,
449
+ v_cat,
450
+ video_len,
451
+ eps,
452
+ eps,
453
+ eps,
454
+ )
455
+ exp_q, exp_k, _ = torch_ref_joint3(
456
+ packed_v,
457
+ qkv_v_bias,
458
+ v_q_w,
459
+ v_k_w,
460
+ freqs_re,
461
+ freqs_im,
462
+ packed_a,
463
+ a_q_w,
464
+ a_k_w,
465
+ packed_u,
466
+ u_q_w,
467
+ u_k_w,
468
+ heads,
469
+ head_dim,
470
+ eps,
471
+ )
472
+ q_p99, q_cos = metrics(got_q, exp_q)
473
+ k_p99, k_cos = metrics(got_k, exp_k)
474
+ flashrt_us = time_us(
475
+ lambda: ops.qkv_split_joint3_cat_bf16(
476
+ packed_v,
477
+ qkv_v_bias,
478
+ v_q_w,
479
+ v_k_w,
480
+ freqs_re,
481
+ freqs_im,
482
+ packed_a,
483
+ a_q_w,
484
+ a_k_w,
485
+ packed_u,
486
+ u_q_w,
487
+ u_k_w,
488
+ heads,
489
+ head_dim,
490
+ q_cat,
491
+ k_cat,
492
+ v_cat,
493
+ video_len,
494
+ eps,
495
+ eps,
496
+ eps,
497
+ ),
498
+ args.warmup,
499
+ args.iters,
500
+ )
501
+ eager_us = time_us(
502
+ lambda: torch_ref_joint3(
503
+ packed_v,
504
+ qkv_v_bias,
505
+ v_q_w,
506
+ v_k_w,
507
+ freqs_re,
508
+ freqs_im,
509
+ packed_a,
510
+ a_q_w,
511
+ a_k_w,
512
+ packed_u,
513
+ u_q_w,
514
+ u_k_w,
515
+ heads,
516
+ head_dim,
517
+ eps,
518
+ ),
519
+ args.warmup,
520
+ args.iters,
521
+ )
522
+ status = "PASS" if q_p99 <= args.p99_abs_limit and k_p99 <= args.p99_abs_limit else "FAIL"
523
+ return Result(
524
+ shape=name,
525
+ batch=1,
526
+ seq_len=video_len + action_len + und_len,
527
+ heads=heads,
528
+ head_dim=head_dim,
529
+ flashrt_us=flashrt_us,
530
+ torch_eager_us=eager_us,
531
+ speedup_vs_eager=eager_us / flashrt_us,
532
+ q_p99_abs=q_p99,
533
+ k_p99_abs=k_p99,
534
+ q_cosine=q_cos,
535
+ k_cosine=k_cos,
536
+ status=status,
537
+ )
538
+
539
+
540
+ def run_decode_q(ops, name: str, heads: int, args) -> Result:
541
+ q, _, _, q_w, _, cos, sin = make_decode_case(heads)
542
+ q_out = torch.empty_like(q)
543
+ eps = args.eps
544
+ got = ops.decode_q_norm_rope_stage_bf16(q, q_w, cos, sin, eps, q_out)
545
+ exp = torch_ref_decode(q, q_w, cos, sin, eps)
546
+ q_p99, q_cos = metrics(got, exp)
547
+ flashrt_us = time_us(
548
+ lambda: ops.decode_q_norm_rope_stage_bf16(q, q_w, cos, sin, eps, q_out),
549
+ args.warmup,
550
+ args.iters,
551
+ )
552
+ eager_us = time_us(lambda: torch_ref_decode(q, q_w, cos, sin, eps), args.warmup, args.iters)
553
+ status = "PASS" if q_p99 <= args.p99_abs_limit else "FAIL"
554
+ return Result(
555
+ shape=name,
556
+ batch=1,
557
+ seq_len=1,
558
+ heads=heads,
559
+ head_dim=128,
560
+ flashrt_us=flashrt_us,
561
+ torch_eager_us=eager_us,
562
+ speedup_vs_eager=eager_us / flashrt_us,
563
+ q_p99_abs=q_p99,
564
+ k_p99_abs=0.0,
565
+ q_cosine=q_cos,
566
+ k_cosine=1.0,
567
+ status=status,
568
+ )
569
+
570
+
571
+ def run_decode_kv(ops, name: str, heads: int, devpos: bool, args) -> Result:
572
+ _, k, v, _, k_w, cos, sin = make_decode_case(heads)
573
+ k_slot = torch.empty_like(k)
574
+ v_slot = torch.empty_like(v)
575
+ eps = args.eps
576
+ exp_k = torch_ref_decode(k, k_w, cos, sin, eps)
577
+ if devpos:
578
+ pos = 3
579
+ k_cache = torch.empty((8, heads, 128), device="cuda", dtype=torch.bfloat16)
580
+ v_cache = torch.empty_like(k_cache)
581
+ cur_pos = torch.tensor([pos], device="cuda", dtype=torch.int32)
582
+
583
+ def flashrt_fn():
584
+ return ops.decode_k_norm_rope_kvwrite_devpos_bf16(k, v, k_w, cos, sin, cur_pos, k_cache, v_cache, eps)
585
+
586
+ def eager_fn():
587
+ k_cache[pos].copy_(torch_ref_decode(k, k_w, cos, sin, eps))
588
+ v_cache[pos].copy_(v)
589
+ return k_cache, v_cache
590
+
591
+ flashrt_fn()
592
+ got_k = k_cache[pos]
593
+ got_v = v_cache[pos]
594
+ else:
595
+ def flashrt_fn():
596
+ return ops.decode_k_norm_rope_kvwrite_bf16(k, v, k_w, cos, sin, eps, k_slot, v_slot)
597
+
598
+ def eager_fn():
599
+ k_slot.copy_(torch_ref_decode(k, k_w, cos, sin, eps))
600
+ v_slot.copy_(v)
601
+ return k_slot, v_slot
602
+
603
+ got_k, got_v = flashrt_fn()
604
+ k_p99, k_cos = metrics(got_k, exp_k)
605
+ v_p99, v_cos = metrics(got_v, v)
606
+ flashrt_us = time_us(flashrt_fn, args.warmup, args.iters)
607
+ eager_us = time_us(eager_fn, args.warmup, args.iters)
608
+ status = "PASS" if k_p99 <= args.p99_abs_limit and v_p99 == 0.0 else "FAIL"
609
+ return Result(
610
+ shape=name,
611
+ batch=1,
612
+ seq_len=1,
613
+ heads=heads,
614
+ head_dim=128,
615
+ flashrt_us=flashrt_us,
616
+ torch_eager_us=eager_us,
617
+ speedup_vs_eager=eager_us / flashrt_us,
618
+ q_p99_abs=v_p99,
619
+ k_p99_abs=k_p99,
620
+ q_cosine=v_cos,
621
+ k_cosine=k_cos,
622
+ status=status,
623
+ )
624
+
625
+
626
+ def write_markdown(path: Path, results: list[Result]) -> None:
627
+ lines = [
628
+ "| Shape | B,L,H,D | FlashRT us | Eager us | vs eager | Q p99 | K p99 | Q cosine | K cosine | Status |",
629
+ "|---|---:|---:|---:|---:|---:|---:|---:|---:|---|",
630
+ ]
631
+ for r in results:
632
+ lines.append(
633
+ f"| {r.shape} | {r.batch},{r.seq_len},{r.heads},{r.head_dim} | "
634
+ f"{r.flashrt_us:.3f} | {r.torch_eager_us:.3f} | {r.speedup_vs_eager:.2f}x | "
635
+ f"{r.q_p99_abs:.6f} | {r.k_p99_abs:.6f} | {r.q_cosine:.8f} | "
636
+ f"{r.k_cosine:.8f} | {r.status} |"
637
+ )
638
+ path.write_text("\n".join(lines) + "\n")
639
+
640
+
641
+ def main() -> None:
642
+ parser = argparse.ArgumentParser()
643
+ parser.add_argument("--backend", choices=["source", "installed"], default="source")
644
+ parser.add_argument("--artifact", default=None)
645
+ parser.add_argument("--shapes", choices=sorted(SHAPE_GROUPS), default="smoke")
646
+ parser.add_argument("--warmup", type=int, default=5)
647
+ parser.add_argument("--iters", type=int, default=20)
648
+ parser.add_argument("--eps", type=float, default=1e-6)
649
+ parser.add_argument("--p99-abs-limit", type=float, default=0.015625)
650
+ parser.add_argument("--output", default=None)
651
+ parser.add_argument("--markdown", default=None)
652
+ args = parser.parse_args()
653
+
654
+ if not torch.cuda.is_available():
655
+ raise SystemExit("CUDA is required")
656
+ torch.manual_seed(37)
657
+ ops = load_source_ops() if args.backend == "source" else load_installed_ops(args.artifact)
658
+ results = [run_one(ops, name, SHAPES[name], args) for name in SHAPE_GROUPS[args.shapes]]
659
+ if args.shapes in ("smoke", "all"):
660
+ results.append(run_joint3(ops, "joint3_small", 64, 8, 4, 8, 128, args))
661
+ if args.shapes in ("headline", "all"):
662
+ results.append(run_joint3(ops, "joint3_vla", 2520, 16, 16, 24, 128, args))
663
+ results.append(run_decode_q(ops, "decode_q_stage_h24", 24, args))
664
+ results.append(run_decode_kv(ops, "decode_kvwrite_h8", 8, False, args))
665
+ results.append(run_decode_kv(ops, "decode_kvwrite_devpos_h8", 8, True, args))
666
+
667
+ for r in results:
668
+ print(
669
+ f"{r.status} {r.shape}: flashrt={r.flashrt_us:.3f}us "
670
+ f"eager={r.torch_eager_us:.3f}us speedup={r.speedup_vs_eager:.2f}x "
671
+ f"q_p99={r.q_p99_abs:.6f} k_p99={r.k_p99_abs:.6f}"
672
+ )
673
+
674
+ if args.output:
675
+ Path(args.output).parent.mkdir(parents=True, exist_ok=True)
676
+ Path(args.output).write_text(json.dumps([asdict(r) for r in results], indent=2) + "\n")
677
+ if args.markdown:
678
+ Path(args.markdown).parent.mkdir(parents=True, exist_ok=True)
679
+ write_markdown(Path(args.markdown), results)
680
+
681
+ if any(r.status != "PASS" for r in results):
682
+ raise SystemExit(1)
683
+
684
+
685
+ if __name__ == "__main__":
686
+ main()
build/torch210-cxx11-cu128-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT QKV split, Q/K RMSNorm, and RoPE 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_decode_rope(x: torch.Tensor, weight: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, out: torch.Tensor, name: str) -> None:
11
+ if x.dim() != 2 or x.shape[1] != 128:
12
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
13
+ if weight.shape != (128,):
14
+ raise RuntimeError("norm weight must have shape (128,)")
15
+ if cos.shape != (64,) or sin.shape != (64,):
16
+ raise RuntimeError("cos and sin must have shape (64,)")
17
+ if out.shape != x.shape:
18
+ raise RuntimeError("out must have the same shape as input")
19
+
20
+
21
+ @torch.library.register_fake(add_op_namespace_prefix("decode_q_norm_rope_stage_bf16"))
22
+ def _decode_q_norm_rope_stage_bf16_fake(
23
+ q_pre: torch.Tensor,
24
+ q_norm_weight: torch.Tensor,
25
+ cos: torch.Tensor,
26
+ sin: torch.Tensor,
27
+ eps: float,
28
+ q_out: torch.Tensor,
29
+ ) -> None:
30
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
31
+ return None
32
+
33
+
34
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_bf16"))
35
+ def _decode_k_norm_rope_kvwrite_bf16_fake(
36
+ k_pre: torch.Tensor,
37
+ v_pre: torch.Tensor,
38
+ k_norm_weight: torch.Tensor,
39
+ cos: torch.Tensor,
40
+ sin: torch.Tensor,
41
+ eps: float,
42
+ k_cache_dst: torch.Tensor,
43
+ v_cache_dst: torch.Tensor,
44
+ ) -> None:
45
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
46
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
47
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
48
+ return None
49
+
50
+
51
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_devpos_bf16"))
52
+ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
53
+ k_pre: torch.Tensor,
54
+ v_pre: torch.Tensor,
55
+ k_norm_weight: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ cur_pos: torch.Tensor,
59
+ eps: float,
60
+ k_cache: torch.Tensor,
61
+ v_cache: torch.Tensor,
62
+ ) -> None:
63
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
64
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
65
+ n_kv = k_pre.shape[0]
66
+ if v_pre.shape != k_pre.shape:
67
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
68
+ if k_norm_weight.shape != (128,):
69
+ raise RuntimeError("k_norm_weight must have shape (128,)")
70
+ if cos.shape != (64,) or sin.shape != (64,):
71
+ raise RuntimeError("cos and sin must have shape (64,)")
72
+ if cur_pos.numel() != 1:
73
+ raise RuntimeError("cur_pos must have one int32 element")
74
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
75
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
76
+ if v_cache.shape != k_cache.shape:
77
+ raise RuntimeError("v_cache must have the same shape as k_cache")
78
+ return None
79
+
80
+
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
82
+ def _qkv_split_norm_rope_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ norm_q_weight: torch.Tensor,
85
+ norm_k_weight: torch.Tensor,
86
+ freqs_re: torch.Tensor,
87
+ freqs_im: torch.Tensor,
88
+ heads: int,
89
+ head_dim: int,
90
+ rope_seq_len: int,
91
+ eps: float,
92
+ q_out: torch.Tensor,
93
+ k_out: torch.Tensor,
94
+ ) -> None:
95
+ if packed_qkv.dim() != 3:
96
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
97
+ batch, seq_len, cols = packed_qkv.shape
98
+ dim = heads * head_dim
99
+ if cols != 3 * dim:
100
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
101
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
102
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
103
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
104
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
105
+ if freqs_im.shape != freqs_re.shape:
106
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
107
+ if q_out.shape != (batch, seq_len, heads, head_dim):
108
+ raise RuntimeError("q_out must have shape (batch, seq_len, heads, head_dim)")
109
+ if k_out.shape != q_out.shape:
110
+ raise RuntimeError("k_out must have the same shape as q_out")
111
+ return None
112
+
113
+
114
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_bf16"))
115
+ def _qkv_split_bias_norm_rope_v_bf16_fake(
116
+ packed_qkv: torch.Tensor,
117
+ qkv_bias: torch.Tensor,
118
+ norm_q_weight: torch.Tensor,
119
+ norm_k_weight: torch.Tensor,
120
+ freqs_re: torch.Tensor,
121
+ freqs_im: torch.Tensor,
122
+ heads: int,
123
+ head_dim: int,
124
+ rope_seq_len: int,
125
+ eps: float,
126
+ q_out: torch.Tensor,
127
+ k_out: torch.Tensor,
128
+ v_out: torch.Tensor,
129
+ ) -> None:
130
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
131
+ batch, seq_len, _ = packed_qkv.shape
132
+ dim = heads * head_dim
133
+ if qkv_bias.shape != (3 * dim,):
134
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
135
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
136
+ out_shape = (batch, seq_len, heads, head_dim)
137
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
138
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
139
+ return None
140
+
141
+
142
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_cat_bf16"))
143
+ def _qkv_split_bias_norm_rope_v_cat_bf16_fake(
144
+ packed_qkv: torch.Tensor,
145
+ qkv_bias: torch.Tensor,
146
+ norm_q_weight: torch.Tensor,
147
+ norm_k_weight: torch.Tensor,
148
+ freqs_re: torch.Tensor,
149
+ freqs_im: torch.Tensor,
150
+ heads: int,
151
+ head_dim: int,
152
+ video_offset: int,
153
+ rope_seq_len: int,
154
+ eps: float,
155
+ q_cat_out: torch.Tensor,
156
+ k_cat_out: torch.Tensor,
157
+ v_cat_out: torch.Tensor,
158
+ ) -> None:
159
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
160
+ batch, seq_len, _ = packed_qkv.shape
161
+ dim = heads * head_dim
162
+ if qkv_bias.shape != (3 * dim,):
163
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
164
+ if q_cat_out.dim() != 4:
165
+ raise RuntimeError("q_cat_out must have shape (batch, total_seq_len, heads, head_dim)")
166
+ total_seq_len = q_cat_out.shape[1]
167
+ if video_offset < 0 or video_offset + seq_len > total_seq_len:
168
+ raise RuntimeError("video_offset + packed_qkv.shape[1] must be within q_cat_out.shape[1]")
169
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
170
+ out_shape = (batch, total_seq_len, heads, head_dim)
171
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
172
+ raise RuntimeError("cat outputs must have shape (batch, total_seq_len, heads, head_dim)")
173
+ return None
174
+
175
+
176
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_joint3_cat_bf16"))
177
+ def _qkv_split_joint3_cat_bf16_fake(
178
+ packed_v: torch.Tensor,
179
+ qkv_v_bias: torch.Tensor,
180
+ norm_v_q_weight: torch.Tensor,
181
+ norm_v_k_weight: torch.Tensor,
182
+ freqs_re: torch.Tensor,
183
+ freqs_im: torch.Tensor,
184
+ packed_a: torch.Tensor,
185
+ norm_a_q_weight: torch.Tensor,
186
+ norm_a_k_weight: torch.Tensor,
187
+ packed_u: torch.Tensor,
188
+ norm_u_q_weight: torch.Tensor,
189
+ norm_u_k_weight: torch.Tensor,
190
+ heads: int,
191
+ head_dim: int,
192
+ rope_seq_len: int,
193
+ eps_v: float,
194
+ eps_a: float,
195
+ eps_u: float,
196
+ q_cat_out: torch.Tensor,
197
+ k_cat_out: torch.Tensor,
198
+ v_cat_out: torch.Tensor,
199
+ ) -> None:
200
+ _check_packed_qkv(packed_v, norm_v_q_weight, norm_v_k_weight, heads, head_dim)
201
+ _check_packed_qkv(packed_a, norm_a_q_weight, norm_a_k_weight, heads, head_dim)
202
+ _check_packed_qkv(packed_u, norm_u_q_weight, norm_u_k_weight, heads, head_dim)
203
+ batch = packed_v.shape[0]
204
+ if batch != 1 or packed_a.shape[0] != batch or packed_u.shape[0] != batch:
205
+ raise RuntimeError("qkv_split_joint3_cat_bf16 currently supports batch == 1")
206
+ total_seq_len = packed_v.shape[1] + packed_a.shape[1] + packed_u.shape[1]
207
+ dim = heads * head_dim
208
+ if qkv_v_bias.shape != (3 * dim,):
209
+ raise RuntimeError("qkv_v_bias must have shape (3 * heads * head_dim,)")
210
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
211
+ out_shape = (batch, total_seq_len, heads, head_dim)
212
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
213
+ raise RuntimeError("cat outputs must have shape (1, L_v + L_a + L_u, heads, head_dim)")
214
+ return None
215
+
216
+
217
+ def _check_packed_qkv(
218
+ packed_qkv: torch.Tensor,
219
+ norm_q_weight: torch.Tensor,
220
+ norm_k_weight: torch.Tensor,
221
+ heads: int,
222
+ head_dim: int,
223
+ ) -> None:
224
+ if packed_qkv.dim() != 3:
225
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
226
+ dim = heads * head_dim
227
+ if head_dim % 2 != 0 or packed_qkv.shape[2] != 3 * dim:
228
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim and head_dim must be even")
229
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
230
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
231
+
232
+
233
+ def _check_freqs(
234
+ freqs_re: torch.Tensor,
235
+ freqs_im: torch.Tensor,
236
+ head_dim: int,
237
+ rope_seq_len: int,
238
+ ) -> None:
239
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
240
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
241
+ if freqs_im.shape != freqs_re.shape:
242
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
243
+ if rope_seq_len < 0 or freqs_re.shape[0] < rope_seq_len:
244
+ raise RuntimeError("freqs_re must have at least rope_seq_len rows")
245
+
246
+
247
+ def qkv_split_norm_rope_bf16(
248
+ packed_qkv: torch.Tensor,
249
+ norm_q_weight: torch.Tensor,
250
+ norm_k_weight: torch.Tensor,
251
+ freqs_re: torch.Tensor,
252
+ freqs_im: torch.Tensor,
253
+ heads: int,
254
+ head_dim: int,
255
+ rope_seq_len: int | None = None,
256
+ eps: float = 1e-6,
257
+ q_out: torch.Tensor | None = None,
258
+ k_out: torch.Tensor | None = None,
259
+ ) -> tuple[torch.Tensor, torch.Tensor]:
260
+ """Split packed QKV, RMSNorm Q/K, apply RoPE, and return Q/K tensors.
261
+
262
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
263
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
264
+ """
265
+
266
+ if rope_seq_len is None:
267
+ rope_seq_len = packed_qkv.shape[1]
268
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
269
+ if q_out is None:
270
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
271
+ if k_out is None:
272
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
273
+ ops.qkv_split_norm_rope_bf16(
274
+ packed_qkv,
275
+ norm_q_weight,
276
+ norm_k_weight,
277
+ freqs_re,
278
+ freqs_im,
279
+ int(heads),
280
+ int(head_dim),
281
+ int(rope_seq_len),
282
+ float(eps),
283
+ q_out,
284
+ k_out,
285
+ )
286
+ return q_out, k_out
287
+
288
+
289
+ def decode_q_norm_rope_stage_bf16(
290
+ q_pre: torch.Tensor,
291
+ q_norm_weight: torch.Tensor,
292
+ cos: torch.Tensor,
293
+ sin: torch.Tensor,
294
+ eps: float = 1e-6,
295
+ q_out: torch.Tensor | None = None,
296
+ ) -> torch.Tensor:
297
+ """RMSNorm Q, apply rotate-half RoPE, and write a decode Q staging buffer.
298
+
299
+ The decode path is fixed to ``head_dim == 128``. ``cos`` and ``sin`` have
300
+ shape ``(64,)`` and dtype BF16.
301
+ """
302
+
303
+ if q_out is None:
304
+ q_out = torch.empty_like(q_pre)
305
+ ops.decode_q_norm_rope_stage_bf16(
306
+ q_pre, q_norm_weight, cos, sin, float(eps), q_out
307
+ )
308
+ return q_out
309
+
310
+
311
+ def decode_k_norm_rope_kvwrite_bf16(
312
+ k_pre: torch.Tensor,
313
+ v_pre: torch.Tensor,
314
+ k_norm_weight: torch.Tensor,
315
+ cos: torch.Tensor,
316
+ sin: torch.Tensor,
317
+ eps: float = 1e-6,
318
+ k_cache_dst: torch.Tensor | None = None,
319
+ v_cache_dst: torch.Tensor | None = None,
320
+ ) -> tuple[torch.Tensor, torch.Tensor]:
321
+ """RMSNorm K, apply rotate-half RoPE, and write one KV cache slot."""
322
+
323
+ if k_cache_dst is None:
324
+ k_cache_dst = torch.empty_like(k_pre)
325
+ if v_cache_dst is None:
326
+ v_cache_dst = torch.empty_like(v_pre)
327
+ ops.decode_k_norm_rope_kvwrite_bf16(
328
+ k_pre, v_pre, k_norm_weight, cos, sin, float(eps), k_cache_dst, v_cache_dst
329
+ )
330
+ return k_cache_dst, v_cache_dst
331
+
332
+
333
+ def decode_k_norm_rope_kvwrite_devpos_bf16(
334
+ k_pre: torch.Tensor,
335
+ v_pre: torch.Tensor,
336
+ k_norm_weight: torch.Tensor,
337
+ cos: torch.Tensor,
338
+ sin: torch.Tensor,
339
+ cur_pos: torch.Tensor,
340
+ k_cache: torch.Tensor,
341
+ v_cache: torch.Tensor,
342
+ eps: float = 1e-6,
343
+ ) -> tuple[torch.Tensor, torch.Tensor]:
344
+ """Write one KV cache slot selected by device int32 ``cur_pos``."""
345
+
346
+ ops.decode_k_norm_rope_kvwrite_devpos_bf16(
347
+ k_pre, v_pre, k_norm_weight, cos, sin, cur_pos, float(eps), k_cache, v_cache
348
+ )
349
+ return k_cache, v_cache
350
+
351
+
352
+ def qkv_split_bias_norm_rope_v_bf16(
353
+ packed_qkv: torch.Tensor,
354
+ qkv_bias: torch.Tensor,
355
+ norm_q_weight: torch.Tensor,
356
+ norm_k_weight: torch.Tensor,
357
+ freqs_re: torch.Tensor,
358
+ freqs_im: torch.Tensor,
359
+ heads: int,
360
+ head_dim: int,
361
+ rope_seq_len: int | None = None,
362
+ eps: float = 1e-6,
363
+ q_out: torch.Tensor | None = None,
364
+ k_out: torch.Tensor | None = None,
365
+ v_out: torch.Tensor | None = None,
366
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
367
+ """Bias QKV, RMSNorm Q/K, apply RoPE, and materialize Q/K/V."""
368
+
369
+ if rope_seq_len is None:
370
+ rope_seq_len = packed_qkv.shape[1]
371
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
372
+ if q_out is None:
373
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
374
+ if k_out is None:
375
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
376
+ if v_out is None:
377
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
378
+ ops.qkv_split_bias_norm_rope_v_bf16(
379
+ packed_qkv,
380
+ qkv_bias,
381
+ norm_q_weight,
382
+ norm_k_weight,
383
+ freqs_re,
384
+ freqs_im,
385
+ int(heads),
386
+ int(head_dim),
387
+ int(rope_seq_len),
388
+ float(eps),
389
+ q_out,
390
+ k_out,
391
+ v_out,
392
+ )
393
+ return q_out, k_out, v_out
394
+
395
+
396
+ def qkv_split_bias_norm_rope_v_cat_bf16(
397
+ packed_qkv: torch.Tensor,
398
+ qkv_bias: torch.Tensor,
399
+ norm_q_weight: torch.Tensor,
400
+ norm_k_weight: torch.Tensor,
401
+ freqs_re: torch.Tensor,
402
+ freqs_im: torch.Tensor,
403
+ heads: int,
404
+ head_dim: int,
405
+ video_offset: int,
406
+ q_cat_out: torch.Tensor,
407
+ k_cat_out: torch.Tensor,
408
+ v_cat_out: torch.Tensor,
409
+ rope_seq_len: int | None = None,
410
+ eps: float = 1e-6,
411
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
412
+ """Write a biased video QKV segment directly into joint Q/K/V workspaces."""
413
+
414
+ if rope_seq_len is None:
415
+ rope_seq_len = packed_qkv.shape[1]
416
+ ops.qkv_split_bias_norm_rope_v_cat_bf16(
417
+ packed_qkv,
418
+ qkv_bias,
419
+ norm_q_weight,
420
+ norm_k_weight,
421
+ freqs_re,
422
+ freqs_im,
423
+ int(heads),
424
+ int(head_dim),
425
+ int(video_offset),
426
+ int(rope_seq_len),
427
+ float(eps),
428
+ q_cat_out,
429
+ k_cat_out,
430
+ v_cat_out,
431
+ )
432
+ return q_cat_out, k_cat_out, v_cat_out
433
+
434
+
435
+ def qkv_split_joint3_cat_bf16(
436
+ packed_v: torch.Tensor,
437
+ qkv_v_bias: torch.Tensor,
438
+ norm_v_q_weight: torch.Tensor,
439
+ norm_v_k_weight: torch.Tensor,
440
+ freqs_re: torch.Tensor,
441
+ freqs_im: torch.Tensor,
442
+ packed_a: torch.Tensor,
443
+ norm_a_q_weight: torch.Tensor,
444
+ norm_a_k_weight: torch.Tensor,
445
+ packed_u: torch.Tensor,
446
+ norm_u_q_weight: torch.Tensor,
447
+ norm_u_k_weight: torch.Tensor,
448
+ heads: int,
449
+ head_dim: int,
450
+ q_cat_out: torch.Tensor,
451
+ k_cat_out: torch.Tensor,
452
+ v_cat_out: torch.Tensor,
453
+ rope_seq_len: int | None = None,
454
+ eps_v: float = 1e-6,
455
+ eps_a: float = 1e-6,
456
+ eps_u: float = 1e-6,
457
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
458
+ """Fuse video/action/und QKV postprocess into one joint Q/K/V workspace."""
459
+
460
+ if rope_seq_len is None:
461
+ rope_seq_len = packed_v.shape[1]
462
+ ops.qkv_split_joint3_cat_bf16(
463
+ packed_v,
464
+ qkv_v_bias,
465
+ norm_v_q_weight,
466
+ norm_v_k_weight,
467
+ freqs_re,
468
+ freqs_im,
469
+ packed_a,
470
+ norm_a_q_weight,
471
+ norm_a_k_weight,
472
+ packed_u,
473
+ norm_u_q_weight,
474
+ norm_u_k_weight,
475
+ int(heads),
476
+ int(head_dim),
477
+ int(rope_seq_len),
478
+ float(eps_v),
479
+ float(eps_a),
480
+ float(eps_u),
481
+ q_cat_out,
482
+ k_cat_out,
483
+ v_cat_out,
484
+ )
485
+ return q_cat_out, k_cat_out, v_cat_out
486
+
487
+
488
+ __all__ = [
489
+ "decode_q_norm_rope_stage_bf16",
490
+ "decode_k_norm_rope_kvwrite_bf16",
491
+ "decode_k_norm_rope_kvwrite_devpos_bf16",
492
+ "qkv_split_norm_rope_bf16",
493
+ "qkv_split_bias_norm_rope_v_bf16",
494
+ "qkv_split_bias_norm_rope_v_cat_bf16",
495
+ "qkv_split_joint3_cat_bf16",
496
+ ]
build/torch210-cxx11-cu128-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c24faa8f546717442662ac2c4983201f4a08fbdcd01450c43f17efd8e7c01818
3
+ size 3653648
build/torch210-cxx11-cu128-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_cf903dd
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_cf903dd
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_flashrt_qkv_cache_rope_cuda_cf903dd::{op_name}"
build/torch210-cxx11-cu128-x86_64-linux/flashrt_qkv_cache_rope/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch210-cxx11-cu128-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_cf903dd",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "10.1",
12
+ "12.0+PTX",
13
+ "7.0",
14
+ "7.2",
15
+ "7.5",
16
+ "8.0",
17
+ "8.6",
18
+ "8.7",
19
+ "8.9",
20
+ "9.0"
21
+ ]
22
+ }
23
+ }
build/torch210-cxx11-cu130-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT QKV split, Q/K RMSNorm, and RoPE 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_decode_rope(x: torch.Tensor, weight: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, out: torch.Tensor, name: str) -> None:
11
+ if x.dim() != 2 or x.shape[1] != 128:
12
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
13
+ if weight.shape != (128,):
14
+ raise RuntimeError("norm weight must have shape (128,)")
15
+ if cos.shape != (64,) or sin.shape != (64,):
16
+ raise RuntimeError("cos and sin must have shape (64,)")
17
+ if out.shape != x.shape:
18
+ raise RuntimeError("out must have the same shape as input")
19
+
20
+
21
+ @torch.library.register_fake(add_op_namespace_prefix("decode_q_norm_rope_stage_bf16"))
22
+ def _decode_q_norm_rope_stage_bf16_fake(
23
+ q_pre: torch.Tensor,
24
+ q_norm_weight: torch.Tensor,
25
+ cos: torch.Tensor,
26
+ sin: torch.Tensor,
27
+ eps: float,
28
+ q_out: torch.Tensor,
29
+ ) -> None:
30
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
31
+ return None
32
+
33
+
34
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_bf16"))
35
+ def _decode_k_norm_rope_kvwrite_bf16_fake(
36
+ k_pre: torch.Tensor,
37
+ v_pre: torch.Tensor,
38
+ k_norm_weight: torch.Tensor,
39
+ cos: torch.Tensor,
40
+ sin: torch.Tensor,
41
+ eps: float,
42
+ k_cache_dst: torch.Tensor,
43
+ v_cache_dst: torch.Tensor,
44
+ ) -> None:
45
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
46
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
47
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
48
+ return None
49
+
50
+
51
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_devpos_bf16"))
52
+ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
53
+ k_pre: torch.Tensor,
54
+ v_pre: torch.Tensor,
55
+ k_norm_weight: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ cur_pos: torch.Tensor,
59
+ eps: float,
60
+ k_cache: torch.Tensor,
61
+ v_cache: torch.Tensor,
62
+ ) -> None:
63
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
64
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
65
+ n_kv = k_pre.shape[0]
66
+ if v_pre.shape != k_pre.shape:
67
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
68
+ if k_norm_weight.shape != (128,):
69
+ raise RuntimeError("k_norm_weight must have shape (128,)")
70
+ if cos.shape != (64,) or sin.shape != (64,):
71
+ raise RuntimeError("cos and sin must have shape (64,)")
72
+ if cur_pos.numel() != 1:
73
+ raise RuntimeError("cur_pos must have one int32 element")
74
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
75
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
76
+ if v_cache.shape != k_cache.shape:
77
+ raise RuntimeError("v_cache must have the same shape as k_cache")
78
+ return None
79
+
80
+
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
82
+ def _qkv_split_norm_rope_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ norm_q_weight: torch.Tensor,
85
+ norm_k_weight: torch.Tensor,
86
+ freqs_re: torch.Tensor,
87
+ freqs_im: torch.Tensor,
88
+ heads: int,
89
+ head_dim: int,
90
+ rope_seq_len: int,
91
+ eps: float,
92
+ q_out: torch.Tensor,
93
+ k_out: torch.Tensor,
94
+ ) -> None:
95
+ if packed_qkv.dim() != 3:
96
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
97
+ batch, seq_len, cols = packed_qkv.shape
98
+ dim = heads * head_dim
99
+ if cols != 3 * dim:
100
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
101
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
102
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
103
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
104
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
105
+ if freqs_im.shape != freqs_re.shape:
106
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
107
+ if q_out.shape != (batch, seq_len, heads, head_dim):
108
+ raise RuntimeError("q_out must have shape (batch, seq_len, heads, head_dim)")
109
+ if k_out.shape != q_out.shape:
110
+ raise RuntimeError("k_out must have the same shape as q_out")
111
+ return None
112
+
113
+
114
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_bf16"))
115
+ def _qkv_split_bias_norm_rope_v_bf16_fake(
116
+ packed_qkv: torch.Tensor,
117
+ qkv_bias: torch.Tensor,
118
+ norm_q_weight: torch.Tensor,
119
+ norm_k_weight: torch.Tensor,
120
+ freqs_re: torch.Tensor,
121
+ freqs_im: torch.Tensor,
122
+ heads: int,
123
+ head_dim: int,
124
+ rope_seq_len: int,
125
+ eps: float,
126
+ q_out: torch.Tensor,
127
+ k_out: torch.Tensor,
128
+ v_out: torch.Tensor,
129
+ ) -> None:
130
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
131
+ batch, seq_len, _ = packed_qkv.shape
132
+ dim = heads * head_dim
133
+ if qkv_bias.shape != (3 * dim,):
134
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
135
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
136
+ out_shape = (batch, seq_len, heads, head_dim)
137
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
138
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
139
+ return None
140
+
141
+
142
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_cat_bf16"))
143
+ def _qkv_split_bias_norm_rope_v_cat_bf16_fake(
144
+ packed_qkv: torch.Tensor,
145
+ qkv_bias: torch.Tensor,
146
+ norm_q_weight: torch.Tensor,
147
+ norm_k_weight: torch.Tensor,
148
+ freqs_re: torch.Tensor,
149
+ freqs_im: torch.Tensor,
150
+ heads: int,
151
+ head_dim: int,
152
+ video_offset: int,
153
+ rope_seq_len: int,
154
+ eps: float,
155
+ q_cat_out: torch.Tensor,
156
+ k_cat_out: torch.Tensor,
157
+ v_cat_out: torch.Tensor,
158
+ ) -> None:
159
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
160
+ batch, seq_len, _ = packed_qkv.shape
161
+ dim = heads * head_dim
162
+ if qkv_bias.shape != (3 * dim,):
163
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
164
+ if q_cat_out.dim() != 4:
165
+ raise RuntimeError("q_cat_out must have shape (batch, total_seq_len, heads, head_dim)")
166
+ total_seq_len = q_cat_out.shape[1]
167
+ if video_offset < 0 or video_offset + seq_len > total_seq_len:
168
+ raise RuntimeError("video_offset + packed_qkv.shape[1] must be within q_cat_out.shape[1]")
169
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
170
+ out_shape = (batch, total_seq_len, heads, head_dim)
171
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
172
+ raise RuntimeError("cat outputs must have shape (batch, total_seq_len, heads, head_dim)")
173
+ return None
174
+
175
+
176
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_joint3_cat_bf16"))
177
+ def _qkv_split_joint3_cat_bf16_fake(
178
+ packed_v: torch.Tensor,
179
+ qkv_v_bias: torch.Tensor,
180
+ norm_v_q_weight: torch.Tensor,
181
+ norm_v_k_weight: torch.Tensor,
182
+ freqs_re: torch.Tensor,
183
+ freqs_im: torch.Tensor,
184
+ packed_a: torch.Tensor,
185
+ norm_a_q_weight: torch.Tensor,
186
+ norm_a_k_weight: torch.Tensor,
187
+ packed_u: torch.Tensor,
188
+ norm_u_q_weight: torch.Tensor,
189
+ norm_u_k_weight: torch.Tensor,
190
+ heads: int,
191
+ head_dim: int,
192
+ rope_seq_len: int,
193
+ eps_v: float,
194
+ eps_a: float,
195
+ eps_u: float,
196
+ q_cat_out: torch.Tensor,
197
+ k_cat_out: torch.Tensor,
198
+ v_cat_out: torch.Tensor,
199
+ ) -> None:
200
+ _check_packed_qkv(packed_v, norm_v_q_weight, norm_v_k_weight, heads, head_dim)
201
+ _check_packed_qkv(packed_a, norm_a_q_weight, norm_a_k_weight, heads, head_dim)
202
+ _check_packed_qkv(packed_u, norm_u_q_weight, norm_u_k_weight, heads, head_dim)
203
+ batch = packed_v.shape[0]
204
+ if batch != 1 or packed_a.shape[0] != batch or packed_u.shape[0] != batch:
205
+ raise RuntimeError("qkv_split_joint3_cat_bf16 currently supports batch == 1")
206
+ total_seq_len = packed_v.shape[1] + packed_a.shape[1] + packed_u.shape[1]
207
+ dim = heads * head_dim
208
+ if qkv_v_bias.shape != (3 * dim,):
209
+ raise RuntimeError("qkv_v_bias must have shape (3 * heads * head_dim,)")
210
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
211
+ out_shape = (batch, total_seq_len, heads, head_dim)
212
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
213
+ raise RuntimeError("cat outputs must have shape (1, L_v + L_a + L_u, heads, head_dim)")
214
+ return None
215
+
216
+
217
+ def _check_packed_qkv(
218
+ packed_qkv: torch.Tensor,
219
+ norm_q_weight: torch.Tensor,
220
+ norm_k_weight: torch.Tensor,
221
+ heads: int,
222
+ head_dim: int,
223
+ ) -> None:
224
+ if packed_qkv.dim() != 3:
225
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
226
+ dim = heads * head_dim
227
+ if head_dim % 2 != 0 or packed_qkv.shape[2] != 3 * dim:
228
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim and head_dim must be even")
229
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
230
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
231
+
232
+
233
+ def _check_freqs(
234
+ freqs_re: torch.Tensor,
235
+ freqs_im: torch.Tensor,
236
+ head_dim: int,
237
+ rope_seq_len: int,
238
+ ) -> None:
239
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
240
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
241
+ if freqs_im.shape != freqs_re.shape:
242
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
243
+ if rope_seq_len < 0 or freqs_re.shape[0] < rope_seq_len:
244
+ raise RuntimeError("freqs_re must have at least rope_seq_len rows")
245
+
246
+
247
+ def qkv_split_norm_rope_bf16(
248
+ packed_qkv: torch.Tensor,
249
+ norm_q_weight: torch.Tensor,
250
+ norm_k_weight: torch.Tensor,
251
+ freqs_re: torch.Tensor,
252
+ freqs_im: torch.Tensor,
253
+ heads: int,
254
+ head_dim: int,
255
+ rope_seq_len: int | None = None,
256
+ eps: float = 1e-6,
257
+ q_out: torch.Tensor | None = None,
258
+ k_out: torch.Tensor | None = None,
259
+ ) -> tuple[torch.Tensor, torch.Tensor]:
260
+ """Split packed QKV, RMSNorm Q/K, apply RoPE, and return Q/K tensors.
261
+
262
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
263
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
264
+ """
265
+
266
+ if rope_seq_len is None:
267
+ rope_seq_len = packed_qkv.shape[1]
268
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
269
+ if q_out is None:
270
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
271
+ if k_out is None:
272
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
273
+ ops.qkv_split_norm_rope_bf16(
274
+ packed_qkv,
275
+ norm_q_weight,
276
+ norm_k_weight,
277
+ freqs_re,
278
+ freqs_im,
279
+ int(heads),
280
+ int(head_dim),
281
+ int(rope_seq_len),
282
+ float(eps),
283
+ q_out,
284
+ k_out,
285
+ )
286
+ return q_out, k_out
287
+
288
+
289
+ def decode_q_norm_rope_stage_bf16(
290
+ q_pre: torch.Tensor,
291
+ q_norm_weight: torch.Tensor,
292
+ cos: torch.Tensor,
293
+ sin: torch.Tensor,
294
+ eps: float = 1e-6,
295
+ q_out: torch.Tensor | None = None,
296
+ ) -> torch.Tensor:
297
+ """RMSNorm Q, apply rotate-half RoPE, and write a decode Q staging buffer.
298
+
299
+ The decode path is fixed to ``head_dim == 128``. ``cos`` and ``sin`` have
300
+ shape ``(64,)`` and dtype BF16.
301
+ """
302
+
303
+ if q_out is None:
304
+ q_out = torch.empty_like(q_pre)
305
+ ops.decode_q_norm_rope_stage_bf16(
306
+ q_pre, q_norm_weight, cos, sin, float(eps), q_out
307
+ )
308
+ return q_out
309
+
310
+
311
+ def decode_k_norm_rope_kvwrite_bf16(
312
+ k_pre: torch.Tensor,
313
+ v_pre: torch.Tensor,
314
+ k_norm_weight: torch.Tensor,
315
+ cos: torch.Tensor,
316
+ sin: torch.Tensor,
317
+ eps: float = 1e-6,
318
+ k_cache_dst: torch.Tensor | None = None,
319
+ v_cache_dst: torch.Tensor | None = None,
320
+ ) -> tuple[torch.Tensor, torch.Tensor]:
321
+ """RMSNorm K, apply rotate-half RoPE, and write one KV cache slot."""
322
+
323
+ if k_cache_dst is None:
324
+ k_cache_dst = torch.empty_like(k_pre)
325
+ if v_cache_dst is None:
326
+ v_cache_dst = torch.empty_like(v_pre)
327
+ ops.decode_k_norm_rope_kvwrite_bf16(
328
+ k_pre, v_pre, k_norm_weight, cos, sin, float(eps), k_cache_dst, v_cache_dst
329
+ )
330
+ return k_cache_dst, v_cache_dst
331
+
332
+
333
+ def decode_k_norm_rope_kvwrite_devpos_bf16(
334
+ k_pre: torch.Tensor,
335
+ v_pre: torch.Tensor,
336
+ k_norm_weight: torch.Tensor,
337
+ cos: torch.Tensor,
338
+ sin: torch.Tensor,
339
+ cur_pos: torch.Tensor,
340
+ k_cache: torch.Tensor,
341
+ v_cache: torch.Tensor,
342
+ eps: float = 1e-6,
343
+ ) -> tuple[torch.Tensor, torch.Tensor]:
344
+ """Write one KV cache slot selected by device int32 ``cur_pos``."""
345
+
346
+ ops.decode_k_norm_rope_kvwrite_devpos_bf16(
347
+ k_pre, v_pre, k_norm_weight, cos, sin, cur_pos, float(eps), k_cache, v_cache
348
+ )
349
+ return k_cache, v_cache
350
+
351
+
352
+ def qkv_split_bias_norm_rope_v_bf16(
353
+ packed_qkv: torch.Tensor,
354
+ qkv_bias: torch.Tensor,
355
+ norm_q_weight: torch.Tensor,
356
+ norm_k_weight: torch.Tensor,
357
+ freqs_re: torch.Tensor,
358
+ freqs_im: torch.Tensor,
359
+ heads: int,
360
+ head_dim: int,
361
+ rope_seq_len: int | None = None,
362
+ eps: float = 1e-6,
363
+ q_out: torch.Tensor | None = None,
364
+ k_out: torch.Tensor | None = None,
365
+ v_out: torch.Tensor | None = None,
366
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
367
+ """Bias QKV, RMSNorm Q/K, apply RoPE, and materialize Q/K/V."""
368
+
369
+ if rope_seq_len is None:
370
+ rope_seq_len = packed_qkv.shape[1]
371
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
372
+ if q_out is None:
373
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
374
+ if k_out is None:
375
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
376
+ if v_out is None:
377
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
378
+ ops.qkv_split_bias_norm_rope_v_bf16(
379
+ packed_qkv,
380
+ qkv_bias,
381
+ norm_q_weight,
382
+ norm_k_weight,
383
+ freqs_re,
384
+ freqs_im,
385
+ int(heads),
386
+ int(head_dim),
387
+ int(rope_seq_len),
388
+ float(eps),
389
+ q_out,
390
+ k_out,
391
+ v_out,
392
+ )
393
+ return q_out, k_out, v_out
394
+
395
+
396
+ def qkv_split_bias_norm_rope_v_cat_bf16(
397
+ packed_qkv: torch.Tensor,
398
+ qkv_bias: torch.Tensor,
399
+ norm_q_weight: torch.Tensor,
400
+ norm_k_weight: torch.Tensor,
401
+ freqs_re: torch.Tensor,
402
+ freqs_im: torch.Tensor,
403
+ heads: int,
404
+ head_dim: int,
405
+ video_offset: int,
406
+ q_cat_out: torch.Tensor,
407
+ k_cat_out: torch.Tensor,
408
+ v_cat_out: torch.Tensor,
409
+ rope_seq_len: int | None = None,
410
+ eps: float = 1e-6,
411
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
412
+ """Write a biased video QKV segment directly into joint Q/K/V workspaces."""
413
+
414
+ if rope_seq_len is None:
415
+ rope_seq_len = packed_qkv.shape[1]
416
+ ops.qkv_split_bias_norm_rope_v_cat_bf16(
417
+ packed_qkv,
418
+ qkv_bias,
419
+ norm_q_weight,
420
+ norm_k_weight,
421
+ freqs_re,
422
+ freqs_im,
423
+ int(heads),
424
+ int(head_dim),
425
+ int(video_offset),
426
+ int(rope_seq_len),
427
+ float(eps),
428
+ q_cat_out,
429
+ k_cat_out,
430
+ v_cat_out,
431
+ )
432
+ return q_cat_out, k_cat_out, v_cat_out
433
+
434
+
435
+ def qkv_split_joint3_cat_bf16(
436
+ packed_v: torch.Tensor,
437
+ qkv_v_bias: torch.Tensor,
438
+ norm_v_q_weight: torch.Tensor,
439
+ norm_v_k_weight: torch.Tensor,
440
+ freqs_re: torch.Tensor,
441
+ freqs_im: torch.Tensor,
442
+ packed_a: torch.Tensor,
443
+ norm_a_q_weight: torch.Tensor,
444
+ norm_a_k_weight: torch.Tensor,
445
+ packed_u: torch.Tensor,
446
+ norm_u_q_weight: torch.Tensor,
447
+ norm_u_k_weight: torch.Tensor,
448
+ heads: int,
449
+ head_dim: int,
450
+ q_cat_out: torch.Tensor,
451
+ k_cat_out: torch.Tensor,
452
+ v_cat_out: torch.Tensor,
453
+ rope_seq_len: int | None = None,
454
+ eps_v: float = 1e-6,
455
+ eps_a: float = 1e-6,
456
+ eps_u: float = 1e-6,
457
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
458
+ """Fuse video/action/und QKV postprocess into one joint Q/K/V workspace."""
459
+
460
+ if rope_seq_len is None:
461
+ rope_seq_len = packed_v.shape[1]
462
+ ops.qkv_split_joint3_cat_bf16(
463
+ packed_v,
464
+ qkv_v_bias,
465
+ norm_v_q_weight,
466
+ norm_v_k_weight,
467
+ freqs_re,
468
+ freqs_im,
469
+ packed_a,
470
+ norm_a_q_weight,
471
+ norm_a_k_weight,
472
+ packed_u,
473
+ norm_u_q_weight,
474
+ norm_u_k_weight,
475
+ int(heads),
476
+ int(head_dim),
477
+ int(rope_seq_len),
478
+ float(eps_v),
479
+ float(eps_a),
480
+ float(eps_u),
481
+ q_cat_out,
482
+ k_cat_out,
483
+ v_cat_out,
484
+ )
485
+ return q_cat_out, k_cat_out, v_cat_out
486
+
487
+
488
+ __all__ = [
489
+ "decode_q_norm_rope_stage_bf16",
490
+ "decode_k_norm_rope_kvwrite_bf16",
491
+ "decode_k_norm_rope_kvwrite_devpos_bf16",
492
+ "qkv_split_norm_rope_bf16",
493
+ "qkv_split_bias_norm_rope_v_bf16",
494
+ "qkv_split_bias_norm_rope_v_cat_bf16",
495
+ "qkv_split_joint3_cat_bf16",
496
+ ]
build/torch210-cxx11-cu130-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ae499f70cdfedbe14e61fc4285decd2a3f2bca5a883b498330c51af41f56155a
3
+ size 3418968
build/torch210-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_cf903dd
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_cf903dd
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_flashrt_qkv_cache_rope_cuda_cf903dd::{op_name}"
build/torch210-cxx11-cu130-x86_64-linux/flashrt_qkv_cache_rope/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch210-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_cf903dd",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "11.0",
12
+ "12.0+PTX",
13
+ "7.5",
14
+ "8.0",
15
+ "8.6",
16
+ "8.7",
17
+ "8.9",
18
+ "9.0"
19
+ ]
20
+ }
21
+ }
build/torch211-cxx11-cu128-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT QKV split, Q/K RMSNorm, and RoPE 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_decode_rope(x: torch.Tensor, weight: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, out: torch.Tensor, name: str) -> None:
11
+ if x.dim() != 2 or x.shape[1] != 128:
12
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
13
+ if weight.shape != (128,):
14
+ raise RuntimeError("norm weight must have shape (128,)")
15
+ if cos.shape != (64,) or sin.shape != (64,):
16
+ raise RuntimeError("cos and sin must have shape (64,)")
17
+ if out.shape != x.shape:
18
+ raise RuntimeError("out must have the same shape as input")
19
+
20
+
21
+ @torch.library.register_fake(add_op_namespace_prefix("decode_q_norm_rope_stage_bf16"))
22
+ def _decode_q_norm_rope_stage_bf16_fake(
23
+ q_pre: torch.Tensor,
24
+ q_norm_weight: torch.Tensor,
25
+ cos: torch.Tensor,
26
+ sin: torch.Tensor,
27
+ eps: float,
28
+ q_out: torch.Tensor,
29
+ ) -> None:
30
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
31
+ return None
32
+
33
+
34
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_bf16"))
35
+ def _decode_k_norm_rope_kvwrite_bf16_fake(
36
+ k_pre: torch.Tensor,
37
+ v_pre: torch.Tensor,
38
+ k_norm_weight: torch.Tensor,
39
+ cos: torch.Tensor,
40
+ sin: torch.Tensor,
41
+ eps: float,
42
+ k_cache_dst: torch.Tensor,
43
+ v_cache_dst: torch.Tensor,
44
+ ) -> None:
45
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
46
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
47
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
48
+ return None
49
+
50
+
51
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_devpos_bf16"))
52
+ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
53
+ k_pre: torch.Tensor,
54
+ v_pre: torch.Tensor,
55
+ k_norm_weight: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ cur_pos: torch.Tensor,
59
+ eps: float,
60
+ k_cache: torch.Tensor,
61
+ v_cache: torch.Tensor,
62
+ ) -> None:
63
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
64
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
65
+ n_kv = k_pre.shape[0]
66
+ if v_pre.shape != k_pre.shape:
67
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
68
+ if k_norm_weight.shape != (128,):
69
+ raise RuntimeError("k_norm_weight must have shape (128,)")
70
+ if cos.shape != (64,) or sin.shape != (64,):
71
+ raise RuntimeError("cos and sin must have shape (64,)")
72
+ if cur_pos.numel() != 1:
73
+ raise RuntimeError("cur_pos must have one int32 element")
74
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
75
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
76
+ if v_cache.shape != k_cache.shape:
77
+ raise RuntimeError("v_cache must have the same shape as k_cache")
78
+ return None
79
+
80
+
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
82
+ def _qkv_split_norm_rope_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ norm_q_weight: torch.Tensor,
85
+ norm_k_weight: torch.Tensor,
86
+ freqs_re: torch.Tensor,
87
+ freqs_im: torch.Tensor,
88
+ heads: int,
89
+ head_dim: int,
90
+ rope_seq_len: int,
91
+ eps: float,
92
+ q_out: torch.Tensor,
93
+ k_out: torch.Tensor,
94
+ ) -> None:
95
+ if packed_qkv.dim() != 3:
96
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
97
+ batch, seq_len, cols = packed_qkv.shape
98
+ dim = heads * head_dim
99
+ if cols != 3 * dim:
100
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
101
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
102
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
103
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
104
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
105
+ if freqs_im.shape != freqs_re.shape:
106
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
107
+ if q_out.shape != (batch, seq_len, heads, head_dim):
108
+ raise RuntimeError("q_out must have shape (batch, seq_len, heads, head_dim)")
109
+ if k_out.shape != q_out.shape:
110
+ raise RuntimeError("k_out must have the same shape as q_out")
111
+ return None
112
+
113
+
114
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_bf16"))
115
+ def _qkv_split_bias_norm_rope_v_bf16_fake(
116
+ packed_qkv: torch.Tensor,
117
+ qkv_bias: torch.Tensor,
118
+ norm_q_weight: torch.Tensor,
119
+ norm_k_weight: torch.Tensor,
120
+ freqs_re: torch.Tensor,
121
+ freqs_im: torch.Tensor,
122
+ heads: int,
123
+ head_dim: int,
124
+ rope_seq_len: int,
125
+ eps: float,
126
+ q_out: torch.Tensor,
127
+ k_out: torch.Tensor,
128
+ v_out: torch.Tensor,
129
+ ) -> None:
130
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
131
+ batch, seq_len, _ = packed_qkv.shape
132
+ dim = heads * head_dim
133
+ if qkv_bias.shape != (3 * dim,):
134
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
135
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
136
+ out_shape = (batch, seq_len, heads, head_dim)
137
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
138
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
139
+ return None
140
+
141
+
142
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_cat_bf16"))
143
+ def _qkv_split_bias_norm_rope_v_cat_bf16_fake(
144
+ packed_qkv: torch.Tensor,
145
+ qkv_bias: torch.Tensor,
146
+ norm_q_weight: torch.Tensor,
147
+ norm_k_weight: torch.Tensor,
148
+ freqs_re: torch.Tensor,
149
+ freqs_im: torch.Tensor,
150
+ heads: int,
151
+ head_dim: int,
152
+ video_offset: int,
153
+ rope_seq_len: int,
154
+ eps: float,
155
+ q_cat_out: torch.Tensor,
156
+ k_cat_out: torch.Tensor,
157
+ v_cat_out: torch.Tensor,
158
+ ) -> None:
159
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
160
+ batch, seq_len, _ = packed_qkv.shape
161
+ dim = heads * head_dim
162
+ if qkv_bias.shape != (3 * dim,):
163
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
164
+ if q_cat_out.dim() != 4:
165
+ raise RuntimeError("q_cat_out must have shape (batch, total_seq_len, heads, head_dim)")
166
+ total_seq_len = q_cat_out.shape[1]
167
+ if video_offset < 0 or video_offset + seq_len > total_seq_len:
168
+ raise RuntimeError("video_offset + packed_qkv.shape[1] must be within q_cat_out.shape[1]")
169
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
170
+ out_shape = (batch, total_seq_len, heads, head_dim)
171
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
172
+ raise RuntimeError("cat outputs must have shape (batch, total_seq_len, heads, head_dim)")
173
+ return None
174
+
175
+
176
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_joint3_cat_bf16"))
177
+ def _qkv_split_joint3_cat_bf16_fake(
178
+ packed_v: torch.Tensor,
179
+ qkv_v_bias: torch.Tensor,
180
+ norm_v_q_weight: torch.Tensor,
181
+ norm_v_k_weight: torch.Tensor,
182
+ freqs_re: torch.Tensor,
183
+ freqs_im: torch.Tensor,
184
+ packed_a: torch.Tensor,
185
+ norm_a_q_weight: torch.Tensor,
186
+ norm_a_k_weight: torch.Tensor,
187
+ packed_u: torch.Tensor,
188
+ norm_u_q_weight: torch.Tensor,
189
+ norm_u_k_weight: torch.Tensor,
190
+ heads: int,
191
+ head_dim: int,
192
+ rope_seq_len: int,
193
+ eps_v: float,
194
+ eps_a: float,
195
+ eps_u: float,
196
+ q_cat_out: torch.Tensor,
197
+ k_cat_out: torch.Tensor,
198
+ v_cat_out: torch.Tensor,
199
+ ) -> None:
200
+ _check_packed_qkv(packed_v, norm_v_q_weight, norm_v_k_weight, heads, head_dim)
201
+ _check_packed_qkv(packed_a, norm_a_q_weight, norm_a_k_weight, heads, head_dim)
202
+ _check_packed_qkv(packed_u, norm_u_q_weight, norm_u_k_weight, heads, head_dim)
203
+ batch = packed_v.shape[0]
204
+ if batch != 1 or packed_a.shape[0] != batch or packed_u.shape[0] != batch:
205
+ raise RuntimeError("qkv_split_joint3_cat_bf16 currently supports batch == 1")
206
+ total_seq_len = packed_v.shape[1] + packed_a.shape[1] + packed_u.shape[1]
207
+ dim = heads * head_dim
208
+ if qkv_v_bias.shape != (3 * dim,):
209
+ raise RuntimeError("qkv_v_bias must have shape (3 * heads * head_dim,)")
210
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
211
+ out_shape = (batch, total_seq_len, heads, head_dim)
212
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
213
+ raise RuntimeError("cat outputs must have shape (1, L_v + L_a + L_u, heads, head_dim)")
214
+ return None
215
+
216
+
217
+ def _check_packed_qkv(
218
+ packed_qkv: torch.Tensor,
219
+ norm_q_weight: torch.Tensor,
220
+ norm_k_weight: torch.Tensor,
221
+ heads: int,
222
+ head_dim: int,
223
+ ) -> None:
224
+ if packed_qkv.dim() != 3:
225
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
226
+ dim = heads * head_dim
227
+ if head_dim % 2 != 0 or packed_qkv.shape[2] != 3 * dim:
228
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim and head_dim must be even")
229
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
230
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
231
+
232
+
233
+ def _check_freqs(
234
+ freqs_re: torch.Tensor,
235
+ freqs_im: torch.Tensor,
236
+ head_dim: int,
237
+ rope_seq_len: int,
238
+ ) -> None:
239
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
240
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
241
+ if freqs_im.shape != freqs_re.shape:
242
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
243
+ if rope_seq_len < 0 or freqs_re.shape[0] < rope_seq_len:
244
+ raise RuntimeError("freqs_re must have at least rope_seq_len rows")
245
+
246
+
247
+ def qkv_split_norm_rope_bf16(
248
+ packed_qkv: torch.Tensor,
249
+ norm_q_weight: torch.Tensor,
250
+ norm_k_weight: torch.Tensor,
251
+ freqs_re: torch.Tensor,
252
+ freqs_im: torch.Tensor,
253
+ heads: int,
254
+ head_dim: int,
255
+ rope_seq_len: int | None = None,
256
+ eps: float = 1e-6,
257
+ q_out: torch.Tensor | None = None,
258
+ k_out: torch.Tensor | None = None,
259
+ ) -> tuple[torch.Tensor, torch.Tensor]:
260
+ """Split packed QKV, RMSNorm Q/K, apply RoPE, and return Q/K tensors.
261
+
262
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
263
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
264
+ """
265
+
266
+ if rope_seq_len is None:
267
+ rope_seq_len = packed_qkv.shape[1]
268
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
269
+ if q_out is None:
270
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
271
+ if k_out is None:
272
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
273
+ ops.qkv_split_norm_rope_bf16(
274
+ packed_qkv,
275
+ norm_q_weight,
276
+ norm_k_weight,
277
+ freqs_re,
278
+ freqs_im,
279
+ int(heads),
280
+ int(head_dim),
281
+ int(rope_seq_len),
282
+ float(eps),
283
+ q_out,
284
+ k_out,
285
+ )
286
+ return q_out, k_out
287
+
288
+
289
+ def decode_q_norm_rope_stage_bf16(
290
+ q_pre: torch.Tensor,
291
+ q_norm_weight: torch.Tensor,
292
+ cos: torch.Tensor,
293
+ sin: torch.Tensor,
294
+ eps: float = 1e-6,
295
+ q_out: torch.Tensor | None = None,
296
+ ) -> torch.Tensor:
297
+ """RMSNorm Q, apply rotate-half RoPE, and write a decode Q staging buffer.
298
+
299
+ The decode path is fixed to ``head_dim == 128``. ``cos`` and ``sin`` have
300
+ shape ``(64,)`` and dtype BF16.
301
+ """
302
+
303
+ if q_out is None:
304
+ q_out = torch.empty_like(q_pre)
305
+ ops.decode_q_norm_rope_stage_bf16(
306
+ q_pre, q_norm_weight, cos, sin, float(eps), q_out
307
+ )
308
+ return q_out
309
+
310
+
311
+ def decode_k_norm_rope_kvwrite_bf16(
312
+ k_pre: torch.Tensor,
313
+ v_pre: torch.Tensor,
314
+ k_norm_weight: torch.Tensor,
315
+ cos: torch.Tensor,
316
+ sin: torch.Tensor,
317
+ eps: float = 1e-6,
318
+ k_cache_dst: torch.Tensor | None = None,
319
+ v_cache_dst: torch.Tensor | None = None,
320
+ ) -> tuple[torch.Tensor, torch.Tensor]:
321
+ """RMSNorm K, apply rotate-half RoPE, and write one KV cache slot."""
322
+
323
+ if k_cache_dst is None:
324
+ k_cache_dst = torch.empty_like(k_pre)
325
+ if v_cache_dst is None:
326
+ v_cache_dst = torch.empty_like(v_pre)
327
+ ops.decode_k_norm_rope_kvwrite_bf16(
328
+ k_pre, v_pre, k_norm_weight, cos, sin, float(eps), k_cache_dst, v_cache_dst
329
+ )
330
+ return k_cache_dst, v_cache_dst
331
+
332
+
333
+ def decode_k_norm_rope_kvwrite_devpos_bf16(
334
+ k_pre: torch.Tensor,
335
+ v_pre: torch.Tensor,
336
+ k_norm_weight: torch.Tensor,
337
+ cos: torch.Tensor,
338
+ sin: torch.Tensor,
339
+ cur_pos: torch.Tensor,
340
+ k_cache: torch.Tensor,
341
+ v_cache: torch.Tensor,
342
+ eps: float = 1e-6,
343
+ ) -> tuple[torch.Tensor, torch.Tensor]:
344
+ """Write one KV cache slot selected by device int32 ``cur_pos``."""
345
+
346
+ ops.decode_k_norm_rope_kvwrite_devpos_bf16(
347
+ k_pre, v_pre, k_norm_weight, cos, sin, cur_pos, float(eps), k_cache, v_cache
348
+ )
349
+ return k_cache, v_cache
350
+
351
+
352
+ def qkv_split_bias_norm_rope_v_bf16(
353
+ packed_qkv: torch.Tensor,
354
+ qkv_bias: torch.Tensor,
355
+ norm_q_weight: torch.Tensor,
356
+ norm_k_weight: torch.Tensor,
357
+ freqs_re: torch.Tensor,
358
+ freqs_im: torch.Tensor,
359
+ heads: int,
360
+ head_dim: int,
361
+ rope_seq_len: int | None = None,
362
+ eps: float = 1e-6,
363
+ q_out: torch.Tensor | None = None,
364
+ k_out: torch.Tensor | None = None,
365
+ v_out: torch.Tensor | None = None,
366
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
367
+ """Bias QKV, RMSNorm Q/K, apply RoPE, and materialize Q/K/V."""
368
+
369
+ if rope_seq_len is None:
370
+ rope_seq_len = packed_qkv.shape[1]
371
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
372
+ if q_out is None:
373
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
374
+ if k_out is None:
375
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
376
+ if v_out is None:
377
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
378
+ ops.qkv_split_bias_norm_rope_v_bf16(
379
+ packed_qkv,
380
+ qkv_bias,
381
+ norm_q_weight,
382
+ norm_k_weight,
383
+ freqs_re,
384
+ freqs_im,
385
+ int(heads),
386
+ int(head_dim),
387
+ int(rope_seq_len),
388
+ float(eps),
389
+ q_out,
390
+ k_out,
391
+ v_out,
392
+ )
393
+ return q_out, k_out, v_out
394
+
395
+
396
+ def qkv_split_bias_norm_rope_v_cat_bf16(
397
+ packed_qkv: torch.Tensor,
398
+ qkv_bias: torch.Tensor,
399
+ norm_q_weight: torch.Tensor,
400
+ norm_k_weight: torch.Tensor,
401
+ freqs_re: torch.Tensor,
402
+ freqs_im: torch.Tensor,
403
+ heads: int,
404
+ head_dim: int,
405
+ video_offset: int,
406
+ q_cat_out: torch.Tensor,
407
+ k_cat_out: torch.Tensor,
408
+ v_cat_out: torch.Tensor,
409
+ rope_seq_len: int | None = None,
410
+ eps: float = 1e-6,
411
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
412
+ """Write a biased video QKV segment directly into joint Q/K/V workspaces."""
413
+
414
+ if rope_seq_len is None:
415
+ rope_seq_len = packed_qkv.shape[1]
416
+ ops.qkv_split_bias_norm_rope_v_cat_bf16(
417
+ packed_qkv,
418
+ qkv_bias,
419
+ norm_q_weight,
420
+ norm_k_weight,
421
+ freqs_re,
422
+ freqs_im,
423
+ int(heads),
424
+ int(head_dim),
425
+ int(video_offset),
426
+ int(rope_seq_len),
427
+ float(eps),
428
+ q_cat_out,
429
+ k_cat_out,
430
+ v_cat_out,
431
+ )
432
+ return q_cat_out, k_cat_out, v_cat_out
433
+
434
+
435
+ def qkv_split_joint3_cat_bf16(
436
+ packed_v: torch.Tensor,
437
+ qkv_v_bias: torch.Tensor,
438
+ norm_v_q_weight: torch.Tensor,
439
+ norm_v_k_weight: torch.Tensor,
440
+ freqs_re: torch.Tensor,
441
+ freqs_im: torch.Tensor,
442
+ packed_a: torch.Tensor,
443
+ norm_a_q_weight: torch.Tensor,
444
+ norm_a_k_weight: torch.Tensor,
445
+ packed_u: torch.Tensor,
446
+ norm_u_q_weight: torch.Tensor,
447
+ norm_u_k_weight: torch.Tensor,
448
+ heads: int,
449
+ head_dim: int,
450
+ q_cat_out: torch.Tensor,
451
+ k_cat_out: torch.Tensor,
452
+ v_cat_out: torch.Tensor,
453
+ rope_seq_len: int | None = None,
454
+ eps_v: float = 1e-6,
455
+ eps_a: float = 1e-6,
456
+ eps_u: float = 1e-6,
457
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
458
+ """Fuse video/action/und QKV postprocess into one joint Q/K/V workspace."""
459
+
460
+ if rope_seq_len is None:
461
+ rope_seq_len = packed_v.shape[1]
462
+ ops.qkv_split_joint3_cat_bf16(
463
+ packed_v,
464
+ qkv_v_bias,
465
+ norm_v_q_weight,
466
+ norm_v_k_weight,
467
+ freqs_re,
468
+ freqs_im,
469
+ packed_a,
470
+ norm_a_q_weight,
471
+ norm_a_k_weight,
472
+ packed_u,
473
+ norm_u_q_weight,
474
+ norm_u_k_weight,
475
+ int(heads),
476
+ int(head_dim),
477
+ int(rope_seq_len),
478
+ float(eps_v),
479
+ float(eps_a),
480
+ float(eps_u),
481
+ q_cat_out,
482
+ k_cat_out,
483
+ v_cat_out,
484
+ )
485
+ return q_cat_out, k_cat_out, v_cat_out
486
+
487
+
488
+ __all__ = [
489
+ "decode_q_norm_rope_stage_bf16",
490
+ "decode_k_norm_rope_kvwrite_bf16",
491
+ "decode_k_norm_rope_kvwrite_devpos_bf16",
492
+ "qkv_split_norm_rope_bf16",
493
+ "qkv_split_bias_norm_rope_v_bf16",
494
+ "qkv_split_bias_norm_rope_v_cat_bf16",
495
+ "qkv_split_joint3_cat_bf16",
496
+ ]
build/torch211-cxx11-cu128-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dc3cb9db8b4cc435477d30180ef7a20386e74514a14e328b942eee64e3397d1f
3
+ size 3646904
build/torch211-cxx11-cu128-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_cf903dd
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_cf903dd
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_flashrt_qkv_cache_rope_cuda_cf903dd::{op_name}"
build/torch211-cxx11-cu128-x86_64-linux/flashrt_qkv_cache_rope/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch211-cxx11-cu128-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_cf903dd",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "10.1",
12
+ "12.0+PTX",
13
+ "7.0",
14
+ "7.2",
15
+ "7.5",
16
+ "8.0",
17
+ "8.6",
18
+ "8.7",
19
+ "8.9",
20
+ "9.0"
21
+ ]
22
+ }
23
+ }
build/torch211-cxx11-cu130-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT QKV split, Q/K RMSNorm, and RoPE 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_decode_rope(x: torch.Tensor, weight: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, out: torch.Tensor, name: str) -> None:
11
+ if x.dim() != 2 or x.shape[1] != 128:
12
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
13
+ if weight.shape != (128,):
14
+ raise RuntimeError("norm weight must have shape (128,)")
15
+ if cos.shape != (64,) or sin.shape != (64,):
16
+ raise RuntimeError("cos and sin must have shape (64,)")
17
+ if out.shape != x.shape:
18
+ raise RuntimeError("out must have the same shape as input")
19
+
20
+
21
+ @torch.library.register_fake(add_op_namespace_prefix("decode_q_norm_rope_stage_bf16"))
22
+ def _decode_q_norm_rope_stage_bf16_fake(
23
+ q_pre: torch.Tensor,
24
+ q_norm_weight: torch.Tensor,
25
+ cos: torch.Tensor,
26
+ sin: torch.Tensor,
27
+ eps: float,
28
+ q_out: torch.Tensor,
29
+ ) -> None:
30
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
31
+ return None
32
+
33
+
34
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_bf16"))
35
+ def _decode_k_norm_rope_kvwrite_bf16_fake(
36
+ k_pre: torch.Tensor,
37
+ v_pre: torch.Tensor,
38
+ k_norm_weight: torch.Tensor,
39
+ cos: torch.Tensor,
40
+ sin: torch.Tensor,
41
+ eps: float,
42
+ k_cache_dst: torch.Tensor,
43
+ v_cache_dst: torch.Tensor,
44
+ ) -> None:
45
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
46
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
47
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
48
+ return None
49
+
50
+
51
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_devpos_bf16"))
52
+ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
53
+ k_pre: torch.Tensor,
54
+ v_pre: torch.Tensor,
55
+ k_norm_weight: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ cur_pos: torch.Tensor,
59
+ eps: float,
60
+ k_cache: torch.Tensor,
61
+ v_cache: torch.Tensor,
62
+ ) -> None:
63
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
64
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
65
+ n_kv = k_pre.shape[0]
66
+ if v_pre.shape != k_pre.shape:
67
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
68
+ if k_norm_weight.shape != (128,):
69
+ raise RuntimeError("k_norm_weight must have shape (128,)")
70
+ if cos.shape != (64,) or sin.shape != (64,):
71
+ raise RuntimeError("cos and sin must have shape (64,)")
72
+ if cur_pos.numel() != 1:
73
+ raise RuntimeError("cur_pos must have one int32 element")
74
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
75
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
76
+ if v_cache.shape != k_cache.shape:
77
+ raise RuntimeError("v_cache must have the same shape as k_cache")
78
+ return None
79
+
80
+
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
82
+ def _qkv_split_norm_rope_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ norm_q_weight: torch.Tensor,
85
+ norm_k_weight: torch.Tensor,
86
+ freqs_re: torch.Tensor,
87
+ freqs_im: torch.Tensor,
88
+ heads: int,
89
+ head_dim: int,
90
+ rope_seq_len: int,
91
+ eps: float,
92
+ q_out: torch.Tensor,
93
+ k_out: torch.Tensor,
94
+ ) -> None:
95
+ if packed_qkv.dim() != 3:
96
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
97
+ batch, seq_len, cols = packed_qkv.shape
98
+ dim = heads * head_dim
99
+ if cols != 3 * dim:
100
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
101
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
102
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
103
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
104
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
105
+ if freqs_im.shape != freqs_re.shape:
106
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
107
+ if q_out.shape != (batch, seq_len, heads, head_dim):
108
+ raise RuntimeError("q_out must have shape (batch, seq_len, heads, head_dim)")
109
+ if k_out.shape != q_out.shape:
110
+ raise RuntimeError("k_out must have the same shape as q_out")
111
+ return None
112
+
113
+
114
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_bf16"))
115
+ def _qkv_split_bias_norm_rope_v_bf16_fake(
116
+ packed_qkv: torch.Tensor,
117
+ qkv_bias: torch.Tensor,
118
+ norm_q_weight: torch.Tensor,
119
+ norm_k_weight: torch.Tensor,
120
+ freqs_re: torch.Tensor,
121
+ freqs_im: torch.Tensor,
122
+ heads: int,
123
+ head_dim: int,
124
+ rope_seq_len: int,
125
+ eps: float,
126
+ q_out: torch.Tensor,
127
+ k_out: torch.Tensor,
128
+ v_out: torch.Tensor,
129
+ ) -> None:
130
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
131
+ batch, seq_len, _ = packed_qkv.shape
132
+ dim = heads * head_dim
133
+ if qkv_bias.shape != (3 * dim,):
134
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
135
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
136
+ out_shape = (batch, seq_len, heads, head_dim)
137
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
138
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
139
+ return None
140
+
141
+
142
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_cat_bf16"))
143
+ def _qkv_split_bias_norm_rope_v_cat_bf16_fake(
144
+ packed_qkv: torch.Tensor,
145
+ qkv_bias: torch.Tensor,
146
+ norm_q_weight: torch.Tensor,
147
+ norm_k_weight: torch.Tensor,
148
+ freqs_re: torch.Tensor,
149
+ freqs_im: torch.Tensor,
150
+ heads: int,
151
+ head_dim: int,
152
+ video_offset: int,
153
+ rope_seq_len: int,
154
+ eps: float,
155
+ q_cat_out: torch.Tensor,
156
+ k_cat_out: torch.Tensor,
157
+ v_cat_out: torch.Tensor,
158
+ ) -> None:
159
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
160
+ batch, seq_len, _ = packed_qkv.shape
161
+ dim = heads * head_dim
162
+ if qkv_bias.shape != (3 * dim,):
163
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
164
+ if q_cat_out.dim() != 4:
165
+ raise RuntimeError("q_cat_out must have shape (batch, total_seq_len, heads, head_dim)")
166
+ total_seq_len = q_cat_out.shape[1]
167
+ if video_offset < 0 or video_offset + seq_len > total_seq_len:
168
+ raise RuntimeError("video_offset + packed_qkv.shape[1] must be within q_cat_out.shape[1]")
169
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
170
+ out_shape = (batch, total_seq_len, heads, head_dim)
171
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
172
+ raise RuntimeError("cat outputs must have shape (batch, total_seq_len, heads, head_dim)")
173
+ return None
174
+
175
+
176
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_joint3_cat_bf16"))
177
+ def _qkv_split_joint3_cat_bf16_fake(
178
+ packed_v: torch.Tensor,
179
+ qkv_v_bias: torch.Tensor,
180
+ norm_v_q_weight: torch.Tensor,
181
+ norm_v_k_weight: torch.Tensor,
182
+ freqs_re: torch.Tensor,
183
+ freqs_im: torch.Tensor,
184
+ packed_a: torch.Tensor,
185
+ norm_a_q_weight: torch.Tensor,
186
+ norm_a_k_weight: torch.Tensor,
187
+ packed_u: torch.Tensor,
188
+ norm_u_q_weight: torch.Tensor,
189
+ norm_u_k_weight: torch.Tensor,
190
+ heads: int,
191
+ head_dim: int,
192
+ rope_seq_len: int,
193
+ eps_v: float,
194
+ eps_a: float,
195
+ eps_u: float,
196
+ q_cat_out: torch.Tensor,
197
+ k_cat_out: torch.Tensor,
198
+ v_cat_out: torch.Tensor,
199
+ ) -> None:
200
+ _check_packed_qkv(packed_v, norm_v_q_weight, norm_v_k_weight, heads, head_dim)
201
+ _check_packed_qkv(packed_a, norm_a_q_weight, norm_a_k_weight, heads, head_dim)
202
+ _check_packed_qkv(packed_u, norm_u_q_weight, norm_u_k_weight, heads, head_dim)
203
+ batch = packed_v.shape[0]
204
+ if batch != 1 or packed_a.shape[0] != batch or packed_u.shape[0] != batch:
205
+ raise RuntimeError("qkv_split_joint3_cat_bf16 currently supports batch == 1")
206
+ total_seq_len = packed_v.shape[1] + packed_a.shape[1] + packed_u.shape[1]
207
+ dim = heads * head_dim
208
+ if qkv_v_bias.shape != (3 * dim,):
209
+ raise RuntimeError("qkv_v_bias must have shape (3 * heads * head_dim,)")
210
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
211
+ out_shape = (batch, total_seq_len, heads, head_dim)
212
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
213
+ raise RuntimeError("cat outputs must have shape (1, L_v + L_a + L_u, heads, head_dim)")
214
+ return None
215
+
216
+
217
+ def _check_packed_qkv(
218
+ packed_qkv: torch.Tensor,
219
+ norm_q_weight: torch.Tensor,
220
+ norm_k_weight: torch.Tensor,
221
+ heads: int,
222
+ head_dim: int,
223
+ ) -> None:
224
+ if packed_qkv.dim() != 3:
225
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
226
+ dim = heads * head_dim
227
+ if head_dim % 2 != 0 or packed_qkv.shape[2] != 3 * dim:
228
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim and head_dim must be even")
229
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
230
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
231
+
232
+
233
+ def _check_freqs(
234
+ freqs_re: torch.Tensor,
235
+ freqs_im: torch.Tensor,
236
+ head_dim: int,
237
+ rope_seq_len: int,
238
+ ) -> None:
239
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
240
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
241
+ if freqs_im.shape != freqs_re.shape:
242
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
243
+ if rope_seq_len < 0 or freqs_re.shape[0] < rope_seq_len:
244
+ raise RuntimeError("freqs_re must have at least rope_seq_len rows")
245
+
246
+
247
+ def qkv_split_norm_rope_bf16(
248
+ packed_qkv: torch.Tensor,
249
+ norm_q_weight: torch.Tensor,
250
+ norm_k_weight: torch.Tensor,
251
+ freqs_re: torch.Tensor,
252
+ freqs_im: torch.Tensor,
253
+ heads: int,
254
+ head_dim: int,
255
+ rope_seq_len: int | None = None,
256
+ eps: float = 1e-6,
257
+ q_out: torch.Tensor | None = None,
258
+ k_out: torch.Tensor | None = None,
259
+ ) -> tuple[torch.Tensor, torch.Tensor]:
260
+ """Split packed QKV, RMSNorm Q/K, apply RoPE, and return Q/K tensors.
261
+
262
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
263
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
264
+ """
265
+
266
+ if rope_seq_len is None:
267
+ rope_seq_len = packed_qkv.shape[1]
268
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
269
+ if q_out is None:
270
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
271
+ if k_out is None:
272
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
273
+ ops.qkv_split_norm_rope_bf16(
274
+ packed_qkv,
275
+ norm_q_weight,
276
+ norm_k_weight,
277
+ freqs_re,
278
+ freqs_im,
279
+ int(heads),
280
+ int(head_dim),
281
+ int(rope_seq_len),
282
+ float(eps),
283
+ q_out,
284
+ k_out,
285
+ )
286
+ return q_out, k_out
287
+
288
+
289
+ def decode_q_norm_rope_stage_bf16(
290
+ q_pre: torch.Tensor,
291
+ q_norm_weight: torch.Tensor,
292
+ cos: torch.Tensor,
293
+ sin: torch.Tensor,
294
+ eps: float = 1e-6,
295
+ q_out: torch.Tensor | None = None,
296
+ ) -> torch.Tensor:
297
+ """RMSNorm Q, apply rotate-half RoPE, and write a decode Q staging buffer.
298
+
299
+ The decode path is fixed to ``head_dim == 128``. ``cos`` and ``sin`` have
300
+ shape ``(64,)`` and dtype BF16.
301
+ """
302
+
303
+ if q_out is None:
304
+ q_out = torch.empty_like(q_pre)
305
+ ops.decode_q_norm_rope_stage_bf16(
306
+ q_pre, q_norm_weight, cos, sin, float(eps), q_out
307
+ )
308
+ return q_out
309
+
310
+
311
+ def decode_k_norm_rope_kvwrite_bf16(
312
+ k_pre: torch.Tensor,
313
+ v_pre: torch.Tensor,
314
+ k_norm_weight: torch.Tensor,
315
+ cos: torch.Tensor,
316
+ sin: torch.Tensor,
317
+ eps: float = 1e-6,
318
+ k_cache_dst: torch.Tensor | None = None,
319
+ v_cache_dst: torch.Tensor | None = None,
320
+ ) -> tuple[torch.Tensor, torch.Tensor]:
321
+ """RMSNorm K, apply rotate-half RoPE, and write one KV cache slot."""
322
+
323
+ if k_cache_dst is None:
324
+ k_cache_dst = torch.empty_like(k_pre)
325
+ if v_cache_dst is None:
326
+ v_cache_dst = torch.empty_like(v_pre)
327
+ ops.decode_k_norm_rope_kvwrite_bf16(
328
+ k_pre, v_pre, k_norm_weight, cos, sin, float(eps), k_cache_dst, v_cache_dst
329
+ )
330
+ return k_cache_dst, v_cache_dst
331
+
332
+
333
+ def decode_k_norm_rope_kvwrite_devpos_bf16(
334
+ k_pre: torch.Tensor,
335
+ v_pre: torch.Tensor,
336
+ k_norm_weight: torch.Tensor,
337
+ cos: torch.Tensor,
338
+ sin: torch.Tensor,
339
+ cur_pos: torch.Tensor,
340
+ k_cache: torch.Tensor,
341
+ v_cache: torch.Tensor,
342
+ eps: float = 1e-6,
343
+ ) -> tuple[torch.Tensor, torch.Tensor]:
344
+ """Write one KV cache slot selected by device int32 ``cur_pos``."""
345
+
346
+ ops.decode_k_norm_rope_kvwrite_devpos_bf16(
347
+ k_pre, v_pre, k_norm_weight, cos, sin, cur_pos, float(eps), k_cache, v_cache
348
+ )
349
+ return k_cache, v_cache
350
+
351
+
352
+ def qkv_split_bias_norm_rope_v_bf16(
353
+ packed_qkv: torch.Tensor,
354
+ qkv_bias: torch.Tensor,
355
+ norm_q_weight: torch.Tensor,
356
+ norm_k_weight: torch.Tensor,
357
+ freqs_re: torch.Tensor,
358
+ freqs_im: torch.Tensor,
359
+ heads: int,
360
+ head_dim: int,
361
+ rope_seq_len: int | None = None,
362
+ eps: float = 1e-6,
363
+ q_out: torch.Tensor | None = None,
364
+ k_out: torch.Tensor | None = None,
365
+ v_out: torch.Tensor | None = None,
366
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
367
+ """Bias QKV, RMSNorm Q/K, apply RoPE, and materialize Q/K/V."""
368
+
369
+ if rope_seq_len is None:
370
+ rope_seq_len = packed_qkv.shape[1]
371
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
372
+ if q_out is None:
373
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
374
+ if k_out is None:
375
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
376
+ if v_out is None:
377
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
378
+ ops.qkv_split_bias_norm_rope_v_bf16(
379
+ packed_qkv,
380
+ qkv_bias,
381
+ norm_q_weight,
382
+ norm_k_weight,
383
+ freqs_re,
384
+ freqs_im,
385
+ int(heads),
386
+ int(head_dim),
387
+ int(rope_seq_len),
388
+ float(eps),
389
+ q_out,
390
+ k_out,
391
+ v_out,
392
+ )
393
+ return q_out, k_out, v_out
394
+
395
+
396
+ def qkv_split_bias_norm_rope_v_cat_bf16(
397
+ packed_qkv: torch.Tensor,
398
+ qkv_bias: torch.Tensor,
399
+ norm_q_weight: torch.Tensor,
400
+ norm_k_weight: torch.Tensor,
401
+ freqs_re: torch.Tensor,
402
+ freqs_im: torch.Tensor,
403
+ heads: int,
404
+ head_dim: int,
405
+ video_offset: int,
406
+ q_cat_out: torch.Tensor,
407
+ k_cat_out: torch.Tensor,
408
+ v_cat_out: torch.Tensor,
409
+ rope_seq_len: int | None = None,
410
+ eps: float = 1e-6,
411
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
412
+ """Write a biased video QKV segment directly into joint Q/K/V workspaces."""
413
+
414
+ if rope_seq_len is None:
415
+ rope_seq_len = packed_qkv.shape[1]
416
+ ops.qkv_split_bias_norm_rope_v_cat_bf16(
417
+ packed_qkv,
418
+ qkv_bias,
419
+ norm_q_weight,
420
+ norm_k_weight,
421
+ freqs_re,
422
+ freqs_im,
423
+ int(heads),
424
+ int(head_dim),
425
+ int(video_offset),
426
+ int(rope_seq_len),
427
+ float(eps),
428
+ q_cat_out,
429
+ k_cat_out,
430
+ v_cat_out,
431
+ )
432
+ return q_cat_out, k_cat_out, v_cat_out
433
+
434
+
435
+ def qkv_split_joint3_cat_bf16(
436
+ packed_v: torch.Tensor,
437
+ qkv_v_bias: torch.Tensor,
438
+ norm_v_q_weight: torch.Tensor,
439
+ norm_v_k_weight: torch.Tensor,
440
+ freqs_re: torch.Tensor,
441
+ freqs_im: torch.Tensor,
442
+ packed_a: torch.Tensor,
443
+ norm_a_q_weight: torch.Tensor,
444
+ norm_a_k_weight: torch.Tensor,
445
+ packed_u: torch.Tensor,
446
+ norm_u_q_weight: torch.Tensor,
447
+ norm_u_k_weight: torch.Tensor,
448
+ heads: int,
449
+ head_dim: int,
450
+ q_cat_out: torch.Tensor,
451
+ k_cat_out: torch.Tensor,
452
+ v_cat_out: torch.Tensor,
453
+ rope_seq_len: int | None = None,
454
+ eps_v: float = 1e-6,
455
+ eps_a: float = 1e-6,
456
+ eps_u: float = 1e-6,
457
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
458
+ """Fuse video/action/und QKV postprocess into one joint Q/K/V workspace."""
459
+
460
+ if rope_seq_len is None:
461
+ rope_seq_len = packed_v.shape[1]
462
+ ops.qkv_split_joint3_cat_bf16(
463
+ packed_v,
464
+ qkv_v_bias,
465
+ norm_v_q_weight,
466
+ norm_v_k_weight,
467
+ freqs_re,
468
+ freqs_im,
469
+ packed_a,
470
+ norm_a_q_weight,
471
+ norm_a_k_weight,
472
+ packed_u,
473
+ norm_u_q_weight,
474
+ norm_u_k_weight,
475
+ int(heads),
476
+ int(head_dim),
477
+ int(rope_seq_len),
478
+ float(eps_v),
479
+ float(eps_a),
480
+ float(eps_u),
481
+ q_cat_out,
482
+ k_cat_out,
483
+ v_cat_out,
484
+ )
485
+ return q_cat_out, k_cat_out, v_cat_out
486
+
487
+
488
+ __all__ = [
489
+ "decode_q_norm_rope_stage_bf16",
490
+ "decode_k_norm_rope_kvwrite_bf16",
491
+ "decode_k_norm_rope_kvwrite_devpos_bf16",
492
+ "qkv_split_norm_rope_bf16",
493
+ "qkv_split_bias_norm_rope_v_bf16",
494
+ "qkv_split_bias_norm_rope_v_cat_bf16",
495
+ "qkv_split_joint3_cat_bf16",
496
+ ]
build/torch211-cxx11-cu130-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:905b694fd69586c964951f9024058991285177834a230f4a4f57ceef85b8f0f6
3
+ size 3404176
build/torch211-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_cf903dd
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_cf903dd
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_flashrt_qkv_cache_rope_cuda_cf903dd::{op_name}"
build/torch211-cxx11-cu130-x86_64-linux/flashrt_qkv_cache_rope/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch211-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_cf903dd",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "11.0",
12
+ "12.0+PTX",
13
+ "7.5",
14
+ "8.0",
15
+ "8.6",
16
+ "8.7",
17
+ "8.9",
18
+ "9.0"
19
+ ]
20
+ }
21
+ }
build/torch212-cxx11-cu130-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT QKV split, Q/K RMSNorm, and RoPE 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_decode_rope(x: torch.Tensor, weight: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, out: torch.Tensor, name: str) -> None:
11
+ if x.dim() != 2 or x.shape[1] != 128:
12
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
13
+ if weight.shape != (128,):
14
+ raise RuntimeError("norm weight must have shape (128,)")
15
+ if cos.shape != (64,) or sin.shape != (64,):
16
+ raise RuntimeError("cos and sin must have shape (64,)")
17
+ if out.shape != x.shape:
18
+ raise RuntimeError("out must have the same shape as input")
19
+
20
+
21
+ @torch.library.register_fake(add_op_namespace_prefix("decode_q_norm_rope_stage_bf16"))
22
+ def _decode_q_norm_rope_stage_bf16_fake(
23
+ q_pre: torch.Tensor,
24
+ q_norm_weight: torch.Tensor,
25
+ cos: torch.Tensor,
26
+ sin: torch.Tensor,
27
+ eps: float,
28
+ q_out: torch.Tensor,
29
+ ) -> None:
30
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
31
+ return None
32
+
33
+
34
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_bf16"))
35
+ def _decode_k_norm_rope_kvwrite_bf16_fake(
36
+ k_pre: torch.Tensor,
37
+ v_pre: torch.Tensor,
38
+ k_norm_weight: torch.Tensor,
39
+ cos: torch.Tensor,
40
+ sin: torch.Tensor,
41
+ eps: float,
42
+ k_cache_dst: torch.Tensor,
43
+ v_cache_dst: torch.Tensor,
44
+ ) -> None:
45
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
46
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
47
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
48
+ return None
49
+
50
+
51
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_devpos_bf16"))
52
+ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
53
+ k_pre: torch.Tensor,
54
+ v_pre: torch.Tensor,
55
+ k_norm_weight: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ cur_pos: torch.Tensor,
59
+ eps: float,
60
+ k_cache: torch.Tensor,
61
+ v_cache: torch.Tensor,
62
+ ) -> None:
63
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
64
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
65
+ n_kv = k_pre.shape[0]
66
+ if v_pre.shape != k_pre.shape:
67
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
68
+ if k_norm_weight.shape != (128,):
69
+ raise RuntimeError("k_norm_weight must have shape (128,)")
70
+ if cos.shape != (64,) or sin.shape != (64,):
71
+ raise RuntimeError("cos and sin must have shape (64,)")
72
+ if cur_pos.numel() != 1:
73
+ raise RuntimeError("cur_pos must have one int32 element")
74
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
75
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
76
+ if v_cache.shape != k_cache.shape:
77
+ raise RuntimeError("v_cache must have the same shape as k_cache")
78
+ return None
79
+
80
+
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
82
+ def _qkv_split_norm_rope_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ norm_q_weight: torch.Tensor,
85
+ norm_k_weight: torch.Tensor,
86
+ freqs_re: torch.Tensor,
87
+ freqs_im: torch.Tensor,
88
+ heads: int,
89
+ head_dim: int,
90
+ rope_seq_len: int,
91
+ eps: float,
92
+ q_out: torch.Tensor,
93
+ k_out: torch.Tensor,
94
+ ) -> None:
95
+ if packed_qkv.dim() != 3:
96
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
97
+ batch, seq_len, cols = packed_qkv.shape
98
+ dim = heads * head_dim
99
+ if cols != 3 * dim:
100
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
101
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
102
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
103
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
104
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
105
+ if freqs_im.shape != freqs_re.shape:
106
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
107
+ if q_out.shape != (batch, seq_len, heads, head_dim):
108
+ raise RuntimeError("q_out must have shape (batch, seq_len, heads, head_dim)")
109
+ if k_out.shape != q_out.shape:
110
+ raise RuntimeError("k_out must have the same shape as q_out")
111
+ return None
112
+
113
+
114
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_bf16"))
115
+ def _qkv_split_bias_norm_rope_v_bf16_fake(
116
+ packed_qkv: torch.Tensor,
117
+ qkv_bias: torch.Tensor,
118
+ norm_q_weight: torch.Tensor,
119
+ norm_k_weight: torch.Tensor,
120
+ freqs_re: torch.Tensor,
121
+ freqs_im: torch.Tensor,
122
+ heads: int,
123
+ head_dim: int,
124
+ rope_seq_len: int,
125
+ eps: float,
126
+ q_out: torch.Tensor,
127
+ k_out: torch.Tensor,
128
+ v_out: torch.Tensor,
129
+ ) -> None:
130
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
131
+ batch, seq_len, _ = packed_qkv.shape
132
+ dim = heads * head_dim
133
+ if qkv_bias.shape != (3 * dim,):
134
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
135
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
136
+ out_shape = (batch, seq_len, heads, head_dim)
137
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
138
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
139
+ return None
140
+
141
+
142
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_cat_bf16"))
143
+ def _qkv_split_bias_norm_rope_v_cat_bf16_fake(
144
+ packed_qkv: torch.Tensor,
145
+ qkv_bias: torch.Tensor,
146
+ norm_q_weight: torch.Tensor,
147
+ norm_k_weight: torch.Tensor,
148
+ freqs_re: torch.Tensor,
149
+ freqs_im: torch.Tensor,
150
+ heads: int,
151
+ head_dim: int,
152
+ video_offset: int,
153
+ rope_seq_len: int,
154
+ eps: float,
155
+ q_cat_out: torch.Tensor,
156
+ k_cat_out: torch.Tensor,
157
+ v_cat_out: torch.Tensor,
158
+ ) -> None:
159
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
160
+ batch, seq_len, _ = packed_qkv.shape
161
+ dim = heads * head_dim
162
+ if qkv_bias.shape != (3 * dim,):
163
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
164
+ if q_cat_out.dim() != 4:
165
+ raise RuntimeError("q_cat_out must have shape (batch, total_seq_len, heads, head_dim)")
166
+ total_seq_len = q_cat_out.shape[1]
167
+ if video_offset < 0 or video_offset + seq_len > total_seq_len:
168
+ raise RuntimeError("video_offset + packed_qkv.shape[1] must be within q_cat_out.shape[1]")
169
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
170
+ out_shape = (batch, total_seq_len, heads, head_dim)
171
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
172
+ raise RuntimeError("cat outputs must have shape (batch, total_seq_len, heads, head_dim)")
173
+ return None
174
+
175
+
176
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_joint3_cat_bf16"))
177
+ def _qkv_split_joint3_cat_bf16_fake(
178
+ packed_v: torch.Tensor,
179
+ qkv_v_bias: torch.Tensor,
180
+ norm_v_q_weight: torch.Tensor,
181
+ norm_v_k_weight: torch.Tensor,
182
+ freqs_re: torch.Tensor,
183
+ freqs_im: torch.Tensor,
184
+ packed_a: torch.Tensor,
185
+ norm_a_q_weight: torch.Tensor,
186
+ norm_a_k_weight: torch.Tensor,
187
+ packed_u: torch.Tensor,
188
+ norm_u_q_weight: torch.Tensor,
189
+ norm_u_k_weight: torch.Tensor,
190
+ heads: int,
191
+ head_dim: int,
192
+ rope_seq_len: int,
193
+ eps_v: float,
194
+ eps_a: float,
195
+ eps_u: float,
196
+ q_cat_out: torch.Tensor,
197
+ k_cat_out: torch.Tensor,
198
+ v_cat_out: torch.Tensor,
199
+ ) -> None:
200
+ _check_packed_qkv(packed_v, norm_v_q_weight, norm_v_k_weight, heads, head_dim)
201
+ _check_packed_qkv(packed_a, norm_a_q_weight, norm_a_k_weight, heads, head_dim)
202
+ _check_packed_qkv(packed_u, norm_u_q_weight, norm_u_k_weight, heads, head_dim)
203
+ batch = packed_v.shape[0]
204
+ if batch != 1 or packed_a.shape[0] != batch or packed_u.shape[0] != batch:
205
+ raise RuntimeError("qkv_split_joint3_cat_bf16 currently supports batch == 1")
206
+ total_seq_len = packed_v.shape[1] + packed_a.shape[1] + packed_u.shape[1]
207
+ dim = heads * head_dim
208
+ if qkv_v_bias.shape != (3 * dim,):
209
+ raise RuntimeError("qkv_v_bias must have shape (3 * heads * head_dim,)")
210
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
211
+ out_shape = (batch, total_seq_len, heads, head_dim)
212
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
213
+ raise RuntimeError("cat outputs must have shape (1, L_v + L_a + L_u, heads, head_dim)")
214
+ return None
215
+
216
+
217
+ def _check_packed_qkv(
218
+ packed_qkv: torch.Tensor,
219
+ norm_q_weight: torch.Tensor,
220
+ norm_k_weight: torch.Tensor,
221
+ heads: int,
222
+ head_dim: int,
223
+ ) -> None:
224
+ if packed_qkv.dim() != 3:
225
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
226
+ dim = heads * head_dim
227
+ if head_dim % 2 != 0 or packed_qkv.shape[2] != 3 * dim:
228
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim and head_dim must be even")
229
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
230
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
231
+
232
+
233
+ def _check_freqs(
234
+ freqs_re: torch.Tensor,
235
+ freqs_im: torch.Tensor,
236
+ head_dim: int,
237
+ rope_seq_len: int,
238
+ ) -> None:
239
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
240
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
241
+ if freqs_im.shape != freqs_re.shape:
242
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
243
+ if rope_seq_len < 0 or freqs_re.shape[0] < rope_seq_len:
244
+ raise RuntimeError("freqs_re must have at least rope_seq_len rows")
245
+
246
+
247
+ def qkv_split_norm_rope_bf16(
248
+ packed_qkv: torch.Tensor,
249
+ norm_q_weight: torch.Tensor,
250
+ norm_k_weight: torch.Tensor,
251
+ freqs_re: torch.Tensor,
252
+ freqs_im: torch.Tensor,
253
+ heads: int,
254
+ head_dim: int,
255
+ rope_seq_len: int | None = None,
256
+ eps: float = 1e-6,
257
+ q_out: torch.Tensor | None = None,
258
+ k_out: torch.Tensor | None = None,
259
+ ) -> tuple[torch.Tensor, torch.Tensor]:
260
+ """Split packed QKV, RMSNorm Q/K, apply RoPE, and return Q/K tensors.
261
+
262
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
263
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
264
+ """
265
+
266
+ if rope_seq_len is None:
267
+ rope_seq_len = packed_qkv.shape[1]
268
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
269
+ if q_out is None:
270
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
271
+ if k_out is None:
272
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
273
+ ops.qkv_split_norm_rope_bf16(
274
+ packed_qkv,
275
+ norm_q_weight,
276
+ norm_k_weight,
277
+ freqs_re,
278
+ freqs_im,
279
+ int(heads),
280
+ int(head_dim),
281
+ int(rope_seq_len),
282
+ float(eps),
283
+ q_out,
284
+ k_out,
285
+ )
286
+ return q_out, k_out
287
+
288
+
289
+ def decode_q_norm_rope_stage_bf16(
290
+ q_pre: torch.Tensor,
291
+ q_norm_weight: torch.Tensor,
292
+ cos: torch.Tensor,
293
+ sin: torch.Tensor,
294
+ eps: float = 1e-6,
295
+ q_out: torch.Tensor | None = None,
296
+ ) -> torch.Tensor:
297
+ """RMSNorm Q, apply rotate-half RoPE, and write a decode Q staging buffer.
298
+
299
+ The decode path is fixed to ``head_dim == 128``. ``cos`` and ``sin`` have
300
+ shape ``(64,)`` and dtype BF16.
301
+ """
302
+
303
+ if q_out is None:
304
+ q_out = torch.empty_like(q_pre)
305
+ ops.decode_q_norm_rope_stage_bf16(
306
+ q_pre, q_norm_weight, cos, sin, float(eps), q_out
307
+ )
308
+ return q_out
309
+
310
+
311
+ def decode_k_norm_rope_kvwrite_bf16(
312
+ k_pre: torch.Tensor,
313
+ v_pre: torch.Tensor,
314
+ k_norm_weight: torch.Tensor,
315
+ cos: torch.Tensor,
316
+ sin: torch.Tensor,
317
+ eps: float = 1e-6,
318
+ k_cache_dst: torch.Tensor | None = None,
319
+ v_cache_dst: torch.Tensor | None = None,
320
+ ) -> tuple[torch.Tensor, torch.Tensor]:
321
+ """RMSNorm K, apply rotate-half RoPE, and write one KV cache slot."""
322
+
323
+ if k_cache_dst is None:
324
+ k_cache_dst = torch.empty_like(k_pre)
325
+ if v_cache_dst is None:
326
+ v_cache_dst = torch.empty_like(v_pre)
327
+ ops.decode_k_norm_rope_kvwrite_bf16(
328
+ k_pre, v_pre, k_norm_weight, cos, sin, float(eps), k_cache_dst, v_cache_dst
329
+ )
330
+ return k_cache_dst, v_cache_dst
331
+
332
+
333
+ def decode_k_norm_rope_kvwrite_devpos_bf16(
334
+ k_pre: torch.Tensor,
335
+ v_pre: torch.Tensor,
336
+ k_norm_weight: torch.Tensor,
337
+ cos: torch.Tensor,
338
+ sin: torch.Tensor,
339
+ cur_pos: torch.Tensor,
340
+ k_cache: torch.Tensor,
341
+ v_cache: torch.Tensor,
342
+ eps: float = 1e-6,
343
+ ) -> tuple[torch.Tensor, torch.Tensor]:
344
+ """Write one KV cache slot selected by device int32 ``cur_pos``."""
345
+
346
+ ops.decode_k_norm_rope_kvwrite_devpos_bf16(
347
+ k_pre, v_pre, k_norm_weight, cos, sin, cur_pos, float(eps), k_cache, v_cache
348
+ )
349
+ return k_cache, v_cache
350
+
351
+
352
+ def qkv_split_bias_norm_rope_v_bf16(
353
+ packed_qkv: torch.Tensor,
354
+ qkv_bias: torch.Tensor,
355
+ norm_q_weight: torch.Tensor,
356
+ norm_k_weight: torch.Tensor,
357
+ freqs_re: torch.Tensor,
358
+ freqs_im: torch.Tensor,
359
+ heads: int,
360
+ head_dim: int,
361
+ rope_seq_len: int | None = None,
362
+ eps: float = 1e-6,
363
+ q_out: torch.Tensor | None = None,
364
+ k_out: torch.Tensor | None = None,
365
+ v_out: torch.Tensor | None = None,
366
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
367
+ """Bias QKV, RMSNorm Q/K, apply RoPE, and materialize Q/K/V."""
368
+
369
+ if rope_seq_len is None:
370
+ rope_seq_len = packed_qkv.shape[1]
371
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
372
+ if q_out is None:
373
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
374
+ if k_out is None:
375
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
376
+ if v_out is None:
377
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
378
+ ops.qkv_split_bias_norm_rope_v_bf16(
379
+ packed_qkv,
380
+ qkv_bias,
381
+ norm_q_weight,
382
+ norm_k_weight,
383
+ freqs_re,
384
+ freqs_im,
385
+ int(heads),
386
+ int(head_dim),
387
+ int(rope_seq_len),
388
+ float(eps),
389
+ q_out,
390
+ k_out,
391
+ v_out,
392
+ )
393
+ return q_out, k_out, v_out
394
+
395
+
396
+ def qkv_split_bias_norm_rope_v_cat_bf16(
397
+ packed_qkv: torch.Tensor,
398
+ qkv_bias: torch.Tensor,
399
+ norm_q_weight: torch.Tensor,
400
+ norm_k_weight: torch.Tensor,
401
+ freqs_re: torch.Tensor,
402
+ freqs_im: torch.Tensor,
403
+ heads: int,
404
+ head_dim: int,
405
+ video_offset: int,
406
+ q_cat_out: torch.Tensor,
407
+ k_cat_out: torch.Tensor,
408
+ v_cat_out: torch.Tensor,
409
+ rope_seq_len: int | None = None,
410
+ eps: float = 1e-6,
411
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
412
+ """Write a biased video QKV segment directly into joint Q/K/V workspaces."""
413
+
414
+ if rope_seq_len is None:
415
+ rope_seq_len = packed_qkv.shape[1]
416
+ ops.qkv_split_bias_norm_rope_v_cat_bf16(
417
+ packed_qkv,
418
+ qkv_bias,
419
+ norm_q_weight,
420
+ norm_k_weight,
421
+ freqs_re,
422
+ freqs_im,
423
+ int(heads),
424
+ int(head_dim),
425
+ int(video_offset),
426
+ int(rope_seq_len),
427
+ float(eps),
428
+ q_cat_out,
429
+ k_cat_out,
430
+ v_cat_out,
431
+ )
432
+ return q_cat_out, k_cat_out, v_cat_out
433
+
434
+
435
+ def qkv_split_joint3_cat_bf16(
436
+ packed_v: torch.Tensor,
437
+ qkv_v_bias: torch.Tensor,
438
+ norm_v_q_weight: torch.Tensor,
439
+ norm_v_k_weight: torch.Tensor,
440
+ freqs_re: torch.Tensor,
441
+ freqs_im: torch.Tensor,
442
+ packed_a: torch.Tensor,
443
+ norm_a_q_weight: torch.Tensor,
444
+ norm_a_k_weight: torch.Tensor,
445
+ packed_u: torch.Tensor,
446
+ norm_u_q_weight: torch.Tensor,
447
+ norm_u_k_weight: torch.Tensor,
448
+ heads: int,
449
+ head_dim: int,
450
+ q_cat_out: torch.Tensor,
451
+ k_cat_out: torch.Tensor,
452
+ v_cat_out: torch.Tensor,
453
+ rope_seq_len: int | None = None,
454
+ eps_v: float = 1e-6,
455
+ eps_a: float = 1e-6,
456
+ eps_u: float = 1e-6,
457
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
458
+ """Fuse video/action/und QKV postprocess into one joint Q/K/V workspace."""
459
+
460
+ if rope_seq_len is None:
461
+ rope_seq_len = packed_v.shape[1]
462
+ ops.qkv_split_joint3_cat_bf16(
463
+ packed_v,
464
+ qkv_v_bias,
465
+ norm_v_q_weight,
466
+ norm_v_k_weight,
467
+ freqs_re,
468
+ freqs_im,
469
+ packed_a,
470
+ norm_a_q_weight,
471
+ norm_a_k_weight,
472
+ packed_u,
473
+ norm_u_q_weight,
474
+ norm_u_k_weight,
475
+ int(heads),
476
+ int(head_dim),
477
+ int(rope_seq_len),
478
+ float(eps_v),
479
+ float(eps_a),
480
+ float(eps_u),
481
+ q_cat_out,
482
+ k_cat_out,
483
+ v_cat_out,
484
+ )
485
+ return q_cat_out, k_cat_out, v_cat_out
486
+
487
+
488
+ __all__ = [
489
+ "decode_q_norm_rope_stage_bf16",
490
+ "decode_k_norm_rope_kvwrite_bf16",
491
+ "decode_k_norm_rope_kvwrite_devpos_bf16",
492
+ "qkv_split_norm_rope_bf16",
493
+ "qkv_split_bias_norm_rope_v_bf16",
494
+ "qkv_split_bias_norm_rope_v_cat_bf16",
495
+ "qkv_split_joint3_cat_bf16",
496
+ ]
build/torch212-cxx11-cu130-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5638894ffc0223761c86b73f75de3cfbe3128dff1f755f7e2463e4a6700d98d3
3
+ size 3409696
build/torch212-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_cf903dd
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_cf903dd
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_flashrt_qkv_cache_rope_cuda_cf903dd::{op_name}"
build/torch212-cxx11-cu130-x86_64-linux/flashrt_qkv_cache_rope/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch212-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_cf903dd",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "11.0",
12
+ "12.0+PTX",
13
+ "7.5",
14
+ "8.0",
15
+ "8.6",
16
+ "8.7",
17
+ "8.9",
18
+ "9.0"
19
+ ]
20
+ }
21
+ }
build/torch212-cxx11-cu132-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT QKV split, Q/K RMSNorm, and RoPE 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_decode_rope(x: torch.Tensor, weight: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, out: torch.Tensor, name: str) -> None:
11
+ if x.dim() != 2 or x.shape[1] != 128:
12
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
13
+ if weight.shape != (128,):
14
+ raise RuntimeError("norm weight must have shape (128,)")
15
+ if cos.shape != (64,) or sin.shape != (64,):
16
+ raise RuntimeError("cos and sin must have shape (64,)")
17
+ if out.shape != x.shape:
18
+ raise RuntimeError("out must have the same shape as input")
19
+
20
+
21
+ @torch.library.register_fake(add_op_namespace_prefix("decode_q_norm_rope_stage_bf16"))
22
+ def _decode_q_norm_rope_stage_bf16_fake(
23
+ q_pre: torch.Tensor,
24
+ q_norm_weight: torch.Tensor,
25
+ cos: torch.Tensor,
26
+ sin: torch.Tensor,
27
+ eps: float,
28
+ q_out: torch.Tensor,
29
+ ) -> None:
30
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
31
+ return None
32
+
33
+
34
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_bf16"))
35
+ def _decode_k_norm_rope_kvwrite_bf16_fake(
36
+ k_pre: torch.Tensor,
37
+ v_pre: torch.Tensor,
38
+ k_norm_weight: torch.Tensor,
39
+ cos: torch.Tensor,
40
+ sin: torch.Tensor,
41
+ eps: float,
42
+ k_cache_dst: torch.Tensor,
43
+ v_cache_dst: torch.Tensor,
44
+ ) -> None:
45
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
46
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
47
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
48
+ return None
49
+
50
+
51
+ @torch.library.register_fake(add_op_namespace_prefix("decode_k_norm_rope_kvwrite_devpos_bf16"))
52
+ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
53
+ k_pre: torch.Tensor,
54
+ v_pre: torch.Tensor,
55
+ k_norm_weight: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ cur_pos: torch.Tensor,
59
+ eps: float,
60
+ k_cache: torch.Tensor,
61
+ v_cache: torch.Tensor,
62
+ ) -> None:
63
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
64
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
65
+ n_kv = k_pre.shape[0]
66
+ if v_pre.shape != k_pre.shape:
67
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
68
+ if k_norm_weight.shape != (128,):
69
+ raise RuntimeError("k_norm_weight must have shape (128,)")
70
+ if cos.shape != (64,) or sin.shape != (64,):
71
+ raise RuntimeError("cos and sin must have shape (64,)")
72
+ if cur_pos.numel() != 1:
73
+ raise RuntimeError("cur_pos must have one int32 element")
74
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
75
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
76
+ if v_cache.shape != k_cache.shape:
77
+ raise RuntimeError("v_cache must have the same shape as k_cache")
78
+ return None
79
+
80
+
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
82
+ def _qkv_split_norm_rope_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ norm_q_weight: torch.Tensor,
85
+ norm_k_weight: torch.Tensor,
86
+ freqs_re: torch.Tensor,
87
+ freqs_im: torch.Tensor,
88
+ heads: int,
89
+ head_dim: int,
90
+ rope_seq_len: int,
91
+ eps: float,
92
+ q_out: torch.Tensor,
93
+ k_out: torch.Tensor,
94
+ ) -> None:
95
+ if packed_qkv.dim() != 3:
96
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
97
+ batch, seq_len, cols = packed_qkv.shape
98
+ dim = heads * head_dim
99
+ if cols != 3 * dim:
100
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
101
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
102
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
103
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
104
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
105
+ if freqs_im.shape != freqs_re.shape:
106
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
107
+ if q_out.shape != (batch, seq_len, heads, head_dim):
108
+ raise RuntimeError("q_out must have shape (batch, seq_len, heads, head_dim)")
109
+ if k_out.shape != q_out.shape:
110
+ raise RuntimeError("k_out must have the same shape as q_out")
111
+ return None
112
+
113
+
114
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_bf16"))
115
+ def _qkv_split_bias_norm_rope_v_bf16_fake(
116
+ packed_qkv: torch.Tensor,
117
+ qkv_bias: torch.Tensor,
118
+ norm_q_weight: torch.Tensor,
119
+ norm_k_weight: torch.Tensor,
120
+ freqs_re: torch.Tensor,
121
+ freqs_im: torch.Tensor,
122
+ heads: int,
123
+ head_dim: int,
124
+ rope_seq_len: int,
125
+ eps: float,
126
+ q_out: torch.Tensor,
127
+ k_out: torch.Tensor,
128
+ v_out: torch.Tensor,
129
+ ) -> None:
130
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
131
+ batch, seq_len, _ = packed_qkv.shape
132
+ dim = heads * head_dim
133
+ if qkv_bias.shape != (3 * dim,):
134
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
135
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
136
+ out_shape = (batch, seq_len, heads, head_dim)
137
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
138
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
139
+ return None
140
+
141
+
142
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bias_norm_rope_v_cat_bf16"))
143
+ def _qkv_split_bias_norm_rope_v_cat_bf16_fake(
144
+ packed_qkv: torch.Tensor,
145
+ qkv_bias: torch.Tensor,
146
+ norm_q_weight: torch.Tensor,
147
+ norm_k_weight: torch.Tensor,
148
+ freqs_re: torch.Tensor,
149
+ freqs_im: torch.Tensor,
150
+ heads: int,
151
+ head_dim: int,
152
+ video_offset: int,
153
+ rope_seq_len: int,
154
+ eps: float,
155
+ q_cat_out: torch.Tensor,
156
+ k_cat_out: torch.Tensor,
157
+ v_cat_out: torch.Tensor,
158
+ ) -> None:
159
+ _check_packed_qkv(packed_qkv, norm_q_weight, norm_k_weight, heads, head_dim)
160
+ batch, seq_len, _ = packed_qkv.shape
161
+ dim = heads * head_dim
162
+ if qkv_bias.shape != (3 * dim,):
163
+ raise RuntimeError("qkv_bias must have shape (3 * heads * head_dim,)")
164
+ if q_cat_out.dim() != 4:
165
+ raise RuntimeError("q_cat_out must have shape (batch, total_seq_len, heads, head_dim)")
166
+ total_seq_len = q_cat_out.shape[1]
167
+ if video_offset < 0 or video_offset + seq_len > total_seq_len:
168
+ raise RuntimeError("video_offset + packed_qkv.shape[1] must be within q_cat_out.shape[1]")
169
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
170
+ out_shape = (batch, total_seq_len, heads, head_dim)
171
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
172
+ raise RuntimeError("cat outputs must have shape (batch, total_seq_len, heads, head_dim)")
173
+ return None
174
+
175
+
176
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_joint3_cat_bf16"))
177
+ def _qkv_split_joint3_cat_bf16_fake(
178
+ packed_v: torch.Tensor,
179
+ qkv_v_bias: torch.Tensor,
180
+ norm_v_q_weight: torch.Tensor,
181
+ norm_v_k_weight: torch.Tensor,
182
+ freqs_re: torch.Tensor,
183
+ freqs_im: torch.Tensor,
184
+ packed_a: torch.Tensor,
185
+ norm_a_q_weight: torch.Tensor,
186
+ norm_a_k_weight: torch.Tensor,
187
+ packed_u: torch.Tensor,
188
+ norm_u_q_weight: torch.Tensor,
189
+ norm_u_k_weight: torch.Tensor,
190
+ heads: int,
191
+ head_dim: int,
192
+ rope_seq_len: int,
193
+ eps_v: float,
194
+ eps_a: float,
195
+ eps_u: float,
196
+ q_cat_out: torch.Tensor,
197
+ k_cat_out: torch.Tensor,
198
+ v_cat_out: torch.Tensor,
199
+ ) -> None:
200
+ _check_packed_qkv(packed_v, norm_v_q_weight, norm_v_k_weight, heads, head_dim)
201
+ _check_packed_qkv(packed_a, norm_a_q_weight, norm_a_k_weight, heads, head_dim)
202
+ _check_packed_qkv(packed_u, norm_u_q_weight, norm_u_k_weight, heads, head_dim)
203
+ batch = packed_v.shape[0]
204
+ if batch != 1 or packed_a.shape[0] != batch or packed_u.shape[0] != batch:
205
+ raise RuntimeError("qkv_split_joint3_cat_bf16 currently supports batch == 1")
206
+ total_seq_len = packed_v.shape[1] + packed_a.shape[1] + packed_u.shape[1]
207
+ dim = heads * head_dim
208
+ if qkv_v_bias.shape != (3 * dim,):
209
+ raise RuntimeError("qkv_v_bias must have shape (3 * heads * head_dim,)")
210
+ _check_freqs(freqs_re, freqs_im, head_dim, rope_seq_len)
211
+ out_shape = (batch, total_seq_len, heads, head_dim)
212
+ if q_cat_out.shape != out_shape or k_cat_out.shape != out_shape or v_cat_out.shape != out_shape:
213
+ raise RuntimeError("cat outputs must have shape (1, L_v + L_a + L_u, heads, head_dim)")
214
+ return None
215
+
216
+
217
+ def _check_packed_qkv(
218
+ packed_qkv: torch.Tensor,
219
+ norm_q_weight: torch.Tensor,
220
+ norm_k_weight: torch.Tensor,
221
+ heads: int,
222
+ head_dim: int,
223
+ ) -> None:
224
+ if packed_qkv.dim() != 3:
225
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
226
+ dim = heads * head_dim
227
+ if head_dim % 2 != 0 or packed_qkv.shape[2] != 3 * dim:
228
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim and head_dim must be even")
229
+ if norm_q_weight.shape != (dim,) or norm_k_weight.shape != (dim,):
230
+ raise RuntimeError("norm weights must have shape (heads * head_dim,)")
231
+
232
+
233
+ def _check_freqs(
234
+ freqs_re: torch.Tensor,
235
+ freqs_im: torch.Tensor,
236
+ head_dim: int,
237
+ rope_seq_len: int,
238
+ ) -> None:
239
+ if freqs_re.dim() != 2 or freqs_re.shape[1] != head_dim // 2:
240
+ raise RuntimeError("freqs_re must have shape (rope_seq_len, head_dim / 2)")
241
+ if freqs_im.shape != freqs_re.shape:
242
+ raise RuntimeError("freqs_im must have the same shape as freqs_re")
243
+ if rope_seq_len < 0 or freqs_re.shape[0] < rope_seq_len:
244
+ raise RuntimeError("freqs_re must have at least rope_seq_len rows")
245
+
246
+
247
+ def qkv_split_norm_rope_bf16(
248
+ packed_qkv: torch.Tensor,
249
+ norm_q_weight: torch.Tensor,
250
+ norm_k_weight: torch.Tensor,
251
+ freqs_re: torch.Tensor,
252
+ freqs_im: torch.Tensor,
253
+ heads: int,
254
+ head_dim: int,
255
+ rope_seq_len: int | None = None,
256
+ eps: float = 1e-6,
257
+ q_out: torch.Tensor | None = None,
258
+ k_out: torch.Tensor | None = None,
259
+ ) -> tuple[torch.Tensor, torch.Tensor]:
260
+ """Split packed QKV, RMSNorm Q/K, apply RoPE, and return Q/K tensors.
261
+
262
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
263
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
264
+ """
265
+
266
+ if rope_seq_len is None:
267
+ rope_seq_len = packed_qkv.shape[1]
268
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
269
+ if q_out is None:
270
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
271
+ if k_out is None:
272
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
273
+ ops.qkv_split_norm_rope_bf16(
274
+ packed_qkv,
275
+ norm_q_weight,
276
+ norm_k_weight,
277
+ freqs_re,
278
+ freqs_im,
279
+ int(heads),
280
+ int(head_dim),
281
+ int(rope_seq_len),
282
+ float(eps),
283
+ q_out,
284
+ k_out,
285
+ )
286
+ return q_out, k_out
287
+
288
+
289
+ def decode_q_norm_rope_stage_bf16(
290
+ q_pre: torch.Tensor,
291
+ q_norm_weight: torch.Tensor,
292
+ cos: torch.Tensor,
293
+ sin: torch.Tensor,
294
+ eps: float = 1e-6,
295
+ q_out: torch.Tensor | None = None,
296
+ ) -> torch.Tensor:
297
+ """RMSNorm Q, apply rotate-half RoPE, and write a decode Q staging buffer.
298
+
299
+ The decode path is fixed to ``head_dim == 128``. ``cos`` and ``sin`` have
300
+ shape ``(64,)`` and dtype BF16.
301
+ """
302
+
303
+ if q_out is None:
304
+ q_out = torch.empty_like(q_pre)
305
+ ops.decode_q_norm_rope_stage_bf16(
306
+ q_pre, q_norm_weight, cos, sin, float(eps), q_out
307
+ )
308
+ return q_out
309
+
310
+
311
+ def decode_k_norm_rope_kvwrite_bf16(
312
+ k_pre: torch.Tensor,
313
+ v_pre: torch.Tensor,
314
+ k_norm_weight: torch.Tensor,
315
+ cos: torch.Tensor,
316
+ sin: torch.Tensor,
317
+ eps: float = 1e-6,
318
+ k_cache_dst: torch.Tensor | None = None,
319
+ v_cache_dst: torch.Tensor | None = None,
320
+ ) -> tuple[torch.Tensor, torch.Tensor]:
321
+ """RMSNorm K, apply rotate-half RoPE, and write one KV cache slot."""
322
+
323
+ if k_cache_dst is None:
324
+ k_cache_dst = torch.empty_like(k_pre)
325
+ if v_cache_dst is None:
326
+ v_cache_dst = torch.empty_like(v_pre)
327
+ ops.decode_k_norm_rope_kvwrite_bf16(
328
+ k_pre, v_pre, k_norm_weight, cos, sin, float(eps), k_cache_dst, v_cache_dst
329
+ )
330
+ return k_cache_dst, v_cache_dst
331
+
332
+
333
+ def decode_k_norm_rope_kvwrite_devpos_bf16(
334
+ k_pre: torch.Tensor,
335
+ v_pre: torch.Tensor,
336
+ k_norm_weight: torch.Tensor,
337
+ cos: torch.Tensor,
338
+ sin: torch.Tensor,
339
+ cur_pos: torch.Tensor,
340
+ k_cache: torch.Tensor,
341
+ v_cache: torch.Tensor,
342
+ eps: float = 1e-6,
343
+ ) -> tuple[torch.Tensor, torch.Tensor]:
344
+ """Write one KV cache slot selected by device int32 ``cur_pos``."""
345
+
346
+ ops.decode_k_norm_rope_kvwrite_devpos_bf16(
347
+ k_pre, v_pre, k_norm_weight, cos, sin, cur_pos, float(eps), k_cache, v_cache
348
+ )
349
+ return k_cache, v_cache
350
+
351
+
352
+ def qkv_split_bias_norm_rope_v_bf16(
353
+ packed_qkv: torch.Tensor,
354
+ qkv_bias: torch.Tensor,
355
+ norm_q_weight: torch.Tensor,
356
+ norm_k_weight: torch.Tensor,
357
+ freqs_re: torch.Tensor,
358
+ freqs_im: torch.Tensor,
359
+ heads: int,
360
+ head_dim: int,
361
+ rope_seq_len: int | None = None,
362
+ eps: float = 1e-6,
363
+ q_out: torch.Tensor | None = None,
364
+ k_out: torch.Tensor | None = None,
365
+ v_out: torch.Tensor | None = None,
366
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
367
+ """Bias QKV, RMSNorm Q/K, apply RoPE, and materialize Q/K/V."""
368
+
369
+ if rope_seq_len is None:
370
+ rope_seq_len = packed_qkv.shape[1]
371
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
372
+ if q_out is None:
373
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
374
+ if k_out is None:
375
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
376
+ if v_out is None:
377
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
378
+ ops.qkv_split_bias_norm_rope_v_bf16(
379
+ packed_qkv,
380
+ qkv_bias,
381
+ norm_q_weight,
382
+ norm_k_weight,
383
+ freqs_re,
384
+ freqs_im,
385
+ int(heads),
386
+ int(head_dim),
387
+ int(rope_seq_len),
388
+ float(eps),
389
+ q_out,
390
+ k_out,
391
+ v_out,
392
+ )
393
+ return q_out, k_out, v_out
394
+
395
+
396
+ def qkv_split_bias_norm_rope_v_cat_bf16(
397
+ packed_qkv: torch.Tensor,
398
+ qkv_bias: torch.Tensor,
399
+ norm_q_weight: torch.Tensor,
400
+ norm_k_weight: torch.Tensor,
401
+ freqs_re: torch.Tensor,
402
+ freqs_im: torch.Tensor,
403
+ heads: int,
404
+ head_dim: int,
405
+ video_offset: int,
406
+ q_cat_out: torch.Tensor,
407
+ k_cat_out: torch.Tensor,
408
+ v_cat_out: torch.Tensor,
409
+ rope_seq_len: int | None = None,
410
+ eps: float = 1e-6,
411
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
412
+ """Write a biased video QKV segment directly into joint Q/K/V workspaces."""
413
+
414
+ if rope_seq_len is None:
415
+ rope_seq_len = packed_qkv.shape[1]
416
+ ops.qkv_split_bias_norm_rope_v_cat_bf16(
417
+ packed_qkv,
418
+ qkv_bias,
419
+ norm_q_weight,
420
+ norm_k_weight,
421
+ freqs_re,
422
+ freqs_im,
423
+ int(heads),
424
+ int(head_dim),
425
+ int(video_offset),
426
+ int(rope_seq_len),
427
+ float(eps),
428
+ q_cat_out,
429
+ k_cat_out,
430
+ v_cat_out,
431
+ )
432
+ return q_cat_out, k_cat_out, v_cat_out
433
+
434
+
435
+ def qkv_split_joint3_cat_bf16(
436
+ packed_v: torch.Tensor,
437
+ qkv_v_bias: torch.Tensor,
438
+ norm_v_q_weight: torch.Tensor,
439
+ norm_v_k_weight: torch.Tensor,
440
+ freqs_re: torch.Tensor,
441
+ freqs_im: torch.Tensor,
442
+ packed_a: torch.Tensor,
443
+ norm_a_q_weight: torch.Tensor,
444
+ norm_a_k_weight: torch.Tensor,
445
+ packed_u: torch.Tensor,
446
+ norm_u_q_weight: torch.Tensor,
447
+ norm_u_k_weight: torch.Tensor,
448
+ heads: int,
449
+ head_dim: int,
450
+ q_cat_out: torch.Tensor,
451
+ k_cat_out: torch.Tensor,
452
+ v_cat_out: torch.Tensor,
453
+ rope_seq_len: int | None = None,
454
+ eps_v: float = 1e-6,
455
+ eps_a: float = 1e-6,
456
+ eps_u: float = 1e-6,
457
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
458
+ """Fuse video/action/und QKV postprocess into one joint Q/K/V workspace."""
459
+
460
+ if rope_seq_len is None:
461
+ rope_seq_len = packed_v.shape[1]
462
+ ops.qkv_split_joint3_cat_bf16(
463
+ packed_v,
464
+ qkv_v_bias,
465
+ norm_v_q_weight,
466
+ norm_v_k_weight,
467
+ freqs_re,
468
+ freqs_im,
469
+ packed_a,
470
+ norm_a_q_weight,
471
+ norm_a_k_weight,
472
+ packed_u,
473
+ norm_u_q_weight,
474
+ norm_u_k_weight,
475
+ int(heads),
476
+ int(head_dim),
477
+ int(rope_seq_len),
478
+ float(eps_v),
479
+ float(eps_a),
480
+ float(eps_u),
481
+ q_cat_out,
482
+ k_cat_out,
483
+ v_cat_out,
484
+ )
485
+ return q_cat_out, k_cat_out, v_cat_out
486
+
487
+
488
+ __all__ = [
489
+ "decode_q_norm_rope_stage_bf16",
490
+ "decode_k_norm_rope_kvwrite_bf16",
491
+ "decode_k_norm_rope_kvwrite_devpos_bf16",
492
+ "qkv_split_norm_rope_bf16",
493
+ "qkv_split_bias_norm_rope_v_bf16",
494
+ "qkv_split_bias_norm_rope_v_cat_bf16",
495
+ "qkv_split_joint3_cat_bf16",
496
+ ]
build/torch212-cxx11-cu132-x86_64-linux/_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9f32e82da0aabbf1b514318a37adc5deccfd21d20d55e6f274c8c0e83d731f1f
3
+ size 3405568
build/torch212-cxx11-cu132-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_cf903dd
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_cf903dd
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_flashrt_qkv_cache_rope_cuda_cf903dd::{op_name}"
build/torch212-cxx11-cu132-x86_64-linux/flashrt_qkv_cache_rope/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch212-cxx11-cu132-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_cf903dd",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "11.0",
12
+ "12.0+PTX",
13
+ "7.5",
14
+ "8.0",
15
+ "8.6",
16
+ "8.7",
17
+ "8.9",
18
+ "9.0"
19
+ ]
20
+ }
21
+ }