liangsu9988 commited on
Commit
2adb8f7
·
verified ·
1 Parent(s): a9a6160

Uploaded using `kernel-builder`.

Browse files
benchmarks/benchmark.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Benchmark vl-transformer-primitives against PyTorch eager references."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ import torch
11
+
12
+
13
+ PACKAGE = Path(__file__).resolve().parents[1]
14
+ TESTS = PACKAGE / "tests"
15
+ sys.path.insert(0, str(TESTS))
16
+ from test_vl_transformer_primitives import ( # noqa: E402
17
+ load_installed_ops,
18
+ load_source_ops,
19
+ make_decode_case,
20
+ ref_avg_pool,
21
+ ref_norm_rope,
22
+ )
23
+
24
+
25
+ def bench(fn, warmup: int, iters: int) -> float:
26
+ for _ in range(warmup):
27
+ fn()
28
+ torch.cuda.synchronize()
29
+ start = torch.cuda.Event(enable_timing=True)
30
+ end = torch.cuda.Event(enable_timing=True)
31
+ start.record()
32
+ for _ in range(iters):
33
+ fn()
34
+ end.record()
35
+ torch.cuda.synchronize()
36
+ return start.elapsed_time(end) * 1000.0 / iters
37
+
38
+
39
+ def main() -> int:
40
+ parser = argparse.ArgumentParser()
41
+ parser.add_argument("--backend", choices=["source", "installed"], default="source")
42
+ parser.add_argument("--artifact", default=None)
43
+ parser.add_argument("--warmup", type=int, default=50)
44
+ parser.add_argument("--iters", type=int, default=500)
45
+ args = parser.parse_args()
46
+
47
+ if not torch.cuda.is_available():
48
+ raise RuntimeError("CUDA is required")
49
+ torch.manual_seed(1234)
50
+ ops = load_source_ops() if args.backend == "source" else load_installed_ops(args.artifact)
51
+
52
+ print("| Workload | Shape | FlashRT us | PyTorch eager us | Speedup |")
53
+ print("|---|---:|---:|---:|---:|")
54
+
55
+ for heads in [1, 4, 8, 16, 32, 40]:
56
+ q, k, v, q_w, k_w, cos, sin = make_decode_case(heads)
57
+ fused = bench(
58
+ lambda: ops.qwen3_q_norm_rope_qstage_bf16(q, q_w, cos, sin),
59
+ args.warmup,
60
+ args.iters,
61
+ )
62
+ eager = bench(
63
+ lambda: ref_norm_rope(q, q_w, cos, sin, 1e-6),
64
+ args.warmup,
65
+ args.iters,
66
+ )
67
+ print(f"| q_norm_rope | heads={heads}, d=128 | {fused:.3f} | {eager:.3f} | {eager / fused:.2f}x |")
68
+
69
+ fused = bench(
70
+ lambda: ops.qwen3_k_norm_rope_kvwrite_bf16(k, v, k_w, cos, sin),
71
+ args.warmup,
72
+ args.iters,
73
+ )
74
+ eager = bench(
75
+ lambda: (ref_norm_rope(k, k_w, cos, sin, 1e-6), v.clone()),
76
+ args.warmup,
77
+ args.iters,
78
+ )
79
+ print(f"| k_norm_rope_vwrite | heads={heads}, d=128 | {fused:.3f} | {eager:.3f} | {eager / fused:.2f}x |")
80
+
81
+ for nv, h, w, dim, pool in [
82
+ (1, 16, 16, 1024, 2),
83
+ (2, 16, 16, 1152, 2),
84
+ (4, 16, 16, 2048, 4),
85
+ (2, 32, 32, 1024, 4),
86
+ ]:
87
+ x = torch.randn((nv * h * w, dim), device="cuda", dtype=torch.bfloat16)
88
+ fused = bench(
89
+ lambda: ops.avg_pool_vision_tokens_bf16(x, nv, h, w, pool),
90
+ args.warmup,
91
+ args.iters,
92
+ )
93
+ eager = bench(
94
+ lambda: ref_avg_pool(x, nv, h, w, pool),
95
+ args.warmup,
96
+ args.iters,
97
+ )
98
+ print(
99
+ f"| avg_pool_vision | nv={nv}, h={h}, w={w}, dim={dim}, pool={pool} "
100
+ f"| {fused:.3f} | {eager:.3f} | {eager / fused:.2f}x |"
101
+ )
102
+
103
+ return 0
104
+
105
+
106
+ if __name__ == "__main__":
107
+ raise SystemExit(main())
build/torch211-cxx11-cu128-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT VL transformer primitive kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+
9
+ from ._ops import add_op_namespace_prefix, ops
10
+
11
+
12
+ def _check_decode_rope(
13
+ x: torch.Tensor,
14
+ weight: torch.Tensor,
15
+ cos: torch.Tensor,
16
+ sin: torch.Tensor,
17
+ out: torch.Tensor,
18
+ name: str,
19
+ ) -> None:
20
+ if x.dim() != 2 or x.shape[1] != 128:
21
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
22
+ if weight.shape != (128,):
23
+ raise RuntimeError("norm weight must have shape (128,)")
24
+ if cos.shape != (64,) or sin.shape != (64,):
25
+ raise RuntimeError("cos and sin must have shape (64,)")
26
+ if out.shape != x.shape:
27
+ raise RuntimeError("out must have the same shape as input")
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_q_norm_rope_qstage_bf16"))
31
+ def _qwen3_q_norm_rope_qstage_bf16_fake(
32
+ q_pre: torch.Tensor,
33
+ q_norm_weight: torch.Tensor,
34
+ cos: torch.Tensor,
35
+ sin: torch.Tensor,
36
+ eps: float,
37
+ q_out: torch.Tensor,
38
+ ) -> None:
39
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
40
+ return None
41
+
42
+
43
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_bf16"))
44
+ def _qwen3_k_norm_rope_kvwrite_bf16_fake(
45
+ k_pre: torch.Tensor,
46
+ v_pre: torch.Tensor,
47
+ k_norm_weight: torch.Tensor,
48
+ cos: torch.Tensor,
49
+ sin: torch.Tensor,
50
+ eps: float,
51
+ k_cache_dst: torch.Tensor,
52
+ v_cache_dst: torch.Tensor,
53
+ ) -> None:
54
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
55
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
56
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
57
+ return None
58
+
59
+
60
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_devpos_bf16"))
61
+ def _qwen3_k_norm_rope_kvwrite_devpos_bf16_fake(
62
+ k_pre: torch.Tensor,
63
+ v_pre: torch.Tensor,
64
+ k_norm_weight: torch.Tensor,
65
+ cos: torch.Tensor,
66
+ sin: torch.Tensor,
67
+ cur_pos: torch.Tensor,
68
+ eps: float,
69
+ k_cache: torch.Tensor,
70
+ v_cache: torch.Tensor,
71
+ ) -> None:
72
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
73
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
74
+ n_kv = k_pre.shape[0]
75
+ if v_pre.shape != k_pre.shape:
76
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
77
+ if k_norm_weight.shape != (128,):
78
+ raise RuntimeError("k_norm_weight must have shape (128,)")
79
+ if cos.shape != (64,) or sin.shape != (64,):
80
+ raise RuntimeError("cos and sin must have shape (64,)")
81
+ if cur_pos.numel() != 1:
82
+ raise RuntimeError("cur_pos must have one int32 element")
83
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
84
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
85
+ if v_cache.shape != k_cache.shape:
86
+ raise RuntimeError("v_cache must have the same shape as k_cache")
87
+ return None
88
+
89
+
90
+ @torch.library.register_fake(add_op_namespace_prefix("avg_pool_vision_tokens_bf16"))
91
+ def _avg_pool_vision_tokens_bf16_fake(
92
+ x: torch.Tensor,
93
+ nv: int,
94
+ h: int,
95
+ w: int,
96
+ pool_factor: int,
97
+ out: torch.Tensor,
98
+ ) -> None:
99
+ if x.dim() != 2:
100
+ raise RuntimeError("x must have shape (nv * h * w, dim)")
101
+ dim = x.shape[1]
102
+ if x.shape[0] != nv * h * w:
103
+ raise RuntimeError("x.shape[0] must equal nv * h * w")
104
+ if h % pool_factor != 0 or w % pool_factor != 0:
105
+ raise RuntimeError("h and w must be divisible by pool_factor")
106
+ expected = (nv * (h // pool_factor) * (w // pool_factor), dim)
107
+ if out.shape != expected:
108
+ raise RuntimeError("out has the wrong pooled shape")
109
+ return None
110
+
111
+
112
+ def qwen3_q_norm_rope_qstage_bf16(
113
+ q_pre: torch.Tensor,
114
+ q_norm_weight: torch.Tensor,
115
+ cos: torch.Tensor,
116
+ sin: torch.Tensor,
117
+ *,
118
+ eps: float = 1e-6,
119
+ out: Optional[torch.Tensor] = None,
120
+ ) -> torch.Tensor:
121
+ """Apply Q RMSNorm + full RoPE and write decode staging output.
122
+
123
+ Inputs are contiguous BF16 tensors. ``q_pre`` has shape
124
+ ``(n_q_heads, 128)``. ``q_norm_weight`` has shape ``(128,)``.
125
+ ``cos`` and ``sin`` have shape ``(64,)``.
126
+ """
127
+
128
+ if out is None:
129
+ out = torch.empty_like(q_pre)
130
+ ops.qwen3_q_norm_rope_qstage_bf16(q_pre, q_norm_weight, cos, sin, float(eps), out)
131
+ return out
132
+
133
+
134
+ def qwen3_k_norm_rope_kvwrite_bf16(
135
+ k_pre: torch.Tensor,
136
+ v_pre: torch.Tensor,
137
+ k_norm_weight: torch.Tensor,
138
+ cos: torch.Tensor,
139
+ sin: torch.Tensor,
140
+ *,
141
+ eps: float = 1e-6,
142
+ k_cache_dst: Optional[torch.Tensor] = None,
143
+ v_cache_dst: Optional[torch.Tensor] = None,
144
+ ) -> tuple[torch.Tensor, torch.Tensor]:
145
+ """Apply K RMSNorm + full RoPE and copy V into the current KV slot."""
146
+
147
+ if k_cache_dst is None:
148
+ k_cache_dst = torch.empty_like(k_pre)
149
+ if v_cache_dst is None:
150
+ v_cache_dst = torch.empty_like(v_pre)
151
+ ops.qwen3_k_norm_rope_kvwrite_bf16(
152
+ k_pre,
153
+ v_pre,
154
+ k_norm_weight,
155
+ cos,
156
+ sin,
157
+ float(eps),
158
+ k_cache_dst,
159
+ v_cache_dst,
160
+ )
161
+ return k_cache_dst, v_cache_dst
162
+
163
+
164
+ def qwen3_k_norm_rope_kvwrite_devpos_bf16(
165
+ k_pre: torch.Tensor,
166
+ v_pre: torch.Tensor,
167
+ k_norm_weight: torch.Tensor,
168
+ cos: torch.Tensor,
169
+ sin: torch.Tensor,
170
+ cur_pos: torch.Tensor,
171
+ k_cache: torch.Tensor,
172
+ v_cache: torch.Tensor,
173
+ *,
174
+ eps: float = 1e-6,
175
+ ) -> tuple[torch.Tensor, torch.Tensor]:
176
+ """Device-position KV write for CUDA Graph decode loops."""
177
+
178
+ ops.qwen3_k_norm_rope_kvwrite_devpos_bf16(
179
+ k_pre,
180
+ v_pre,
181
+ k_norm_weight,
182
+ cos,
183
+ sin,
184
+ cur_pos,
185
+ float(eps),
186
+ k_cache,
187
+ v_cache,
188
+ )
189
+ return k_cache, v_cache
190
+
191
+
192
+ def avg_pool_vision_tokens_bf16(
193
+ x: torch.Tensor,
194
+ nv: int,
195
+ h: int,
196
+ w: int,
197
+ pool_factor: int,
198
+ *,
199
+ out: Optional[torch.Tensor] = None,
200
+ ) -> torch.Tensor:
201
+ """Average-pool flattened vision tokens in the spatial grid.
202
+
203
+ ``x`` is ``(nv * h * w, dim)`` BF16. The result is
204
+ ``(nv * h / pool_factor * w / pool_factor, dim)`` BF16.
205
+ """
206
+
207
+ if x.dim() != 2:
208
+ raise ValueError("x must have shape (nv * h * w, dim)")
209
+ if h % pool_factor != 0 or w % pool_factor != 0:
210
+ raise ValueError("h and w must be divisible by pool_factor")
211
+ rows = nv * (h // pool_factor) * (w // pool_factor)
212
+ if out is None:
213
+ out = torch.empty((rows, x.shape[1]), device=x.device, dtype=x.dtype)
214
+ ops.avg_pool_vision_tokens_bf16(x, int(nv), int(h), int(w), int(pool_factor), out)
215
+ return out
216
+
217
+
218
+ __all__ = [
219
+ "avg_pool_vision_tokens_bf16",
220
+ "qwen3_k_norm_rope_kvwrite_bf16",
221
+ "qwen3_k_norm_rope_kvwrite_devpos_bf16",
222
+ "qwen3_q_norm_rope_qstage_bf16",
223
+ ]
build/torch211-cxx11-cu128-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _vl_transformer_primitives_cuda_5596053
3
+ ops = torch.ops._vl_transformer_primitives_cuda_5596053
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_vl_transformer_primitives_cuda_5596053::{op_name}"
build/torch211-cxx11-cu128-x86_64-linux/_vl_transformer_primitives_cuda_5596053.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1a659ff89026ca179c7d220e9b761d6a31ce5159182f9b9956ba259b89f26e30
3
+ size 535240
build/torch211-cxx11-cu128-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vl-transformer-primitives",
3
+ "id": "_vl_transformer_primitives_cuda_5596053",
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-cu128-x86_64-linux/vl_transformer_primitives/__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/__init__.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT VL transformer primitive kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+
9
+ from ._ops import add_op_namespace_prefix, ops
10
+
11
+
12
+ def _check_decode_rope(
13
+ x: torch.Tensor,
14
+ weight: torch.Tensor,
15
+ cos: torch.Tensor,
16
+ sin: torch.Tensor,
17
+ out: torch.Tensor,
18
+ name: str,
19
+ ) -> None:
20
+ if x.dim() != 2 or x.shape[1] != 128:
21
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
22
+ if weight.shape != (128,):
23
+ raise RuntimeError("norm weight must have shape (128,)")
24
+ if cos.shape != (64,) or sin.shape != (64,):
25
+ raise RuntimeError("cos and sin must have shape (64,)")
26
+ if out.shape != x.shape:
27
+ raise RuntimeError("out must have the same shape as input")
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_q_norm_rope_qstage_bf16"))
31
+ def _qwen3_q_norm_rope_qstage_bf16_fake(
32
+ q_pre: torch.Tensor,
33
+ q_norm_weight: torch.Tensor,
34
+ cos: torch.Tensor,
35
+ sin: torch.Tensor,
36
+ eps: float,
37
+ q_out: torch.Tensor,
38
+ ) -> None:
39
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
40
+ return None
41
+
42
+
43
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_bf16"))
44
+ def _qwen3_k_norm_rope_kvwrite_bf16_fake(
45
+ k_pre: torch.Tensor,
46
+ v_pre: torch.Tensor,
47
+ k_norm_weight: torch.Tensor,
48
+ cos: torch.Tensor,
49
+ sin: torch.Tensor,
50
+ eps: float,
51
+ k_cache_dst: torch.Tensor,
52
+ v_cache_dst: torch.Tensor,
53
+ ) -> None:
54
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
55
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
56
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
57
+ return None
58
+
59
+
60
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_devpos_bf16"))
61
+ def _qwen3_k_norm_rope_kvwrite_devpos_bf16_fake(
62
+ k_pre: torch.Tensor,
63
+ v_pre: torch.Tensor,
64
+ k_norm_weight: torch.Tensor,
65
+ cos: torch.Tensor,
66
+ sin: torch.Tensor,
67
+ cur_pos: torch.Tensor,
68
+ eps: float,
69
+ k_cache: torch.Tensor,
70
+ v_cache: torch.Tensor,
71
+ ) -> None:
72
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
73
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
74
+ n_kv = k_pre.shape[0]
75
+ if v_pre.shape != k_pre.shape:
76
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
77
+ if k_norm_weight.shape != (128,):
78
+ raise RuntimeError("k_norm_weight must have shape (128,)")
79
+ if cos.shape != (64,) or sin.shape != (64,):
80
+ raise RuntimeError("cos and sin must have shape (64,)")
81
+ if cur_pos.numel() != 1:
82
+ raise RuntimeError("cur_pos must have one int32 element")
83
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
84
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
85
+ if v_cache.shape != k_cache.shape:
86
+ raise RuntimeError("v_cache must have the same shape as k_cache")
87
+ return None
88
+
89
+
90
+ @torch.library.register_fake(add_op_namespace_prefix("avg_pool_vision_tokens_bf16"))
91
+ def _avg_pool_vision_tokens_bf16_fake(
92
+ x: torch.Tensor,
93
+ nv: int,
94
+ h: int,
95
+ w: int,
96
+ pool_factor: int,
97
+ out: torch.Tensor,
98
+ ) -> None:
99
+ if x.dim() != 2:
100
+ raise RuntimeError("x must have shape (nv * h * w, dim)")
101
+ dim = x.shape[1]
102
+ if x.shape[0] != nv * h * w:
103
+ raise RuntimeError("x.shape[0] must equal nv * h * w")
104
+ if h % pool_factor != 0 or w % pool_factor != 0:
105
+ raise RuntimeError("h and w must be divisible by pool_factor")
106
+ expected = (nv * (h // pool_factor) * (w // pool_factor), dim)
107
+ if out.shape != expected:
108
+ raise RuntimeError("out has the wrong pooled shape")
109
+ return None
110
+
111
+
112
+ def qwen3_q_norm_rope_qstage_bf16(
113
+ q_pre: torch.Tensor,
114
+ q_norm_weight: torch.Tensor,
115
+ cos: torch.Tensor,
116
+ sin: torch.Tensor,
117
+ *,
118
+ eps: float = 1e-6,
119
+ out: Optional[torch.Tensor] = None,
120
+ ) -> torch.Tensor:
121
+ """Apply Q RMSNorm + full RoPE and write decode staging output.
122
+
123
+ Inputs are contiguous BF16 tensors. ``q_pre`` has shape
124
+ ``(n_q_heads, 128)``. ``q_norm_weight`` has shape ``(128,)``.
125
+ ``cos`` and ``sin`` have shape ``(64,)``.
126
+ """
127
+
128
+ if out is None:
129
+ out = torch.empty_like(q_pre)
130
+ ops.qwen3_q_norm_rope_qstage_bf16(q_pre, q_norm_weight, cos, sin, float(eps), out)
131
+ return out
132
+
133
+
134
+ def qwen3_k_norm_rope_kvwrite_bf16(
135
+ k_pre: torch.Tensor,
136
+ v_pre: torch.Tensor,
137
+ k_norm_weight: torch.Tensor,
138
+ cos: torch.Tensor,
139
+ sin: torch.Tensor,
140
+ *,
141
+ eps: float = 1e-6,
142
+ k_cache_dst: Optional[torch.Tensor] = None,
143
+ v_cache_dst: Optional[torch.Tensor] = None,
144
+ ) -> tuple[torch.Tensor, torch.Tensor]:
145
+ """Apply K RMSNorm + full RoPE and copy V into the current KV slot."""
146
+
147
+ if k_cache_dst is None:
148
+ k_cache_dst = torch.empty_like(k_pre)
149
+ if v_cache_dst is None:
150
+ v_cache_dst = torch.empty_like(v_pre)
151
+ ops.qwen3_k_norm_rope_kvwrite_bf16(
152
+ k_pre,
153
+ v_pre,
154
+ k_norm_weight,
155
+ cos,
156
+ sin,
157
+ float(eps),
158
+ k_cache_dst,
159
+ v_cache_dst,
160
+ )
161
+ return k_cache_dst, v_cache_dst
162
+
163
+
164
+ def qwen3_k_norm_rope_kvwrite_devpos_bf16(
165
+ k_pre: torch.Tensor,
166
+ v_pre: torch.Tensor,
167
+ k_norm_weight: torch.Tensor,
168
+ cos: torch.Tensor,
169
+ sin: torch.Tensor,
170
+ cur_pos: torch.Tensor,
171
+ k_cache: torch.Tensor,
172
+ v_cache: torch.Tensor,
173
+ *,
174
+ eps: float = 1e-6,
175
+ ) -> tuple[torch.Tensor, torch.Tensor]:
176
+ """Device-position KV write for CUDA Graph decode loops."""
177
+
178
+ ops.qwen3_k_norm_rope_kvwrite_devpos_bf16(
179
+ k_pre,
180
+ v_pre,
181
+ k_norm_weight,
182
+ cos,
183
+ sin,
184
+ cur_pos,
185
+ float(eps),
186
+ k_cache,
187
+ v_cache,
188
+ )
189
+ return k_cache, v_cache
190
+
191
+
192
+ def avg_pool_vision_tokens_bf16(
193
+ x: torch.Tensor,
194
+ nv: int,
195
+ h: int,
196
+ w: int,
197
+ pool_factor: int,
198
+ *,
199
+ out: Optional[torch.Tensor] = None,
200
+ ) -> torch.Tensor:
201
+ """Average-pool flattened vision tokens in the spatial grid.
202
+
203
+ ``x`` is ``(nv * h * w, dim)`` BF16. The result is
204
+ ``(nv * h / pool_factor * w / pool_factor, dim)`` BF16.
205
+ """
206
+
207
+ if x.dim() != 2:
208
+ raise ValueError("x must have shape (nv * h * w, dim)")
209
+ if h % pool_factor != 0 or w % pool_factor != 0:
210
+ raise ValueError("h and w must be divisible by pool_factor")
211
+ rows = nv * (h // pool_factor) * (w // pool_factor)
212
+ if out is None:
213
+ out = torch.empty((rows, x.shape[1]), device=x.device, dtype=x.dtype)
214
+ ops.avg_pool_vision_tokens_bf16(x, int(nv), int(h), int(w), int(pool_factor), out)
215
+ return out
216
+
217
+
218
+ __all__ = [
219
+ "avg_pool_vision_tokens_bf16",
220
+ "qwen3_k_norm_rope_kvwrite_bf16",
221
+ "qwen3_k_norm_rope_kvwrite_devpos_bf16",
222
+ "qwen3_q_norm_rope_qstage_bf16",
223
+ ]
build/torch211-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _vl_transformer_primitives_cuda_5596053
3
+ ops = torch.ops._vl_transformer_primitives_cuda_5596053
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_vl_transformer_primitives_cuda_5596053::{op_name}"
build/torch211-cxx11-cu130-x86_64-linux/_vl_transformer_primitives_cuda_5596053.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:54ffd2375eea20a8bf09f2832eba1eede35711e7cf9c370fcc3929b5d04038f4
3
+ size 519384
build/torch211-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vl-transformer-primitives",
3
+ "id": "_vl_transformer_primitives_cuda_5596053",
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",
13
+ "12.1+PTX",
14
+ "7.5",
15
+ "8.0",
16
+ "8.6",
17
+ "8.7",
18
+ "8.9",
19
+ "9.0"
20
+ ]
21
+ }
22
+ }
build/torch211-cxx11-cu130-x86_64-linux/vl_transformer_primitives/__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/__init__.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT VL transformer primitive kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+
9
+ from ._ops import add_op_namespace_prefix, ops
10
+
11
+
12
+ def _check_decode_rope(
13
+ x: torch.Tensor,
14
+ weight: torch.Tensor,
15
+ cos: torch.Tensor,
16
+ sin: torch.Tensor,
17
+ out: torch.Tensor,
18
+ name: str,
19
+ ) -> None:
20
+ if x.dim() != 2 or x.shape[1] != 128:
21
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
22
+ if weight.shape != (128,):
23
+ raise RuntimeError("norm weight must have shape (128,)")
24
+ if cos.shape != (64,) or sin.shape != (64,):
25
+ raise RuntimeError("cos and sin must have shape (64,)")
26
+ if out.shape != x.shape:
27
+ raise RuntimeError("out must have the same shape as input")
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_q_norm_rope_qstage_bf16"))
31
+ def _qwen3_q_norm_rope_qstage_bf16_fake(
32
+ q_pre: torch.Tensor,
33
+ q_norm_weight: torch.Tensor,
34
+ cos: torch.Tensor,
35
+ sin: torch.Tensor,
36
+ eps: float,
37
+ q_out: torch.Tensor,
38
+ ) -> None:
39
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
40
+ return None
41
+
42
+
43
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_bf16"))
44
+ def _qwen3_k_norm_rope_kvwrite_bf16_fake(
45
+ k_pre: torch.Tensor,
46
+ v_pre: torch.Tensor,
47
+ k_norm_weight: torch.Tensor,
48
+ cos: torch.Tensor,
49
+ sin: torch.Tensor,
50
+ eps: float,
51
+ k_cache_dst: torch.Tensor,
52
+ v_cache_dst: torch.Tensor,
53
+ ) -> None:
54
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
55
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
56
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
57
+ return None
58
+
59
+
60
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_devpos_bf16"))
61
+ def _qwen3_k_norm_rope_kvwrite_devpos_bf16_fake(
62
+ k_pre: torch.Tensor,
63
+ v_pre: torch.Tensor,
64
+ k_norm_weight: torch.Tensor,
65
+ cos: torch.Tensor,
66
+ sin: torch.Tensor,
67
+ cur_pos: torch.Tensor,
68
+ eps: float,
69
+ k_cache: torch.Tensor,
70
+ v_cache: torch.Tensor,
71
+ ) -> None:
72
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
73
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
74
+ n_kv = k_pre.shape[0]
75
+ if v_pre.shape != k_pre.shape:
76
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
77
+ if k_norm_weight.shape != (128,):
78
+ raise RuntimeError("k_norm_weight must have shape (128,)")
79
+ if cos.shape != (64,) or sin.shape != (64,):
80
+ raise RuntimeError("cos and sin must have shape (64,)")
81
+ if cur_pos.numel() != 1:
82
+ raise RuntimeError("cur_pos must have one int32 element")
83
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
84
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
85
+ if v_cache.shape != k_cache.shape:
86
+ raise RuntimeError("v_cache must have the same shape as k_cache")
87
+ return None
88
+
89
+
90
+ @torch.library.register_fake(add_op_namespace_prefix("avg_pool_vision_tokens_bf16"))
91
+ def _avg_pool_vision_tokens_bf16_fake(
92
+ x: torch.Tensor,
93
+ nv: int,
94
+ h: int,
95
+ w: int,
96
+ pool_factor: int,
97
+ out: torch.Tensor,
98
+ ) -> None:
99
+ if x.dim() != 2:
100
+ raise RuntimeError("x must have shape (nv * h * w, dim)")
101
+ dim = x.shape[1]
102
+ if x.shape[0] != nv * h * w:
103
+ raise RuntimeError("x.shape[0] must equal nv * h * w")
104
+ if h % pool_factor != 0 or w % pool_factor != 0:
105
+ raise RuntimeError("h and w must be divisible by pool_factor")
106
+ expected = (nv * (h // pool_factor) * (w // pool_factor), dim)
107
+ if out.shape != expected:
108
+ raise RuntimeError("out has the wrong pooled shape")
109
+ return None
110
+
111
+
112
+ def qwen3_q_norm_rope_qstage_bf16(
113
+ q_pre: torch.Tensor,
114
+ q_norm_weight: torch.Tensor,
115
+ cos: torch.Tensor,
116
+ sin: torch.Tensor,
117
+ *,
118
+ eps: float = 1e-6,
119
+ out: Optional[torch.Tensor] = None,
120
+ ) -> torch.Tensor:
121
+ """Apply Q RMSNorm + full RoPE and write decode staging output.
122
+
123
+ Inputs are contiguous BF16 tensors. ``q_pre`` has shape
124
+ ``(n_q_heads, 128)``. ``q_norm_weight`` has shape ``(128,)``.
125
+ ``cos`` and ``sin`` have shape ``(64,)``.
126
+ """
127
+
128
+ if out is None:
129
+ out = torch.empty_like(q_pre)
130
+ ops.qwen3_q_norm_rope_qstage_bf16(q_pre, q_norm_weight, cos, sin, float(eps), out)
131
+ return out
132
+
133
+
134
+ def qwen3_k_norm_rope_kvwrite_bf16(
135
+ k_pre: torch.Tensor,
136
+ v_pre: torch.Tensor,
137
+ k_norm_weight: torch.Tensor,
138
+ cos: torch.Tensor,
139
+ sin: torch.Tensor,
140
+ *,
141
+ eps: float = 1e-6,
142
+ k_cache_dst: Optional[torch.Tensor] = None,
143
+ v_cache_dst: Optional[torch.Tensor] = None,
144
+ ) -> tuple[torch.Tensor, torch.Tensor]:
145
+ """Apply K RMSNorm + full RoPE and copy V into the current KV slot."""
146
+
147
+ if k_cache_dst is None:
148
+ k_cache_dst = torch.empty_like(k_pre)
149
+ if v_cache_dst is None:
150
+ v_cache_dst = torch.empty_like(v_pre)
151
+ ops.qwen3_k_norm_rope_kvwrite_bf16(
152
+ k_pre,
153
+ v_pre,
154
+ k_norm_weight,
155
+ cos,
156
+ sin,
157
+ float(eps),
158
+ k_cache_dst,
159
+ v_cache_dst,
160
+ )
161
+ return k_cache_dst, v_cache_dst
162
+
163
+
164
+ def qwen3_k_norm_rope_kvwrite_devpos_bf16(
165
+ k_pre: torch.Tensor,
166
+ v_pre: torch.Tensor,
167
+ k_norm_weight: torch.Tensor,
168
+ cos: torch.Tensor,
169
+ sin: torch.Tensor,
170
+ cur_pos: torch.Tensor,
171
+ k_cache: torch.Tensor,
172
+ v_cache: torch.Tensor,
173
+ *,
174
+ eps: float = 1e-6,
175
+ ) -> tuple[torch.Tensor, torch.Tensor]:
176
+ """Device-position KV write for CUDA Graph decode loops."""
177
+
178
+ ops.qwen3_k_norm_rope_kvwrite_devpos_bf16(
179
+ k_pre,
180
+ v_pre,
181
+ k_norm_weight,
182
+ cos,
183
+ sin,
184
+ cur_pos,
185
+ float(eps),
186
+ k_cache,
187
+ v_cache,
188
+ )
189
+ return k_cache, v_cache
190
+
191
+
192
+ def avg_pool_vision_tokens_bf16(
193
+ x: torch.Tensor,
194
+ nv: int,
195
+ h: int,
196
+ w: int,
197
+ pool_factor: int,
198
+ *,
199
+ out: Optional[torch.Tensor] = None,
200
+ ) -> torch.Tensor:
201
+ """Average-pool flattened vision tokens in the spatial grid.
202
+
203
+ ``x`` is ``(nv * h * w, dim)`` BF16. The result is
204
+ ``(nv * h / pool_factor * w / pool_factor, dim)`` BF16.
205
+ """
206
+
207
+ if x.dim() != 2:
208
+ raise ValueError("x must have shape (nv * h * w, dim)")
209
+ if h % pool_factor != 0 or w % pool_factor != 0:
210
+ raise ValueError("h and w must be divisible by pool_factor")
211
+ rows = nv * (h // pool_factor) * (w // pool_factor)
212
+ if out is None:
213
+ out = torch.empty((rows, x.shape[1]), device=x.device, dtype=x.dtype)
214
+ ops.avg_pool_vision_tokens_bf16(x, int(nv), int(h), int(w), int(pool_factor), out)
215
+ return out
216
+
217
+
218
+ __all__ = [
219
+ "avg_pool_vision_tokens_bf16",
220
+ "qwen3_k_norm_rope_kvwrite_bf16",
221
+ "qwen3_k_norm_rope_kvwrite_devpos_bf16",
222
+ "qwen3_q_norm_rope_qstage_bf16",
223
+ ]
build/torch212-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _vl_transformer_primitives_cuda_5596053
3
+ ops = torch.ops._vl_transformer_primitives_cuda_5596053
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_vl_transformer_primitives_cuda_5596053::{op_name}"
build/torch212-cxx11-cu130-x86_64-linux/_vl_transformer_primitives_cuda_5596053.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e26bda5c6683760bb1e12e2451e31152f8c9acc38ff3dee5042674378570216b
3
+ size 526112
build/torch212-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vl-transformer-primitives",
3
+ "id": "_vl_transformer_primitives_cuda_5596053",
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",
13
+ "12.1+PTX",
14
+ "7.5",
15
+ "8.0",
16
+ "8.6",
17
+ "8.7",
18
+ "8.9",
19
+ "9.0"
20
+ ]
21
+ }
22
+ }
build/torch212-cxx11-cu130-x86_64-linux/vl_transformer_primitives/__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/__init__.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT VL transformer primitive kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+
9
+ from ._ops import add_op_namespace_prefix, ops
10
+
11
+
12
+ def _check_decode_rope(
13
+ x: torch.Tensor,
14
+ weight: torch.Tensor,
15
+ cos: torch.Tensor,
16
+ sin: torch.Tensor,
17
+ out: torch.Tensor,
18
+ name: str,
19
+ ) -> None:
20
+ if x.dim() != 2 or x.shape[1] != 128:
21
+ raise RuntimeError(f"{name} must have shape (heads, 128)")
22
+ if weight.shape != (128,):
23
+ raise RuntimeError("norm weight must have shape (128,)")
24
+ if cos.shape != (64,) or sin.shape != (64,):
25
+ raise RuntimeError("cos and sin must have shape (64,)")
26
+ if out.shape != x.shape:
27
+ raise RuntimeError("out must have the same shape as input")
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_q_norm_rope_qstage_bf16"))
31
+ def _qwen3_q_norm_rope_qstage_bf16_fake(
32
+ q_pre: torch.Tensor,
33
+ q_norm_weight: torch.Tensor,
34
+ cos: torch.Tensor,
35
+ sin: torch.Tensor,
36
+ eps: float,
37
+ q_out: torch.Tensor,
38
+ ) -> None:
39
+ _check_decode_rope(q_pre, q_norm_weight, cos, sin, q_out, "q_pre")
40
+ return None
41
+
42
+
43
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_bf16"))
44
+ def _qwen3_k_norm_rope_kvwrite_bf16_fake(
45
+ k_pre: torch.Tensor,
46
+ v_pre: torch.Tensor,
47
+ k_norm_weight: torch.Tensor,
48
+ cos: torch.Tensor,
49
+ sin: torch.Tensor,
50
+ eps: float,
51
+ k_cache_dst: torch.Tensor,
52
+ v_cache_dst: torch.Tensor,
53
+ ) -> None:
54
+ _check_decode_rope(k_pre, k_norm_weight, cos, sin, k_cache_dst, "k_pre")
55
+ if v_pre.shape != k_pre.shape or v_cache_dst.shape != k_pre.shape:
56
+ raise RuntimeError("v_pre and v_cache_dst must have shape (n_kv_heads, 128)")
57
+ return None
58
+
59
+
60
+ @torch.library.register_fake(add_op_namespace_prefix("qwen3_k_norm_rope_kvwrite_devpos_bf16"))
61
+ def _qwen3_k_norm_rope_kvwrite_devpos_bf16_fake(
62
+ k_pre: torch.Tensor,
63
+ v_pre: torch.Tensor,
64
+ k_norm_weight: torch.Tensor,
65
+ cos: torch.Tensor,
66
+ sin: torch.Tensor,
67
+ cur_pos: torch.Tensor,
68
+ eps: float,
69
+ k_cache: torch.Tensor,
70
+ v_cache: torch.Tensor,
71
+ ) -> None:
72
+ if k_pre.dim() != 2 or k_pre.shape[1] != 128:
73
+ raise RuntimeError("k_pre must have shape (n_kv_heads, 128)")
74
+ n_kv = k_pre.shape[0]
75
+ if v_pre.shape != k_pre.shape:
76
+ raise RuntimeError("v_pre must have shape (n_kv_heads, 128)")
77
+ if k_norm_weight.shape != (128,):
78
+ raise RuntimeError("k_norm_weight must have shape (128,)")
79
+ if cos.shape != (64,) or sin.shape != (64,):
80
+ raise RuntimeError("cos and sin must have shape (64,)")
81
+ if cur_pos.numel() != 1:
82
+ raise RuntimeError("cur_pos must have one int32 element")
83
+ if k_cache.dim() != 3 or k_cache.shape[1:] != (n_kv, 128):
84
+ raise RuntimeError("k_cache must have shape (max_seq_len, n_kv_heads, 128)")
85
+ if v_cache.shape != k_cache.shape:
86
+ raise RuntimeError("v_cache must have the same shape as k_cache")
87
+ return None
88
+
89
+
90
+ @torch.library.register_fake(add_op_namespace_prefix("avg_pool_vision_tokens_bf16"))
91
+ def _avg_pool_vision_tokens_bf16_fake(
92
+ x: torch.Tensor,
93
+ nv: int,
94
+ h: int,
95
+ w: int,
96
+ pool_factor: int,
97
+ out: torch.Tensor,
98
+ ) -> None:
99
+ if x.dim() != 2:
100
+ raise RuntimeError("x must have shape (nv * h * w, dim)")
101
+ dim = x.shape[1]
102
+ if x.shape[0] != nv * h * w:
103
+ raise RuntimeError("x.shape[0] must equal nv * h * w")
104
+ if h % pool_factor != 0 or w % pool_factor != 0:
105
+ raise RuntimeError("h and w must be divisible by pool_factor")
106
+ expected = (nv * (h // pool_factor) * (w // pool_factor), dim)
107
+ if out.shape != expected:
108
+ raise RuntimeError("out has the wrong pooled shape")
109
+ return None
110
+
111
+
112
+ def qwen3_q_norm_rope_qstage_bf16(
113
+ q_pre: torch.Tensor,
114
+ q_norm_weight: torch.Tensor,
115
+ cos: torch.Tensor,
116
+ sin: torch.Tensor,
117
+ *,
118
+ eps: float = 1e-6,
119
+ out: Optional[torch.Tensor] = None,
120
+ ) -> torch.Tensor:
121
+ """Apply Q RMSNorm + full RoPE and write decode staging output.
122
+
123
+ Inputs are contiguous BF16 tensors. ``q_pre`` has shape
124
+ ``(n_q_heads, 128)``. ``q_norm_weight`` has shape ``(128,)``.
125
+ ``cos`` and ``sin`` have shape ``(64,)``.
126
+ """
127
+
128
+ if out is None:
129
+ out = torch.empty_like(q_pre)
130
+ ops.qwen3_q_norm_rope_qstage_bf16(q_pre, q_norm_weight, cos, sin, float(eps), out)
131
+ return out
132
+
133
+
134
+ def qwen3_k_norm_rope_kvwrite_bf16(
135
+ k_pre: torch.Tensor,
136
+ v_pre: torch.Tensor,
137
+ k_norm_weight: torch.Tensor,
138
+ cos: torch.Tensor,
139
+ sin: torch.Tensor,
140
+ *,
141
+ eps: float = 1e-6,
142
+ k_cache_dst: Optional[torch.Tensor] = None,
143
+ v_cache_dst: Optional[torch.Tensor] = None,
144
+ ) -> tuple[torch.Tensor, torch.Tensor]:
145
+ """Apply K RMSNorm + full RoPE and copy V into the current KV slot."""
146
+
147
+ if k_cache_dst is None:
148
+ k_cache_dst = torch.empty_like(k_pre)
149
+ if v_cache_dst is None:
150
+ v_cache_dst = torch.empty_like(v_pre)
151
+ ops.qwen3_k_norm_rope_kvwrite_bf16(
152
+ k_pre,
153
+ v_pre,
154
+ k_norm_weight,
155
+ cos,
156
+ sin,
157
+ float(eps),
158
+ k_cache_dst,
159
+ v_cache_dst,
160
+ )
161
+ return k_cache_dst, v_cache_dst
162
+
163
+
164
+ def qwen3_k_norm_rope_kvwrite_devpos_bf16(
165
+ k_pre: torch.Tensor,
166
+ v_pre: torch.Tensor,
167
+ k_norm_weight: torch.Tensor,
168
+ cos: torch.Tensor,
169
+ sin: torch.Tensor,
170
+ cur_pos: torch.Tensor,
171
+ k_cache: torch.Tensor,
172
+ v_cache: torch.Tensor,
173
+ *,
174
+ eps: float = 1e-6,
175
+ ) -> tuple[torch.Tensor, torch.Tensor]:
176
+ """Device-position KV write for CUDA Graph decode loops."""
177
+
178
+ ops.qwen3_k_norm_rope_kvwrite_devpos_bf16(
179
+ k_pre,
180
+ v_pre,
181
+ k_norm_weight,
182
+ cos,
183
+ sin,
184
+ cur_pos,
185
+ float(eps),
186
+ k_cache,
187
+ v_cache,
188
+ )
189
+ return k_cache, v_cache
190
+
191
+
192
+ def avg_pool_vision_tokens_bf16(
193
+ x: torch.Tensor,
194
+ nv: int,
195
+ h: int,
196
+ w: int,
197
+ pool_factor: int,
198
+ *,
199
+ out: Optional[torch.Tensor] = None,
200
+ ) -> torch.Tensor:
201
+ """Average-pool flattened vision tokens in the spatial grid.
202
+
203
+ ``x`` is ``(nv * h * w, dim)`` BF16. The result is
204
+ ``(nv * h / pool_factor * w / pool_factor, dim)`` BF16.
205
+ """
206
+
207
+ if x.dim() != 2:
208
+ raise ValueError("x must have shape (nv * h * w, dim)")
209
+ if h % pool_factor != 0 or w % pool_factor != 0:
210
+ raise ValueError("h and w must be divisible by pool_factor")
211
+ rows = nv * (h // pool_factor) * (w // pool_factor)
212
+ if out is None:
213
+ out = torch.empty((rows, x.shape[1]), device=x.device, dtype=x.dtype)
214
+ ops.avg_pool_vision_tokens_bf16(x, int(nv), int(h), int(w), int(pool_factor), out)
215
+ return out
216
+
217
+
218
+ __all__ = [
219
+ "avg_pool_vision_tokens_bf16",
220
+ "qwen3_k_norm_rope_kvwrite_bf16",
221
+ "qwen3_k_norm_rope_kvwrite_devpos_bf16",
222
+ "qwen3_q_norm_rope_qstage_bf16",
223
+ ]
build/torch212-cxx11-cu132-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _vl_transformer_primitives_cuda_5596053
3
+ ops = torch.ops._vl_transformer_primitives_cuda_5596053
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_vl_transformer_primitives_cuda_5596053::{op_name}"
build/torch212-cxx11-cu132-x86_64-linux/_vl_transformer_primitives_cuda_5596053.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b7cd744841ddb4334d42601a5606c68c60e5756a85859a36895f89a222b36b93
3
+ size 526112
build/torch212-cxx11-cu132-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vl-transformer-primitives",
3
+ "id": "_vl_transformer_primitives_cuda_5596053",
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",
13
+ "12.1+PTX",
14
+ "7.5",
15
+ "8.0",
16
+ "8.6",
17
+ "8.7",
18
+ "8.9",
19
+ "9.0"
20
+ ]
21
+ }
22
+ }
build/torch212-cxx11-cu132-x86_64-linux/vl_transformer_primitives/__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")))