marcsun13 HF Staff commited on
Commit
2113014
·
1 Parent(s): 8eddc6f

Add prebuilt Metal variants for torch 2.12 and 2.13

Browse files

Verified against the pure-torch reference dequantizer: dequantize is bit-exact
for q8_0/q4_K/q5_K/q6_K and mul_mat_vec agrees to ~1e-6 relative on all four.

.gitattributes CHANGED
@@ -48,3 +48,5 @@ build/torch211-cxx11-cu130-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filte
48
  build/torch212-cxx11-cu126-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filter=lfs diff=lfs merge=lfs -text
49
  build/torch212-cxx11-cu130-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filter=lfs diff=lfs merge=lfs -text
50
  build/torch212-cxx11-cu132-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filter=lfs diff=lfs merge=lfs -text
 
 
 
48
  build/torch212-cxx11-cu126-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filter=lfs diff=lfs merge=lfs -text
49
  build/torch212-cxx11-cu130-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filter=lfs diff=lfs merge=lfs -text
50
  build/torch212-cxx11-cu132-x86_64-linux/_gguf_kernels_cuda_e4e2164.abi3.so filter=lfs diff=lfs merge=lfs -text
51
+ build/torch212-metal-aarch64-darwin/_gguf_kernels_metal_8eddc6f.abi3.so filter=lfs diff=lfs merge=lfs -text
52
+ build/torch213-metal-aarch64-darwin/_gguf_kernels_metal_8eddc6f.abi3.so filter=lfs diff=lfs merge=lfs -text
build/torch212-metal-aarch64-darwin/__init__.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compute directly on the packed blocks of a GGUF checkpoint.
2
+
3
+ A GGUF weight is stored as blocks of 32 or 256 values sharing a scale. These kernels read that
4
+ layout as it is, so a quantized model can be loaded and run without ever materializing a dense
5
+ copy — which is the whole memory saving.
6
+
7
+ Ported from llama.cpp's ggml-cuda; `vendor/UPSTREAM` pins the revision. The ops are named for what
8
+ they do rather than for a backend: each backend registers its own implementation of the same
9
+ schema, so calls dispatch on the tensor's device.
10
+ """
11
+
12
+ import torch
13
+
14
+ from ._ops import add_op_namespace_prefix, ops
15
+
16
+
17
+ __all__ = ["GEMV_TYPES", "MAX_GEMV_ROWS", "dequantize", "mul_mat_vec"]
18
+
19
+ # Upstream's MMVQ_MAX_BATCH_SIZE: `mul_mat_vec` has no implementation beyond this many rows, so a
20
+ # caller with more (prefill) dequantizes and uses an ordinary matmul.
21
+ MAX_GEMV_ROWS = 8
22
+
23
+ # ggml type ids `mul_mat_vec` implements: Q4_0/Q4_1/Q5_0/Q5_1/Q8_0, the K quants, and the IQ quants.
24
+ # `dequantize` covers more, so check this one before choosing the fused path.
25
+ GEMV_TYPES = frozenset({2, 3, 6, 7, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 29, 39, 40, 41, 42})
26
+
27
+
28
+ def dequantize(
29
+ blocks: torch.Tensor, ggml_type: int, rows: int, cols: int, dtype: torch.dtype
30
+ ) -> torch.Tensor:
31
+ """`(rows, bytes_per_row)` uint8 blocks -> `(rows, cols)` values of `dtype`."""
32
+ return ops.dequantize(blocks, ggml_type, rows, cols, dtype)
33
+
34
+
35
+ def mul_mat_vec(
36
+ blocks: torch.Tensor, x: torch.Tensor, ggml_type: int, out_features: int
37
+ ) -> torch.Tensor:
38
+ """Fused dequantize-gemv: `x @ blocks.T` without unpacking `blocks`.
39
+
40
+ `x` is `(rows, in_features)` and `rows` must be at most `MAX_GEMV_ROWS`. The result is f32
41
+ whatever `x`'s dtype was, since the kernel writes an f32 destination; cast it at the call site,
42
+ where the cast can be fused into whatever consumes it.
43
+ """
44
+ return ops.mul_mat_vec(blocks, x, ggml_type, out_features)
45
+
46
+
47
+ # Without these, torch.compile cannot trace the ops and breaks the graph at every call.
48
+ @torch.library.register_fake(add_op_namespace_prefix("dequantize"))
49
+ def _dequantize_fake(blocks, ggml_type, rows, cols, dtype):
50
+ return blocks.new_empty((rows, cols), dtype=dtype)
51
+
52
+
53
+ @torch.library.register_fake(add_op_namespace_prefix("mul_mat_vec"))
54
+ def _mul_mat_vec_fake(blocks, x, ggml_type, out_features):
55
+ return x.new_empty((x.shape[0], out_features), dtype=torch.float32)
build/torch212-metal-aarch64-darwin/_gguf_kernels_metal_8eddc6f.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1a3bb5ec82d6f44ab3dfd809791c7934380dd1d104e08af2eb97fa224d79c597
3
+ size 7378384
build/torch212-metal-aarch64-darwin/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _gguf_kernels_metal_8eddc6f
3
+ ops = torch.ops._gguf_kernels_metal_8eddc6f
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_gguf_kernels_metal_8eddc6f::{op_name}"
build/torch212-metal-aarch64-darwin/metadata.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "gguf-kernels",
3
+ "id": "_gguf_kernels_metal_8eddc6f",
4
+ "version": 1,
5
+ "license": "MIT",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "metal"
9
+ },
10
+ "digest": {
11
+ "algorithm": "sha256",
12
+ "files": {
13
+ "__init__.py": "Xl1/3Qvp7VYrhYXje6GLBwPa9H7ICY5jHKLc7frZvlo=",
14
+ "_gguf_kernels_metal_8eddc6f.abi3.so": "Gju17ILW9Eqz39gJeRx5NDgN0dEE4Iry65f6Ik15xZc=",
15
+ "_ops.py": "661PiUKH8VTMPv3Ls7VBKY+93qr+jFOKGTo1kZZV/yM="
16
+ }
17
+ },
18
+ "provenance": {
19
+ "kernel-builder": {
20
+ "version": "0.17.0-dev0",
21
+ "sha": "81f55ea30fd8f819dcf93a3c934dd584c895bd2f",
22
+ "dirty": false
23
+ },
24
+ "kernel": {
25
+ "sha": "8eddc6f8d9c7c81f19fdb11f8725c4bf9801f62d",
26
+ "dirty": false
27
+ }
28
+ }
29
+ }
build/torch213-metal-aarch64-darwin/__init__.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compute directly on the packed blocks of a GGUF checkpoint.
2
+
3
+ A GGUF weight is stored as blocks of 32 or 256 values sharing a scale. These kernels read that
4
+ layout as it is, so a quantized model can be loaded and run without ever materializing a dense
5
+ copy — which is the whole memory saving.
6
+
7
+ Ported from llama.cpp's ggml-cuda; `vendor/UPSTREAM` pins the revision. The ops are named for what
8
+ they do rather than for a backend: each backend registers its own implementation of the same
9
+ schema, so calls dispatch on the tensor's device.
10
+ """
11
+
12
+ import torch
13
+
14
+ from ._ops import add_op_namespace_prefix, ops
15
+
16
+
17
+ __all__ = ["GEMV_TYPES", "MAX_GEMV_ROWS", "dequantize", "mul_mat_vec"]
18
+
19
+ # Upstream's MMVQ_MAX_BATCH_SIZE: `mul_mat_vec` has no implementation beyond this many rows, so a
20
+ # caller with more (prefill) dequantizes and uses an ordinary matmul.
21
+ MAX_GEMV_ROWS = 8
22
+
23
+ # ggml type ids `mul_mat_vec` implements: Q4_0/Q4_1/Q5_0/Q5_1/Q8_0, the K quants, and the IQ quants.
24
+ # `dequantize` covers more, so check this one before choosing the fused path.
25
+ GEMV_TYPES = frozenset({2, 3, 6, 7, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 29, 39, 40, 41, 42})
26
+
27
+
28
+ def dequantize(
29
+ blocks: torch.Tensor, ggml_type: int, rows: int, cols: int, dtype: torch.dtype
30
+ ) -> torch.Tensor:
31
+ """`(rows, bytes_per_row)` uint8 blocks -> `(rows, cols)` values of `dtype`."""
32
+ return ops.dequantize(blocks, ggml_type, rows, cols, dtype)
33
+
34
+
35
+ def mul_mat_vec(
36
+ blocks: torch.Tensor, x: torch.Tensor, ggml_type: int, out_features: int
37
+ ) -> torch.Tensor:
38
+ """Fused dequantize-gemv: `x @ blocks.T` without unpacking `blocks`.
39
+
40
+ `x` is `(rows, in_features)` and `rows` must be at most `MAX_GEMV_ROWS`. The result is f32
41
+ whatever `x`'s dtype was, since the kernel writes an f32 destination; cast it at the call site,
42
+ where the cast can be fused into whatever consumes it.
43
+ """
44
+ return ops.mul_mat_vec(blocks, x, ggml_type, out_features)
45
+
46
+
47
+ # Without these, torch.compile cannot trace the ops and breaks the graph at every call.
48
+ @torch.library.register_fake(add_op_namespace_prefix("dequantize"))
49
+ def _dequantize_fake(blocks, ggml_type, rows, cols, dtype):
50
+ return blocks.new_empty((rows, cols), dtype=dtype)
51
+
52
+
53
+ @torch.library.register_fake(add_op_namespace_prefix("mul_mat_vec"))
54
+ def _mul_mat_vec_fake(blocks, x, ggml_type, out_features):
55
+ return x.new_empty((x.shape[0], out_features), dtype=torch.float32)
build/torch213-metal-aarch64-darwin/_gguf_kernels_metal_8eddc6f.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b6f177c4b3987b307fd2430c2381e0f125e06bde41f001cf2f3684d677a9cfaf
3
+ size 7378384
build/torch213-metal-aarch64-darwin/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _gguf_kernels_metal_8eddc6f
3
+ ops = torch.ops._gguf_kernels_metal_8eddc6f
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_gguf_kernels_metal_8eddc6f::{op_name}"
build/torch213-metal-aarch64-darwin/metadata.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "gguf-kernels",
3
+ "id": "_gguf_kernels_metal_8eddc6f",
4
+ "version": 1,
5
+ "license": "MIT",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "metal"
9
+ },
10
+ "digest": {
11
+ "algorithm": "sha256",
12
+ "files": {
13
+ "__init__.py": "Xl1/3Qvp7VYrhYXje6GLBwPa9H7ICY5jHKLc7frZvlo=",
14
+ "_gguf_kernels_metal_8eddc6f.abi3.so": "tvF3xLOYezB/0kMMI4Hg8SXga95B8AHPLzaE1nepz68=",
15
+ "_ops.py": "661PiUKH8VTMPv3Ls7VBKY+93qr+jFOKGTo1kZZV/yM="
16
+ }
17
+ },
18
+ "provenance": {
19
+ "kernel-builder": {
20
+ "version": "0.17.0-dev0",
21
+ "sha": "81f55ea30fd8f819dcf93a3c934dd584c895bd2f",
22
+ "dirty": false
23
+ },
24
+ "kernel": {
25
+ "sha": "8eddc6f8d9c7c81f19fdb11f8725c4bf9801f62d",
26
+ "dirty": false
27
+ }
28
+ }
29
+ }