marcsun13 HF Staff commited on
Commit
87cb5c1
·
verified ·
1 Parent(s): 2f7d318

trim the metal shader to the dispatched kernels

Browse files
.gitattributes CHANGED
@@ -56,3 +56,5 @@ build/torch212-cxx11-cu132-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filte
56
  build/torch213-cxx11-cu126-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filter=lfs diff=lfs merge=lfs -text
57
  build/torch213-cxx11-cu130-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filter=lfs diff=lfs merge=lfs -text
58
  build/torch213-cxx11-cu132-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filter=lfs diff=lfs merge=lfs -text
 
 
 
56
  build/torch213-cxx11-cu126-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filter=lfs diff=lfs merge=lfs -text
57
  build/torch213-cxx11-cu130-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filter=lfs diff=lfs merge=lfs -text
58
  build/torch213-cxx11-cu132-x86_64-linux/_gguf_kernels_cuda_1df3b99.abi3.so filter=lfs diff=lfs merge=lfs -text
59
+ build/torch212-metal-aarch64-darwin/_gguf_kernels_metal_7b10656.abi3.so filter=lfs diff=lfs merge=lfs -text
60
+ build/torch213-metal-aarch64-darwin/_gguf_kernels_metal_7b10656.abi3.so filter=lfs diff=lfs merge=lfs -text
build.toml CHANGED
@@ -87,9 +87,11 @@ src = [
87
  "gguf_metal/gguf_metal.cpp",
88
  "gguf_metal/ggml_dispatch.mm",
89
  "gguf_metal/common.h",
90
- # ggml's shader, compiled into the embedded metallib. Its mul_mv_* kernels are what the gemv
91
- # dispatches to; the K-quants have no dequantize-to-dense kernel upstream, so that one is ours.
92
- "vendor/src/ggml-metal/ggml-metal.metal",
 
 
93
  "vendor/src/ggml-metal/ggml-metal-impl.h",
94
  "vendor/src/ggml-common.h",
95
  ]
 
87
  "gguf_metal/gguf_metal.cpp",
88
  "gguf_metal/ggml_dispatch.mm",
89
  "gguf_metal/common.h",
90
+ # ggml's shader, cut by `trim_shader.py` to the three kernels the dispatch names per quantization
91
+ # type -- 628 exported kernels down to 56, since none of ggml's attention, norm, conv or sampling
92
+ # kernels are reachable from here. `vendor/` keeps the untouched copy it is generated from.
93
+ "gguf_metal/ggml-metal-quant.metal",
94
+ "gguf_metal/quant_types.h",
95
  "vendor/src/ggml-metal/ggml-metal-impl.h",
96
  "vendor/src/ggml-common.h",
97
  ]
build/torch212-metal-aarch64-darwin/_gguf_kernels_metal_7b10656.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d5ea02aab35301ff5a378d86de62477becd6604cd56ef6000802c15677c9375d
3
+ size 814816
build/torch212-metal-aarch64-darwin/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _gguf_kernels_metal_0becb0a
3
- ops = torch.ops._gguf_kernels_metal_0becb0a
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_gguf_kernels_metal_0becb0a::{op_name}"
 
1
  import torch
2
+ from . import _gguf_kernels_metal_7b10656
3
+ ops = torch.ops._gguf_kernels_metal_7b10656
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_gguf_kernels_metal_7b10656::{op_name}"
build/torch212-metal-aarch64-darwin/metadata.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "gguf-kernels",
3
- "id": "_gguf_kernels_metal_0becb0a",
4
  "version": 1,
5
  "license": "MIT",
6
  "python-depends": [],
@@ -11,8 +11,8 @@
11
  "algorithm": "sha256",
12
  "files": {
13
  "__init__.py": "GoFcHJbbJ9C8b40XNznMxsTcC+CV9kaFE8a1DP763kU=",
14
- "_gguf_kernels_metal_0becb0a.abi3.so": "s2nMW0aOL9O5rIT1xjiXpSvCXgOBw3EE0R0JVvWtTdk=",
15
- "_ops.py": "3htLDPFBO8zVroZkTqPmryb9pDPjZ5OuMItEBg94Jp4="
16
  }
17
  },
18
  "provenance": {
@@ -22,7 +22,7 @@
22
  "dirty": false
23
  },
24
  "kernel": {
25
- "sha": "0becb0aafe12b27da05fa08f4b6cc7c72b288fbb",
26
  "dirty": false
27
  }
28
  }
 
1
  {
2
  "name": "gguf-kernels",
3
+ "id": "_gguf_kernels_metal_7b10656",
4
  "version": 1,
5
  "license": "MIT",
6
  "python-depends": [],
 
11
  "algorithm": "sha256",
12
  "files": {
13
  "__init__.py": "GoFcHJbbJ9C8b40XNznMxsTcC+CV9kaFE8a1DP763kU=",
14
+ "_gguf_kernels_metal_7b10656.abi3.so": "1eoCqrNTAf9aN42G3mJHe+zWYEzVbvYACALBVnfJN10=",
15
+ "_ops.py": "nwCRMS2OmSxach0Edkf8TLdxZXmrrMhMJntgxLN2MAI="
16
  }
17
  },
18
  "provenance": {
 
22
  "dirty": false
23
  },
24
  "kernel": {
25
+ "sha": "7b106564513dead0e4416ea0c7d3898eb23e0ca2",
26
  "dirty": false
27
  }
28
  }
build/torch213-metal-aarch64-darwin/_gguf_kernels_metal_7b10656.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e6dbb2eaefd58d768f8a9e30e89b08f1f7a139648b12fb8924a34caccbd8d066
3
+ size 814816
build/torch213-metal-aarch64-darwin/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _gguf_kernels_metal_0becb0a
3
- ops = torch.ops._gguf_kernels_metal_0becb0a
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_gguf_kernels_metal_0becb0a::{op_name}"
 
1
  import torch
2
+ from . import _gguf_kernels_metal_7b10656
3
+ ops = torch.ops._gguf_kernels_metal_7b10656
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_gguf_kernels_metal_7b10656::{op_name}"
build/torch213-metal-aarch64-darwin/metadata.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "gguf-kernels",
3
- "id": "_gguf_kernels_metal_0becb0a",
4
  "version": 1,
5
  "license": "MIT",
6
  "python-depends": [],
@@ -11,8 +11,8 @@
11
  "algorithm": "sha256",
12
  "files": {
13
  "__init__.py": "GoFcHJbbJ9C8b40XNznMxsTcC+CV9kaFE8a1DP763kU=",
14
- "_gguf_kernels_metal_0becb0a.abi3.so": "WdDCcX9EMIvJZbguUgzORbCm9+SWvJ6V38I2fbx19EE=",
15
- "_ops.py": "3htLDPFBO8zVroZkTqPmryb9pDPjZ5OuMItEBg94Jp4="
16
  }
17
  },
18
  "provenance": {
@@ -22,7 +22,7 @@
22
  "dirty": false
23
  },
24
  "kernel": {
25
- "sha": "0becb0aafe12b27da05fa08f4b6cc7c72b288fbb",
26
  "dirty": false
27
  }
28
  }
 
1
  {
2
  "name": "gguf-kernels",
3
+ "id": "_gguf_kernels_metal_7b10656",
4
  "version": 1,
5
  "license": "MIT",
6
  "python-depends": [],
 
11
  "algorithm": "sha256",
12
  "files": {
13
  "__init__.py": "GoFcHJbbJ9C8b40XNznMxsTcC+CV9kaFE8a1DP763kU=",
14
+ "_gguf_kernels_metal_7b10656.abi3.so": "5tuy6u/VjXaPip4w6JsI8fehOWSLEvuJJKNMrMvY0GY=",
15
+ "_ops.py": "nwCRMS2OmSxach0Edkf8TLdxZXmrrMhMJntgxLN2MAI="
16
  }
17
  },
18
  "provenance": {
 
22
  "dirty": false
23
  },
24
  "kernel": {
25
+ "sha": "7b106564513dead0e4416ea0c7d3898eb23e0ca2",
26
  "dirty": false
27
  }
28
  }
gguf_metal/ggml-metal-quant.metal ADDED
The diff for this file is too large to render. See raw diff
 
gguf_metal/quant_types.h ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ // Generated by trim_shader.py from llama.cpp @ 432d7ffe2c3b -- do not edit.
3
+
4
+ namespace gguf_quant_types {
5
+
6
+ inline constexpr const char *built[] = {"iq1_m", "iq1_s", "iq2_s", "iq2_xs", "iq2_xxs", "iq3_s", "iq3_xxs", "iq4_nl", "iq4_xs", "mxfp4", "q2_K", "q3_K", "q4_0", "q4_1", "q4_K", "q5_0", "q5_1", "q5_K", "q6_K", "q8_0"};
7
+
8
+ } // namespace gguf_quant_types
trim_shader.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Cut ggml's Metal shader down to the kernels this package can actually dispatch, and emit the
2
+ table `ggml_dispatch.mm` uses to decide what it supports.
3
+
4
+ ggml's Metal backend is one translation unit: `vendor/src/ggml-metal/ggml-metal.metal` exports 603
5
+ kernels, of which we name 25. There is no upstream target for a subset, but the bulk of the file is
6
+ templates plus a block of `template [[host_name(...)]]` instantiations -- and an uninstantiated
7
+ template emits no code. So dropping the instantiations we cannot name is enough: 6.84 MB -> 0.64 MB
8
+ of metallib, with the same 52 tests passing.
9
+
10
+ `vendor/` stays a byte-for-byte copy of upstream; the trimmed shader is generated beside the backend
11
+ source and committed, because a hub `kernels` build compiles the `src` list in build.toml and has
12
+ nowhere to run a pre-step. Both outputs are generated -- never hand-edit them, re-run this instead
13
+ (`vendor.py` does, after every pin bump).
14
+
15
+ The dispatcher names kernels as `kernel_flash_attn_ext[_vec]_f32_dk<K>_dv<V>`, so the f16, bf16 and
16
+ five quantized-KV families upstream also instantiates are unreachable here. The dk/dv pairs that do
17
+ exist are parsed out rather than hand-copied, which is what keeps `supports_flash_attn` from
18
+ claiming a shape whose pipeline was never built.
19
+
20
+ Usage: python trim_shader.py [--no-trim]
21
+ """
22
+
23
+ import argparse
24
+ import os
25
+ import re
26
+
27
+ HERE = os.path.dirname(os.path.abspath(__file__))
28
+ SHADER_IN = os.path.join(HERE, "vendor", "src", "ggml-metal", "ggml-metal.metal")
29
+ SHADER_OUT = os.path.join(HERE, "gguf_metal", "ggml-metal-quant.metal")
30
+ HEADER_OUT = os.path.join(HERE, "gguf_metal", "quant_types.h")
31
+
32
+ # What `ggml_dispatch.mm` builds with snprintf. Everything else upstream exports is dropped.
33
+ # The three kernels `ggml_dispatch.mm` names per quantization type, and the types its `type_table()`
34
+ # carries -- parsed from the dispatch rather than repeated here, so the two cannot drift.
35
+ def _dispatched_types():
36
+ source = open(os.path.join(HERE, "gguf_metal", "ggml_dispatch.mm")).read()
37
+ table = source[source.index("const std::unordered_map<int, TypeInfo> &type_table()") :]
38
+ return re.findall(r'\{GGML_\w+, \{"(\w+)"', table[: table.index("\n}")])
39
+
40
+
41
+ TYPES = _dispatched_types()
42
+ DISPATCHABLE = re.compile(
43
+ r"^kernel_(?:mul_mv|mul_mm)_(" + "|".join(TYPES) + r")_f32$|^kernel_get_rows_(" + "|".join(TYPES) + r")$"
44
+ )
45
+ GENERATED_BY = "trim_shader.py"
46
+
47
+
48
+ # Kernels declared plainly, with no `host_name` to instantiate them -- rwkv, ssm, pooling, upscale,
49
+ # the adamw steps, the quantized mul_mv family. Every `kernel void` is an exported entry point, so
50
+ # they survive on their own; nothing can call them from here, since a kernel cannot call a kernel.
51
+ #
52
+ # A templated one is a different thing with the same spelling: it emits nothing until instantiated,
53
+ # and its body is what the `host_name` lines above refer to. Dropping those leaves the `template<>`
54
+ # prefix dangling on whatever follows, so they are left alone and cost nothing.
55
+ PLAIN_KERNEL = re.compile(r"^kernel void (\w+)\(")
56
+
57
+
58
+ def _referenced_names(lines):
59
+ """Kernel names other lines instantiate or alias: templates the file still needs, not leaf kernels."""
60
+ referring = "".join(l for l in lines if "decltype(" in l or "host_name" in l)
61
+ return set(re.findall(r"\b(kernel_\w+)\b", referring))
62
+
63
+
64
+ def _declaration_start(out, name):
65
+ """Rewind over the `template<...>` header and comments that belong to the definition being cut.
66
+
67
+ A dropped definition whose header stays behind reparents that header onto whatever follows, so
68
+ the header has to go with it -- as do the comment lines written for it.
69
+ """
70
+ while out:
71
+ last = out[-1].strip()
72
+ if last.startswith("//") or not last:
73
+ out.pop()
74
+ continue
75
+ # A `template<...>` header belongs to the definition being cut. A `template [[host_name(..)]] ... ;`
76
+ # instantiation does not -- it is a whole declaration of its own, and eating it silently drops a
77
+ # kernel that was meant to be kept.
78
+ if last.startswith("template") and "host_name" not in last and not last.endswith(";"):
79
+ out.pop()
80
+ continue
81
+ break
82
+ return out
83
+
84
+
85
+ def _skip_statement(lines, i):
86
+ """Advance past the statement starting at `i`: to its `;`, or past the body if it has one."""
87
+ depth, in_body = 0, False
88
+ while i < len(lines):
89
+ depth += lines[i].count("{") - lines[i].count("}")
90
+ in_body = in_body or "{" in lines[i]
91
+ ends = (in_body and depth <= 0) or (not in_body and lines[i].rstrip().endswith(";"))
92
+ i += 1
93
+ if ends:
94
+ return i
95
+ return i
96
+
97
+
98
+ def trim(lines):
99
+ """Drop each exported kernel we cannot name, statement by statement.
100
+
101
+ Four shapes export a kernel: a one-line template instantiation; `template [[host_name(..)]]`
102
+ with the instantiation on following lines; `[[host_name(..)]]` on a full kernel definition; and
103
+ a plain `kernel void name(...)` definition with no attribute at all.
104
+ """
105
+ referenced = _referenced_names(lines)
106
+ kept_names, out, i = [], [], 0
107
+ while i < len(lines):
108
+ plain = PLAIN_KERNEL.match(lines[i])
109
+ if plain and not any(k in plain.group(1) for k in ("mul_mv", "mul_mm", "get_rows")) and plain.group(1) not in referenced:
110
+ _declaration_start(out, plain.group(1))
111
+ i = _skip_statement(lines, i)
112
+ # `typedef decltype(name<...>) name_t;` exists only to name the thing just dropped.
113
+ while i < len(lines) and f"decltype({plain.group(1)}" in lines[i]:
114
+ i += 1
115
+ continue
116
+ found = re.search(r'host_name\("([^"]+)"', lines[i])
117
+ if found and not DISPATCHABLE.match(found.group(1)):
118
+ if lines[i].rstrip().endswith(";"):
119
+ i += 1
120
+ continue
121
+ i = _skip_statement(lines, i)
122
+ continue
123
+ if found:
124
+ kept_names.append(found.group(1))
125
+ out.append(lines[i])
126
+ i += 1
127
+ return out, kept_names
128
+
129
+
130
+ def header(names, revision):
131
+ """The quantization types this build carries kernels for, parsed from the shader."""
132
+ types = sorted({m.group(1) or m.group(2) for name in names if (m := DISPATCHABLE.match(name))})
133
+ entries = ", ".join(f'"{t}"' for t in types)
134
+ return f"""\
135
+ #pragma once
136
+ // Generated by {GENERATED_BY} from llama.cpp @ {revision} -- do not edit.
137
+
138
+ namespace gguf_quant_types {{
139
+
140
+ inline constexpr const char *built[] = {{{entries}}};
141
+
142
+ }} // namespace gguf_quant_types
143
+ """
144
+
145
+
146
+ def main():
147
+ ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
148
+ ap.add_argument(
149
+ "--no-trim",
150
+ action="store_true",
151
+ help="copy the shader verbatim instead of cutting it. The trim is an optimisation, so it must "
152
+ "never be what blocks a pin bump: if upstream reshapes its declarations and the cut stops "
153
+ "compiling, ship the whole 6.84 MB shader with this and fix the cut afterwards.",
154
+ )
155
+ args = ap.parse_args()
156
+
157
+ with open(SHADER_IN) as f:
158
+ lines = f.readlines()
159
+ revision = "unknown"
160
+ upstream = os.path.join(HERE, "vendor", "UPSTREAM")
161
+ if os.path.exists(upstream):
162
+ with open(upstream) as f:
163
+ revision = f.read().split("\n")[1][:12]
164
+
165
+ out, kept = (lines, [m.group(1) for l in lines
166
+ for m in [re.search(r'host_name\("([^"]+)"', l)]
167
+ if m and DISPATCHABLE.match(m.group(1))]) if args.no_trim else trim(lines)
168
+
169
+ # The dispatcher builds names for both paths and calls three helpers by name; a keep-list that
170
+ # lost any of them would build clean and fail at runtime, so refuse to emit it.
171
+ # Every type the dispatch can ask for must have all three of its kernels, or a model using that
172
+ # quantization fails at its first forward instead of at build time.
173
+ # Against the emitted shader, not the instantiation list: some of these are plain definitions rather
174
+ # than `host_name` instantiations, so they never appear in `kept`.
175
+ body = "".join(out)
176
+ missing = [f"{prefix}{t}{suffix}"
177
+ for t in TYPES
178
+ for prefix, suffix in (("kernel_mul_mv_", "_f32"), ("kernel_mul_mm_", "_f32"),
179
+ ("kernel_get_rows_", ""))
180
+ if f"{prefix}{t}{suffix}" not in body]
181
+ if missing:
182
+ raise SystemExit(f"refusing to write: kept {len(kept)} kernels, missing {len(missing)}: "
183
+ f"{missing[:4]}. Upstream's shader has changed shape -- rerun with --no-trim.")
184
+ with open(SHADER_OUT, "w") as f:
185
+ f.write(f"// Generated by {GENERATED_BY} from vendor/src/ggml-metal/ggml-metal.metal "
186
+ f"(llama.cpp @ {revision}) -- do not edit.\n")
187
+ f.writelines(out)
188
+ with open(HEADER_OUT, "w") as f:
189
+ f.write(header(kept, revision))
190
+
191
+ dropped = sum("host_name" in line for line in lines) - len(kept)
192
+ print(f"kept {len(kept)} kernels over {len(TYPES)} quantization types, dropped {dropped}")
193
+ print(f" {SHADER_OUT}")
194
+ print(f" {HEADER_OUT}")
195
+
196
+
197
+ if __name__ == "__main__":
198
+ main()