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

Uploaded using `kernel-builder`.

Browse files
benchmarks/benchmark.py CHANGED
@@ -148,6 +148,47 @@ class SourceOps:
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:
@@ -202,6 +243,13 @@ def make_freqs(seq_len: int, head_dim: int):
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)
@@ -243,6 +291,20 @@ def apply_pair_rope(x: torch.Tensor, freqs_re: torch.Tensor, freqs_im: torch.Ten
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)
@@ -290,6 +352,16 @@ 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)
@@ -623,6 +695,85 @@ def run_decode_kv(ops, name: str, heads: int, devpos: bool, args) -> Result:
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 |",
@@ -658,11 +809,14 @@ def main() -> None:
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(
 
148
  )
149
  return q_cat_out, k_cat_out, v_cat_out
150
 
151
+ def qkv_split_rope_kvcache_bf16(
152
+ self,
153
+ packed_qkv,
154
+ rope,
155
+ q_heads,
156
+ kv_heads,
157
+ head_dim,
158
+ cache_offset,
159
+ q_out=None,
160
+ k_cache=None,
161
+ v_cache=None,
162
+ max_seq_len=None,
163
+ ):
164
+ batch, seq_len, _ = packed_qkv.shape
165
+ if q_out is None:
166
+ q_out = torch.empty(
167
+ (batch, seq_len, q_heads, head_dim),
168
+ device=packed_qkv.device,
169
+ dtype=torch.bfloat16,
170
+ )
171
+ if k_cache is None or v_cache is None:
172
+ if max_seq_len is None:
173
+ max_seq_len = cache_offset + seq_len
174
+ cache_shape = (batch, max_seq_len, kv_heads, head_dim)
175
+ if k_cache is None:
176
+ k_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
177
+ if v_cache is None:
178
+ v_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
179
+ self._ops.qkv_split_rope_kvcache_bf16(
180
+ packed_qkv,
181
+ rope,
182
+ int(q_heads),
183
+ int(kv_heads),
184
+ int(head_dim),
185
+ int(cache_offset),
186
+ q_out,
187
+ k_cache,
188
+ v_cache,
189
+ )
190
+ return q_out, k_cache, v_cache
191
+
192
 
193
  def _preload_cublaslt() -> None:
194
  for parent in Path(torch.__file__).resolve().parents:
 
243
  return torch.cos(theta).contiguous(), torch.sin(theta).contiguous()
244
 
245
 
246
+ def make_interleaved_rope(seq_len: int, head_dim: int):
247
+ theta = torch.randn((seq_len, head_dim // 2), device="cuda", dtype=torch.float32)
248
+ cos = torch.cos(theta).to(torch.bfloat16)
249
+ sin = torch.sin(theta).to(torch.bfloat16)
250
+ return torch.stack([cos, sin], dim=-1).reshape(seq_len, head_dim).contiguous()
251
+
252
+
253
  def make_case(batch: int, seq_len: int, heads: int, head_dim: int):
254
  dim = heads * head_dim
255
  packed = torch.randn((batch, seq_len, 3 * dim), device="cuda", dtype=torch.bfloat16)
 
291
  return out.reshape(batch, seq_len, heads, head_dim).to(torch.bfloat16)
292
 
293
 
294
+ def apply_interleaved_pair_rope(x: torch.Tensor, rope: torch.Tensor):
295
+ batch, seq_len, heads, head_dim = x.shape
296
+ pair = x.float().reshape(batch, seq_len, heads, head_dim // 2, 2)
297
+ re = pair[..., 0]
298
+ im = pair[..., 1]
299
+ rope_pair = rope[:seq_len].float().reshape(seq_len, head_dim // 2, 2)
300
+ cos = rope_pair[..., 0].view(1, seq_len, 1, head_dim // 2)
301
+ sin = rope_pair[..., 1].view(1, seq_len, 1, head_dim // 2)
302
+ out = torch.empty_like(pair.float())
303
+ out[..., 0] = re * cos - im * sin
304
+ out[..., 1] = re * sin + im * cos
305
+ return out.reshape(batch, seq_len, heads, head_dim).to(torch.bfloat16)
306
+
307
+
308
  def apply_rotate_half_rope_128(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor):
309
  xf = x.float()
310
  out = torch.empty_like(xf)
 
352
  return apply_rotate_half_rope_128(rms_norm(x, weight, eps).to(torch.bfloat16), cos, sin)
353
 
354
 
355
+ def torch_ref_kvcache(packed_qkv, rope, q_heads, kv_heads, head_dim):
356
+ batch, seq_len, _ = packed_qkv.shape
357
+ q_dim = q_heads * head_dim
358
+ kv_dim = kv_heads * head_dim
359
+ q = packed_qkv[:, :, :q_dim].view(batch, seq_len, q_heads, head_dim)
360
+ k = packed_qkv[:, :, q_dim : q_dim + kv_dim].view(batch, seq_len, kv_heads, head_dim)
361
+ v = packed_qkv[:, :, q_dim + kv_dim :].view(batch, seq_len, kv_heads, head_dim)
362
+ return apply_interleaved_pair_rope(q, rope), apply_interleaved_pair_rope(k, rope), v
363
+
364
+
365
  def make_joint3_case(video_len: int, action_len: int, und_len: int, heads: int, head_dim: int):
366
  packed_v, v_q_w, v_k_w, freqs_re, freqs_im, _, _ = make_case(1, video_len, heads, head_dim)
367
  packed_a, a_q_w, a_k_w, _, _, _, _ = make_case(1, action_len, heads, head_dim)
 
695
  )
696
 
697
 
698
+ def run_kvcache_gqa(
699
+ ops,
700
+ name: str,
701
+ batch: int,
702
+ seq_len: int,
703
+ q_heads: int,
704
+ kv_heads: int,
705
+ head_dim: int,
706
+ args,
707
+ ) -> Result:
708
+ qkv_dim = (q_heads + 2 * kv_heads) * head_dim
709
+ packed = torch.randn((batch, seq_len, qkv_dim), device="cuda", dtype=torch.bfloat16)
710
+ rope = make_interleaved_rope(seq_len, head_dim)
711
+ cache_offset = 2
712
+ max_seq_len = cache_offset + seq_len + 2
713
+ q_out = torch.empty((batch, seq_len, q_heads, head_dim), device="cuda", dtype=torch.bfloat16)
714
+ k_cache = torch.empty((batch, max_seq_len, kv_heads, head_dim), device="cuda", dtype=torch.bfloat16)
715
+ v_cache = torch.empty_like(k_cache)
716
+ got_q, got_k_cache, got_v_cache = ops.qkv_split_rope_kvcache_bf16(
717
+ packed,
718
+ rope,
719
+ q_heads,
720
+ kv_heads,
721
+ head_dim,
722
+ cache_offset,
723
+ q_out,
724
+ k_cache,
725
+ v_cache,
726
+ )
727
+ exp_q, exp_k, exp_v = torch_ref_kvcache(packed, rope, q_heads, kv_heads, head_dim)
728
+ sl = slice(cache_offset, cache_offset + seq_len)
729
+ q_p99, q_cos = metrics(got_q, exp_q)
730
+ k_p99, k_cos = metrics(got_k_cache[:, sl], exp_k)
731
+ v_p99, v_cos = metrics(got_v_cache[:, sl], exp_v)
732
+
733
+ def flashrt_fn():
734
+ return ops.qkv_split_rope_kvcache_bf16(
735
+ packed,
736
+ rope,
737
+ q_heads,
738
+ kv_heads,
739
+ head_dim,
740
+ cache_offset,
741
+ q_out,
742
+ k_cache,
743
+ v_cache,
744
+ )
745
+
746
+ def eager_fn():
747
+ exp_q_local, exp_k_local, exp_v_local = torch_ref_kvcache(packed, rope, q_heads, kv_heads, head_dim)
748
+ q_out.copy_(exp_q_local)
749
+ k_cache[:, sl].copy_(exp_k_local)
750
+ v_cache[:, sl].copy_(exp_v_local)
751
+ return q_out, k_cache, v_cache
752
+
753
+ flashrt_us = time_us(flashrt_fn, args.warmup, args.iters)
754
+ eager_us = time_us(eager_fn, args.warmup, args.iters)
755
+ status = (
756
+ "PASS"
757
+ if q_p99 <= args.p99_abs_limit and k_p99 <= args.p99_abs_limit and v_p99 == 0.0
758
+ else "FAIL"
759
+ )
760
+ return Result(
761
+ shape=name,
762
+ batch=batch,
763
+ seq_len=seq_len,
764
+ heads=q_heads,
765
+ head_dim=head_dim,
766
+ flashrt_us=flashrt_us,
767
+ torch_eager_us=eager_us,
768
+ speedup_vs_eager=eager_us / flashrt_us,
769
+ q_p99_abs=max(q_p99, v_p99),
770
+ k_p99_abs=k_p99,
771
+ q_cosine=min(q_cos, v_cos),
772
+ k_cosine=k_cos,
773
+ status=status,
774
+ )
775
+
776
+
777
  def write_markdown(path: Path, results: list[Result]) -> None:
778
  lines = [
779
  "| Shape | B,L,H,D | FlashRT us | Eager us | vs eager | Q p99 | K p99 | Q cosine | K cosine | Status |",
 
809
  results = [run_one(ops, name, SHAPES[name], args) for name in SHAPE_GROUPS[args.shapes]]
810
  if args.shapes in ("smoke", "all"):
811
  results.append(run_joint3(ops, "joint3_small", 64, 8, 4, 8, 128, args))
812
+ results.append(run_kvcache_gqa(ops, "pi05_decoder_gqa_kvcache", 1, 10, 8, 1, 256, args))
813
  if args.shapes in ("headline", "all"):
814
  results.append(run_joint3(ops, "joint3_vla", 2520, 16, 16, 24, 128, args))
815
  results.append(run_decode_q(ops, "decode_q_stage_h24", 24, args))
816
  results.append(run_decode_kv(ops, "decode_kvwrite_h8", 8, False, args))
817
  results.append(run_decode_kv(ops, "decode_kvwrite_devpos_h8", 8, True, args))
818
+ if args.shapes == "headline":
819
+ results.append(run_kvcache_gqa(ops, "pi05_decoder_gqa_kvcache", 1, 10, 8, 1, 256, args))
820
 
821
  for r in results:
822
  print(
build/torch211-cxx11-cu128-x86_64-linux/__init__.py CHANGED
@@ -78,6 +78,53 @@ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
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,
@@ -230,6 +277,21 @@ def _check_packed_qkv(
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,
@@ -286,6 +348,82 @@ def qkv_split_norm_rope_bf16(
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,
@@ -489,6 +627,8 @@ __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",
 
78
  return None
79
 
80
 
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_rope_kvcache_bf16"))
82
+ def _qkv_split_rope_kvcache_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ rope: torch.Tensor,
85
+ q_heads: int,
86
+ kv_heads: int,
87
+ head_dim: int,
88
+ cache_offset: int,
89
+ q_out: torch.Tensor,
90
+ k_cache: torch.Tensor,
91
+ v_cache: torch.Tensor,
92
+ ) -> None:
93
+ _check_packed_gqa_qkv(packed_qkv, q_heads, kv_heads, head_dim)
94
+ batch, seq_len, _ = packed_qkv.shape
95
+ if rope.dim() != 2 or rope.shape[0] < seq_len or rope.shape[1] != head_dim:
96
+ raise RuntimeError("rope must have shape (>= seq_len, head_dim)")
97
+ if q_out.shape != (batch, seq_len, q_heads, head_dim):
98
+ raise RuntimeError("q_out must have shape (batch, seq_len, q_heads, head_dim)")
99
+ if k_cache.dim() != 4 or k_cache.shape[0] != batch or k_cache.shape[2:] != (kv_heads, head_dim):
100
+ raise RuntimeError("k_cache must have shape (batch, max_seq_len, kv_heads, head_dim)")
101
+ if v_cache.shape != k_cache.shape:
102
+ raise RuntimeError("v_cache must have the same shape as k_cache")
103
+ if cache_offset < 0 or cache_offset + seq_len > k_cache.shape[1]:
104
+ raise RuntimeError("cache_offset + seq_len must be within k_cache.shape[1]")
105
+ return None
106
+
107
+
108
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bf16"))
109
+ def _qkv_split_bf16_fake(
110
+ packed_qkv: torch.Tensor,
111
+ heads: int,
112
+ head_dim: int,
113
+ q_out: torch.Tensor,
114
+ k_out: torch.Tensor,
115
+ v_out: torch.Tensor,
116
+ ) -> None:
117
+ if packed_qkv.dim() != 3:
118
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
119
+ batch, seq_len, cols = packed_qkv.shape
120
+ if cols != 3 * heads * head_dim:
121
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
122
+ out_shape = (batch, seq_len, heads, head_dim)
123
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
124
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
125
+ return None
126
+
127
+
128
  @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
129
  def _qkv_split_norm_rope_bf16_fake(
130
  packed_qkv: torch.Tensor,
 
277
  raise RuntimeError("norm weights must have shape (heads * head_dim,)")
278
 
279
 
280
+ def _check_packed_gqa_qkv(
281
+ packed_qkv: torch.Tensor,
282
+ q_heads: int,
283
+ kv_heads: int,
284
+ head_dim: int,
285
+ ) -> None:
286
+ if packed_qkv.dim() != 3:
287
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)")
288
+ if q_heads <= 0 or kv_heads <= 0 or head_dim <= 0 or head_dim % 2 != 0:
289
+ raise RuntimeError("q_heads, kv_heads, and even head_dim must be positive")
290
+ expected = (q_heads + 2 * kv_heads) * head_dim
291
+ if packed_qkv.shape[2] != expected:
292
+ raise RuntimeError("packed_qkv.shape[2] must be (q_heads + 2 * kv_heads) * head_dim")
293
+
294
+
295
  def _check_freqs(
296
  freqs_re: torch.Tensor,
297
  freqs_im: torch.Tensor,
 
348
  return q_out, k_out
349
 
350
 
351
+ def qkv_split_rope_kvcache_bf16(
352
+ packed_qkv: torch.Tensor,
353
+ rope: torch.Tensor,
354
+ q_heads: int,
355
+ kv_heads: int,
356
+ head_dim: int,
357
+ cache_offset: int,
358
+ q_out: torch.Tensor | None = None,
359
+ k_cache: torch.Tensor | None = None,
360
+ v_cache: torch.Tensor | None = None,
361
+ max_seq_len: int | None = None,
362
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
363
+ """Split GQA packed QKV, apply interleaved RoPE, and write K/V cache.
364
+
365
+ ``packed_qkv`` has shape ``(batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)``.
366
+ ``rope`` has BF16 interleaved ``[cos0, sin0, cos1, sin1, ...]`` rows with
367
+ shape ``(>= seq_len, head_dim)``. ``q_out`` has shape
368
+ ``(batch, seq_len, q_heads, head_dim)``. K/V are written in-place into
369
+ ``(batch, max_seq_len, kv_heads, head_dim)`` caches starting at
370
+ ``cache_offset``.
371
+ """
372
+
373
+ batch, seq_len, _ = packed_qkv.shape
374
+ if q_out is None:
375
+ q_out = torch.empty(
376
+ (batch, seq_len, q_heads, head_dim),
377
+ device=packed_qkv.device,
378
+ dtype=torch.bfloat16,
379
+ )
380
+ if k_cache is None or v_cache is None:
381
+ if max_seq_len is None:
382
+ max_seq_len = cache_offset + seq_len
383
+ cache_shape = (batch, int(max_seq_len), kv_heads, head_dim)
384
+ if k_cache is None:
385
+ k_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
386
+ if v_cache is None:
387
+ v_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
388
+ ops.qkv_split_rope_kvcache_bf16(
389
+ packed_qkv,
390
+ rope,
391
+ int(q_heads),
392
+ int(kv_heads),
393
+ int(head_dim),
394
+ int(cache_offset),
395
+ q_out,
396
+ k_cache,
397
+ v_cache,
398
+ )
399
+ return q_out, k_cache, v_cache
400
+
401
+
402
+ def qkv_split_bf16(
403
+ packed_qkv: torch.Tensor,
404
+ heads: int,
405
+ head_dim: int,
406
+ q_out: torch.Tensor | None = None,
407
+ k_out: torch.Tensor | None = None,
408
+ v_out: torch.Tensor | None = None,
409
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
410
+ """Split packed BF16 QKV into Q/K/V tensors.
411
+
412
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
413
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
414
+ """
415
+
416
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
417
+ if q_out is None:
418
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
419
+ if k_out is None:
420
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
421
+ if v_out is None:
422
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
423
+ ops.qkv_split_bf16(packed_qkv, int(heads), int(head_dim), q_out, k_out, v_out)
424
+ return q_out, k_out, v_out
425
+
426
+
427
  def decode_q_norm_rope_stage_bf16(
428
  q_pre: torch.Tensor,
429
  q_norm_weight: torch.Tensor,
 
627
  "decode_q_norm_rope_stage_bf16",
628
  "decode_k_norm_rope_kvwrite_bf16",
629
  "decode_k_norm_rope_kvwrite_devpos_bf16",
630
+ "qkv_split_bf16",
631
+ "qkv_split_rope_kvcache_bf16",
632
  "qkv_split_norm_rope_bf16",
633
  "qkv_split_bias_norm_rope_v_bf16",
634
  "qkv_split_bias_norm_rope_v_cat_bf16",
build/torch211-cxx11-cu128-x86_64-linux/{_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so → _flashrt_qkv_cache_rope_cuda_5de4768.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:dc3cb9db8b4cc435477d30180ef7a20386e74514a14e328b942eee64e3397d1f
3
- size 3646904
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c52cad3d53d935f3488e0b2e357d5a59065cc7a3bab2d4f22c67e30473e3eb1
3
+ size 1870824
build/torch211-cxx11-cu128-x86_64-linux/_ops.py CHANGED
@@ -1,9 +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}"
 
1
  import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_5de4768
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_5de4768
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_5de4768::{op_name}"
build/torch211-cxx11-cu128-x86_64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
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": [],
 
1
  {
2
  "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_5de4768",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
build/torch211-cxx11-cu130-x86_64-linux/__init__.py CHANGED
@@ -78,6 +78,53 @@ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
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,
@@ -230,6 +277,21 @@ def _check_packed_qkv(
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,
@@ -286,6 +348,82 @@ def qkv_split_norm_rope_bf16(
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,
@@ -489,6 +627,8 @@ __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",
 
78
  return None
79
 
80
 
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_rope_kvcache_bf16"))
82
+ def _qkv_split_rope_kvcache_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ rope: torch.Tensor,
85
+ q_heads: int,
86
+ kv_heads: int,
87
+ head_dim: int,
88
+ cache_offset: int,
89
+ q_out: torch.Tensor,
90
+ k_cache: torch.Tensor,
91
+ v_cache: torch.Tensor,
92
+ ) -> None:
93
+ _check_packed_gqa_qkv(packed_qkv, q_heads, kv_heads, head_dim)
94
+ batch, seq_len, _ = packed_qkv.shape
95
+ if rope.dim() != 2 or rope.shape[0] < seq_len or rope.shape[1] != head_dim:
96
+ raise RuntimeError("rope must have shape (>= seq_len, head_dim)")
97
+ if q_out.shape != (batch, seq_len, q_heads, head_dim):
98
+ raise RuntimeError("q_out must have shape (batch, seq_len, q_heads, head_dim)")
99
+ if k_cache.dim() != 4 or k_cache.shape[0] != batch or k_cache.shape[2:] != (kv_heads, head_dim):
100
+ raise RuntimeError("k_cache must have shape (batch, max_seq_len, kv_heads, head_dim)")
101
+ if v_cache.shape != k_cache.shape:
102
+ raise RuntimeError("v_cache must have the same shape as k_cache")
103
+ if cache_offset < 0 or cache_offset + seq_len > k_cache.shape[1]:
104
+ raise RuntimeError("cache_offset + seq_len must be within k_cache.shape[1]")
105
+ return None
106
+
107
+
108
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bf16"))
109
+ def _qkv_split_bf16_fake(
110
+ packed_qkv: torch.Tensor,
111
+ heads: int,
112
+ head_dim: int,
113
+ q_out: torch.Tensor,
114
+ k_out: torch.Tensor,
115
+ v_out: torch.Tensor,
116
+ ) -> None:
117
+ if packed_qkv.dim() != 3:
118
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
119
+ batch, seq_len, cols = packed_qkv.shape
120
+ if cols != 3 * heads * head_dim:
121
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
122
+ out_shape = (batch, seq_len, heads, head_dim)
123
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
124
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
125
+ return None
126
+
127
+
128
  @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
129
  def _qkv_split_norm_rope_bf16_fake(
130
  packed_qkv: torch.Tensor,
 
277
  raise RuntimeError("norm weights must have shape (heads * head_dim,)")
278
 
279
 
280
+ def _check_packed_gqa_qkv(
281
+ packed_qkv: torch.Tensor,
282
+ q_heads: int,
283
+ kv_heads: int,
284
+ head_dim: int,
285
+ ) -> None:
286
+ if packed_qkv.dim() != 3:
287
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)")
288
+ if q_heads <= 0 or kv_heads <= 0 or head_dim <= 0 or head_dim % 2 != 0:
289
+ raise RuntimeError("q_heads, kv_heads, and even head_dim must be positive")
290
+ expected = (q_heads + 2 * kv_heads) * head_dim
291
+ if packed_qkv.shape[2] != expected:
292
+ raise RuntimeError("packed_qkv.shape[2] must be (q_heads + 2 * kv_heads) * head_dim")
293
+
294
+
295
  def _check_freqs(
296
  freqs_re: torch.Tensor,
297
  freqs_im: torch.Tensor,
 
348
  return q_out, k_out
349
 
350
 
351
+ def qkv_split_rope_kvcache_bf16(
352
+ packed_qkv: torch.Tensor,
353
+ rope: torch.Tensor,
354
+ q_heads: int,
355
+ kv_heads: int,
356
+ head_dim: int,
357
+ cache_offset: int,
358
+ q_out: torch.Tensor | None = None,
359
+ k_cache: torch.Tensor | None = None,
360
+ v_cache: torch.Tensor | None = None,
361
+ max_seq_len: int | None = None,
362
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
363
+ """Split GQA packed QKV, apply interleaved RoPE, and write K/V cache.
364
+
365
+ ``packed_qkv`` has shape ``(batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)``.
366
+ ``rope`` has BF16 interleaved ``[cos0, sin0, cos1, sin1, ...]`` rows with
367
+ shape ``(>= seq_len, head_dim)``. ``q_out`` has shape
368
+ ``(batch, seq_len, q_heads, head_dim)``. K/V are written in-place into
369
+ ``(batch, max_seq_len, kv_heads, head_dim)`` caches starting at
370
+ ``cache_offset``.
371
+ """
372
+
373
+ batch, seq_len, _ = packed_qkv.shape
374
+ if q_out is None:
375
+ q_out = torch.empty(
376
+ (batch, seq_len, q_heads, head_dim),
377
+ device=packed_qkv.device,
378
+ dtype=torch.bfloat16,
379
+ )
380
+ if k_cache is None or v_cache is None:
381
+ if max_seq_len is None:
382
+ max_seq_len = cache_offset + seq_len
383
+ cache_shape = (batch, int(max_seq_len), kv_heads, head_dim)
384
+ if k_cache is None:
385
+ k_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
386
+ if v_cache is None:
387
+ v_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
388
+ ops.qkv_split_rope_kvcache_bf16(
389
+ packed_qkv,
390
+ rope,
391
+ int(q_heads),
392
+ int(kv_heads),
393
+ int(head_dim),
394
+ int(cache_offset),
395
+ q_out,
396
+ k_cache,
397
+ v_cache,
398
+ )
399
+ return q_out, k_cache, v_cache
400
+
401
+
402
+ def qkv_split_bf16(
403
+ packed_qkv: torch.Tensor,
404
+ heads: int,
405
+ head_dim: int,
406
+ q_out: torch.Tensor | None = None,
407
+ k_out: torch.Tensor | None = None,
408
+ v_out: torch.Tensor | None = None,
409
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
410
+ """Split packed BF16 QKV into Q/K/V tensors.
411
+
412
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
413
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
414
+ """
415
+
416
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
417
+ if q_out is None:
418
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
419
+ if k_out is None:
420
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
421
+ if v_out is None:
422
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
423
+ ops.qkv_split_bf16(packed_qkv, int(heads), int(head_dim), q_out, k_out, v_out)
424
+ return q_out, k_out, v_out
425
+
426
+
427
  def decode_q_norm_rope_stage_bf16(
428
  q_pre: torch.Tensor,
429
  q_norm_weight: torch.Tensor,
 
627
  "decode_q_norm_rope_stage_bf16",
628
  "decode_k_norm_rope_kvwrite_bf16",
629
  "decode_k_norm_rope_kvwrite_devpos_bf16",
630
+ "qkv_split_bf16",
631
+ "qkv_split_rope_kvcache_bf16",
632
  "qkv_split_norm_rope_bf16",
633
  "qkv_split_bias_norm_rope_v_bf16",
634
  "qkv_split_bias_norm_rope_v_cat_bf16",
build/torch211-cxx11-cu130-x86_64-linux/{_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so → _flashrt_qkv_cache_rope_cuda_5de4768.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:905b694fd69586c964951f9024058991285177834a230f4a4f57ceef85b8f0f6
3
- size 3404176
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bbefdd56fc80137e3df7f0725e0b86620e7ceda9f27a3b64a169202515143724
3
+ size 1826248
build/torch211-cxx11-cu130-x86_64-linux/_ops.py CHANGED
@@ -1,9 +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}"
 
1
  import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_5de4768
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_5de4768
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_5de4768::{op_name}"
build/torch211-cxx11-cu130-x86_64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
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": [],
@@ -9,7 +9,8 @@
9
  "archs": [
10
  "10.0",
11
  "11.0",
12
- "12.0+PTX",
 
13
  "7.5",
14
  "8.0",
15
  "8.6",
 
1
  {
2
  "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_5de4768",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
9
  "archs": [
10
  "10.0",
11
  "11.0",
12
+ "12.0",
13
+ "12.1+PTX",
14
  "7.5",
15
  "8.0",
16
  "8.6",
build/torch212-cxx11-cu130-x86_64-linux/__init__.py CHANGED
@@ -78,6 +78,53 @@ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
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,
@@ -230,6 +277,21 @@ def _check_packed_qkv(
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,
@@ -286,6 +348,82 @@ def qkv_split_norm_rope_bf16(
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,
@@ -489,6 +627,8 @@ __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",
 
78
  return None
79
 
80
 
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_rope_kvcache_bf16"))
82
+ def _qkv_split_rope_kvcache_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ rope: torch.Tensor,
85
+ q_heads: int,
86
+ kv_heads: int,
87
+ head_dim: int,
88
+ cache_offset: int,
89
+ q_out: torch.Tensor,
90
+ k_cache: torch.Tensor,
91
+ v_cache: torch.Tensor,
92
+ ) -> None:
93
+ _check_packed_gqa_qkv(packed_qkv, q_heads, kv_heads, head_dim)
94
+ batch, seq_len, _ = packed_qkv.shape
95
+ if rope.dim() != 2 or rope.shape[0] < seq_len or rope.shape[1] != head_dim:
96
+ raise RuntimeError("rope must have shape (>= seq_len, head_dim)")
97
+ if q_out.shape != (batch, seq_len, q_heads, head_dim):
98
+ raise RuntimeError("q_out must have shape (batch, seq_len, q_heads, head_dim)")
99
+ if k_cache.dim() != 4 or k_cache.shape[0] != batch or k_cache.shape[2:] != (kv_heads, head_dim):
100
+ raise RuntimeError("k_cache must have shape (batch, max_seq_len, kv_heads, head_dim)")
101
+ if v_cache.shape != k_cache.shape:
102
+ raise RuntimeError("v_cache must have the same shape as k_cache")
103
+ if cache_offset < 0 or cache_offset + seq_len > k_cache.shape[1]:
104
+ raise RuntimeError("cache_offset + seq_len must be within k_cache.shape[1]")
105
+ return None
106
+
107
+
108
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bf16"))
109
+ def _qkv_split_bf16_fake(
110
+ packed_qkv: torch.Tensor,
111
+ heads: int,
112
+ head_dim: int,
113
+ q_out: torch.Tensor,
114
+ k_out: torch.Tensor,
115
+ v_out: torch.Tensor,
116
+ ) -> None:
117
+ if packed_qkv.dim() != 3:
118
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
119
+ batch, seq_len, cols = packed_qkv.shape
120
+ if cols != 3 * heads * head_dim:
121
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
122
+ out_shape = (batch, seq_len, heads, head_dim)
123
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
124
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
125
+ return None
126
+
127
+
128
  @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
129
  def _qkv_split_norm_rope_bf16_fake(
130
  packed_qkv: torch.Tensor,
 
277
  raise RuntimeError("norm weights must have shape (heads * head_dim,)")
278
 
279
 
280
+ def _check_packed_gqa_qkv(
281
+ packed_qkv: torch.Tensor,
282
+ q_heads: int,
283
+ kv_heads: int,
284
+ head_dim: int,
285
+ ) -> None:
286
+ if packed_qkv.dim() != 3:
287
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)")
288
+ if q_heads <= 0 or kv_heads <= 0 or head_dim <= 0 or head_dim % 2 != 0:
289
+ raise RuntimeError("q_heads, kv_heads, and even head_dim must be positive")
290
+ expected = (q_heads + 2 * kv_heads) * head_dim
291
+ if packed_qkv.shape[2] != expected:
292
+ raise RuntimeError("packed_qkv.shape[2] must be (q_heads + 2 * kv_heads) * head_dim")
293
+
294
+
295
  def _check_freqs(
296
  freqs_re: torch.Tensor,
297
  freqs_im: torch.Tensor,
 
348
  return q_out, k_out
349
 
350
 
351
+ def qkv_split_rope_kvcache_bf16(
352
+ packed_qkv: torch.Tensor,
353
+ rope: torch.Tensor,
354
+ q_heads: int,
355
+ kv_heads: int,
356
+ head_dim: int,
357
+ cache_offset: int,
358
+ q_out: torch.Tensor | None = None,
359
+ k_cache: torch.Tensor | None = None,
360
+ v_cache: torch.Tensor | None = None,
361
+ max_seq_len: int | None = None,
362
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
363
+ """Split GQA packed QKV, apply interleaved RoPE, and write K/V cache.
364
+
365
+ ``packed_qkv`` has shape ``(batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)``.
366
+ ``rope`` has BF16 interleaved ``[cos0, sin0, cos1, sin1, ...]`` rows with
367
+ shape ``(>= seq_len, head_dim)``. ``q_out`` has shape
368
+ ``(batch, seq_len, q_heads, head_dim)``. K/V are written in-place into
369
+ ``(batch, max_seq_len, kv_heads, head_dim)`` caches starting at
370
+ ``cache_offset``.
371
+ """
372
+
373
+ batch, seq_len, _ = packed_qkv.shape
374
+ if q_out is None:
375
+ q_out = torch.empty(
376
+ (batch, seq_len, q_heads, head_dim),
377
+ device=packed_qkv.device,
378
+ dtype=torch.bfloat16,
379
+ )
380
+ if k_cache is None or v_cache is None:
381
+ if max_seq_len is None:
382
+ max_seq_len = cache_offset + seq_len
383
+ cache_shape = (batch, int(max_seq_len), kv_heads, head_dim)
384
+ if k_cache is None:
385
+ k_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
386
+ if v_cache is None:
387
+ v_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
388
+ ops.qkv_split_rope_kvcache_bf16(
389
+ packed_qkv,
390
+ rope,
391
+ int(q_heads),
392
+ int(kv_heads),
393
+ int(head_dim),
394
+ int(cache_offset),
395
+ q_out,
396
+ k_cache,
397
+ v_cache,
398
+ )
399
+ return q_out, k_cache, v_cache
400
+
401
+
402
+ def qkv_split_bf16(
403
+ packed_qkv: torch.Tensor,
404
+ heads: int,
405
+ head_dim: int,
406
+ q_out: torch.Tensor | None = None,
407
+ k_out: torch.Tensor | None = None,
408
+ v_out: torch.Tensor | None = None,
409
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
410
+ """Split packed BF16 QKV into Q/K/V tensors.
411
+
412
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
413
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
414
+ """
415
+
416
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
417
+ if q_out is None:
418
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
419
+ if k_out is None:
420
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
421
+ if v_out is None:
422
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
423
+ ops.qkv_split_bf16(packed_qkv, int(heads), int(head_dim), q_out, k_out, v_out)
424
+ return q_out, k_out, v_out
425
+
426
+
427
  def decode_q_norm_rope_stage_bf16(
428
  q_pre: torch.Tensor,
429
  q_norm_weight: torch.Tensor,
 
627
  "decode_q_norm_rope_stage_bf16",
628
  "decode_k_norm_rope_kvwrite_bf16",
629
  "decode_k_norm_rope_kvwrite_devpos_bf16",
630
+ "qkv_split_bf16",
631
+ "qkv_split_rope_kvcache_bf16",
632
  "qkv_split_norm_rope_bf16",
633
  "qkv_split_bias_norm_rope_v_bf16",
634
  "qkv_split_bias_norm_rope_v_cat_bf16",
build/torch212-cxx11-cu130-x86_64-linux/{_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so → _flashrt_qkv_cache_rope_cuda_5de4768.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5638894ffc0223761c86b73f75de3cfbe3128dff1f755f7e2463e4a6700d98d3
3
- size 3409696
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6574c460576845e030f99410ebc197403c1ac3f1d4358654bdf2698fa358738
3
+ size 1836760
build/torch212-cxx11-cu130-x86_64-linux/_ops.py CHANGED
@@ -1,9 +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}"
 
1
  import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_5de4768
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_5de4768
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_5de4768::{op_name}"
build/torch212-cxx11-cu130-x86_64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
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": [],
@@ -9,7 +9,8 @@
9
  "archs": [
10
  "10.0",
11
  "11.0",
12
- "12.0+PTX",
 
13
  "7.5",
14
  "8.0",
15
  "8.6",
 
1
  {
2
  "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_5de4768",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
9
  "archs": [
10
  "10.0",
11
  "11.0",
12
+ "12.0",
13
+ "12.1+PTX",
14
  "7.5",
15
  "8.0",
16
  "8.6",
build/torch212-cxx11-cu132-x86_64-linux/__init__.py CHANGED
@@ -78,6 +78,53 @@ def _decode_k_norm_rope_kvwrite_devpos_bf16_fake(
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,
@@ -230,6 +277,21 @@ def _check_packed_qkv(
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,
@@ -286,6 +348,82 @@ def qkv_split_norm_rope_bf16(
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,
@@ -489,6 +627,8 @@ __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",
 
78
  return None
79
 
80
 
81
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_rope_kvcache_bf16"))
82
+ def _qkv_split_rope_kvcache_bf16_fake(
83
+ packed_qkv: torch.Tensor,
84
+ rope: torch.Tensor,
85
+ q_heads: int,
86
+ kv_heads: int,
87
+ head_dim: int,
88
+ cache_offset: int,
89
+ q_out: torch.Tensor,
90
+ k_cache: torch.Tensor,
91
+ v_cache: torch.Tensor,
92
+ ) -> None:
93
+ _check_packed_gqa_qkv(packed_qkv, q_heads, kv_heads, head_dim)
94
+ batch, seq_len, _ = packed_qkv.shape
95
+ if rope.dim() != 2 or rope.shape[0] < seq_len or rope.shape[1] != head_dim:
96
+ raise RuntimeError("rope must have shape (>= seq_len, head_dim)")
97
+ if q_out.shape != (batch, seq_len, q_heads, head_dim):
98
+ raise RuntimeError("q_out must have shape (batch, seq_len, q_heads, head_dim)")
99
+ if k_cache.dim() != 4 or k_cache.shape[0] != batch or k_cache.shape[2:] != (kv_heads, head_dim):
100
+ raise RuntimeError("k_cache must have shape (batch, max_seq_len, kv_heads, head_dim)")
101
+ if v_cache.shape != k_cache.shape:
102
+ raise RuntimeError("v_cache must have the same shape as k_cache")
103
+ if cache_offset < 0 or cache_offset + seq_len > k_cache.shape[1]:
104
+ raise RuntimeError("cache_offset + seq_len must be within k_cache.shape[1]")
105
+ return None
106
+
107
+
108
+ @torch.library.register_fake(add_op_namespace_prefix("qkv_split_bf16"))
109
+ def _qkv_split_bf16_fake(
110
+ packed_qkv: torch.Tensor,
111
+ heads: int,
112
+ head_dim: int,
113
+ q_out: torch.Tensor,
114
+ k_out: torch.Tensor,
115
+ v_out: torch.Tensor,
116
+ ) -> None:
117
+ if packed_qkv.dim() != 3:
118
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, 3 * heads * head_dim)")
119
+ batch, seq_len, cols = packed_qkv.shape
120
+ if cols != 3 * heads * head_dim:
121
+ raise RuntimeError("packed_qkv.shape[2] must be 3 * heads * head_dim")
122
+ out_shape = (batch, seq_len, heads, head_dim)
123
+ if q_out.shape != out_shape or k_out.shape != out_shape or v_out.shape != out_shape:
124
+ raise RuntimeError("q_out, k_out, and v_out must have shape (batch, seq_len, heads, head_dim)")
125
+ return None
126
+
127
+
128
  @torch.library.register_fake(add_op_namespace_prefix("qkv_split_norm_rope_bf16"))
129
  def _qkv_split_norm_rope_bf16_fake(
130
  packed_qkv: torch.Tensor,
 
277
  raise RuntimeError("norm weights must have shape (heads * head_dim,)")
278
 
279
 
280
+ def _check_packed_gqa_qkv(
281
+ packed_qkv: torch.Tensor,
282
+ q_heads: int,
283
+ kv_heads: int,
284
+ head_dim: int,
285
+ ) -> None:
286
+ if packed_qkv.dim() != 3:
287
+ raise RuntimeError("packed_qkv must have shape (batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)")
288
+ if q_heads <= 0 or kv_heads <= 0 or head_dim <= 0 or head_dim % 2 != 0:
289
+ raise RuntimeError("q_heads, kv_heads, and even head_dim must be positive")
290
+ expected = (q_heads + 2 * kv_heads) * head_dim
291
+ if packed_qkv.shape[2] != expected:
292
+ raise RuntimeError("packed_qkv.shape[2] must be (q_heads + 2 * kv_heads) * head_dim")
293
+
294
+
295
  def _check_freqs(
296
  freqs_re: torch.Tensor,
297
  freqs_im: torch.Tensor,
 
348
  return q_out, k_out
349
 
350
 
351
+ def qkv_split_rope_kvcache_bf16(
352
+ packed_qkv: torch.Tensor,
353
+ rope: torch.Tensor,
354
+ q_heads: int,
355
+ kv_heads: int,
356
+ head_dim: int,
357
+ cache_offset: int,
358
+ q_out: torch.Tensor | None = None,
359
+ k_cache: torch.Tensor | None = None,
360
+ v_cache: torch.Tensor | None = None,
361
+ max_seq_len: int | None = None,
362
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
363
+ """Split GQA packed QKV, apply interleaved RoPE, and write K/V cache.
364
+
365
+ ``packed_qkv`` has shape ``(batch, seq_len, (q_heads + 2 * kv_heads) * head_dim)``.
366
+ ``rope`` has BF16 interleaved ``[cos0, sin0, cos1, sin1, ...]`` rows with
367
+ shape ``(>= seq_len, head_dim)``. ``q_out`` has shape
368
+ ``(batch, seq_len, q_heads, head_dim)``. K/V are written in-place into
369
+ ``(batch, max_seq_len, kv_heads, head_dim)`` caches starting at
370
+ ``cache_offset``.
371
+ """
372
+
373
+ batch, seq_len, _ = packed_qkv.shape
374
+ if q_out is None:
375
+ q_out = torch.empty(
376
+ (batch, seq_len, q_heads, head_dim),
377
+ device=packed_qkv.device,
378
+ dtype=torch.bfloat16,
379
+ )
380
+ if k_cache is None or v_cache is None:
381
+ if max_seq_len is None:
382
+ max_seq_len = cache_offset + seq_len
383
+ cache_shape = (batch, int(max_seq_len), kv_heads, head_dim)
384
+ if k_cache is None:
385
+ k_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
386
+ if v_cache is None:
387
+ v_cache = torch.empty(cache_shape, device=packed_qkv.device, dtype=torch.bfloat16)
388
+ ops.qkv_split_rope_kvcache_bf16(
389
+ packed_qkv,
390
+ rope,
391
+ int(q_heads),
392
+ int(kv_heads),
393
+ int(head_dim),
394
+ int(cache_offset),
395
+ q_out,
396
+ k_cache,
397
+ v_cache,
398
+ )
399
+ return q_out, k_cache, v_cache
400
+
401
+
402
+ def qkv_split_bf16(
403
+ packed_qkv: torch.Tensor,
404
+ heads: int,
405
+ head_dim: int,
406
+ q_out: torch.Tensor | None = None,
407
+ k_out: torch.Tensor | None = None,
408
+ v_out: torch.Tensor | None = None,
409
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
410
+ """Split packed BF16 QKV into Q/K/V tensors.
411
+
412
+ ``packed_qkv`` has shape ``(batch, seq_len, 3 * heads * head_dim)``.
413
+ Outputs have shape ``(batch, seq_len, heads, head_dim)``.
414
+ """
415
+
416
+ out_shape = (packed_qkv.shape[0], packed_qkv.shape[1], heads, head_dim)
417
+ if q_out is None:
418
+ q_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
419
+ if k_out is None:
420
+ k_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
421
+ if v_out is None:
422
+ v_out = torch.empty(out_shape, device=packed_qkv.device, dtype=torch.bfloat16)
423
+ ops.qkv_split_bf16(packed_qkv, int(heads), int(head_dim), q_out, k_out, v_out)
424
+ return q_out, k_out, v_out
425
+
426
+
427
  def decode_q_norm_rope_stage_bf16(
428
  q_pre: torch.Tensor,
429
  q_norm_weight: torch.Tensor,
 
627
  "decode_q_norm_rope_stage_bf16",
628
  "decode_k_norm_rope_kvwrite_bf16",
629
  "decode_k_norm_rope_kvwrite_devpos_bf16",
630
+ "qkv_split_bf16",
631
+ "qkv_split_rope_kvcache_bf16",
632
  "qkv_split_norm_rope_bf16",
633
  "qkv_split_bias_norm_rope_v_bf16",
634
  "qkv_split_bias_norm_rope_v_cat_bf16",
build/torch212-cxx11-cu132-x86_64-linux/{_flashrt_qkv_cache_rope_cuda_cf903dd.abi3.so → _flashrt_qkv_cache_rope_cuda_5de4768.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:9f32e82da0aabbf1b514318a37adc5deccfd21d20d55e6f274c8c0e83d731f1f
3
- size 3405568
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6f9b056bfcac039776c37f83e30e348dde8d7ec3f2ab1d7f7062e8a57f2bb39f
3
+ size 1828568
build/torch212-cxx11-cu132-x86_64-linux/_ops.py CHANGED
@@ -1,9 +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}"
 
1
  import torch
2
+ from . import _flashrt_qkv_cache_rope_cuda_5de4768
3
+ ops = torch.ops._flashrt_qkv_cache_rope_cuda_5de4768
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_5de4768::{op_name}"
build/torch212-cxx11-cu132-x86_64-linux/metadata.json CHANGED
@@ -1,6 +1,6 @@
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": [],
@@ -9,7 +9,8 @@
9
  "archs": [
10
  "10.0",
11
  "11.0",
12
- "12.0+PTX",
 
13
  "7.5",
14
  "8.0",
15
  "8.6",
 
1
  {
2
  "name": "flashrt-qkv-cache-rope",
3
+ "id": "_flashrt_qkv_cache_rope_cuda_5de4768",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
9
  "archs": [
10
  "10.0",
11
  "11.0",
12
+ "12.0",
13
+ "12.1+PTX",
14
  "7.5",
15
  "8.0",
16
  "8.6",