GGUF kernels: dequantize + fused gemv over packed blocks
Browse files- .pytest_cache/v/cache/lastfailed +3 -1
- README.md +5 -0
- SKILL.md +26 -6
- flake.nix +1 -1
.pytest_cache/v/cache/lastfailed
CHANGED
|
@@ -1 +1,3 @@
|
|
| 1 |
-
{
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tests/test_gguf_kernels.py": true
|
| 3 |
+
}
|
README.md
CHANGED
|
@@ -60,3 +60,8 @@ nix run .#build-and-copy -L # every variant, into build/
|
|
| 60 |
nix build .#ci # just this system's variant
|
| 61 |
pytest tests/ # against a built variant on PYTHONPATH
|
| 62 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
nix build .#ci # just this system's variant
|
| 61 |
pytest tests/ # against a built variant on PYTHONPATH
|
| 62 |
```
|
| 63 |
+
|
| 64 |
+
`flake.nix` tracks the builder's default branch, so CI builds the current variant matrix. The
|
| 65 |
+
`build/` directory here was produced against the builder release matching the `kernels` client it was
|
| 66 |
+
verified with (torch 2.11/2.12): both ops tested against `gguf.quants.dequantize` for Q4_K/Q5_K/Q6_K/
|
| 67 |
+
Q8_0 in f32/f16/bf16, the gemv against a dense matmul, and one `fullgraph=True` compile.
|
SKILL.md
CHANGED
|
@@ -57,8 +57,9 @@ dispatch on the tensor's device as usual.
|
|
| 57 |
## 3. Which builder, and why it decides everything
|
| 58 |
|
| 59 |
Use `github:huggingface/kernels` (the old `kernel-builder` repo is stale) with
|
| 60 |
-
`lib.genKernelFlakeOutputs`.
|
| 61 |
-
|
|
|
|
| 62 |
|
| 63 |
| | |
|
| 64 |
| --- | --- |
|
|
@@ -66,7 +67,8 @@ because the builder version decides two things at once:
|
|
| 66 |
| the `metadata.json` format | older builders omit `name`, and a current client refuses to load that |
|
| 67 |
|
| 68 |
A mismatch surfaces as `Cannot parse metadata from ...: missing field 'name'`, or as a build with no
|
| 69 |
-
variant your interpreter can import
|
|
|
|
| 70 |
|
| 71 |
```bash
|
| 72 |
ls build/ && head -6 build/*/metadata.json
|
|
@@ -137,9 +139,13 @@ reproducible. Then, in increasing order of surprise:
|
|
| 137 |
2. **Trust.** `get_kernel` loads unattended only from a publisher the Hub marks as a trusted kernel
|
| 138 |
publisher (e.g. `kernels-community`). A personal repo needs `trust_remote_code=True`, i.e. the
|
| 139 |
caller opting into running downloaded code — so a library should not pass it silently.
|
| 140 |
-
3. **
|
| 141 |
-
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
Test without the Hub at all:
|
| 145 |
|
|
@@ -160,3 +166,17 @@ _MY_OP = module.my_op # NOT self.kernel.my_op
|
|
| 160 |
Reaching an op through an attribute on an object makes dynamo graph-break at every call, which costs
|
| 161 |
more than the kernel saves — the same reason the fakes matter. Treat "no kernel" as a normal outcome:
|
| 162 |
return `None`, log why, and fall back to the dense path.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
## 3. Which builder, and why it decides everything
|
| 58 |
|
| 59 |
Use `github:huggingface/kernels` (the old `kernel-builder` repo is stale) with
|
| 60 |
+
`lib.genKernelFlakeOutputs`. Leave it unpinned for a repo whose variants CI builds — that keeps the
|
| 61 |
+
matrix current. Pin it to the `kernels` client version only when *you* build the artifacts you will
|
| 62 |
+
load locally, because the builder version decides two things at once:
|
| 63 |
|
| 64 |
| | |
|
| 65 |
| --- | --- |
|
|
|
|
| 67 |
| the `metadata.json` format | older builders omit `name`, and a current client refuses to load that |
|
| 68 |
|
| 69 |
A mismatch surfaces as `Cannot parse metadata from ...: missing field 'name'`, or as a build with no
|
| 70 |
+
variant your interpreter can import — which is what unpinned costs locally: `main` targets torch
|
| 71 |
+
2.12/2.13, so testing a local build needs a matching interpreter. Check what came out before debugging anything else:
|
| 72 |
|
| 73 |
```bash
|
| 74 |
ls build/ && head -6 build/*/metadata.json
|
|
|
|
| 139 |
2. **Trust.** `get_kernel` loads unattended only from a publisher the Hub marks as a trusted kernel
|
| 140 |
publisher (e.g. `kernels-community`). A personal repo needs `trust_remote_code=True`, i.e. the
|
| 141 |
caller opting into running downloaded code — so a library should not pass it silently.
|
| 142 |
+
3. **The repo has to be under an organization.** The client resolves through
|
| 143 |
+
`https://huggingface.co/api/kernels/<repo>/...`, which 401s (404s with a token) for a repo in a
|
| 144 |
+
*user* namespace — no tag, metadata or `kernel` tag changes that. The trust check gives the same
|
| 145 |
+
hint: it calls `get_organization_overview(publisher)`, which a username has no answer for. So a
|
| 146 |
+
correct, fully published repo can still be unfetchable by id. Compare against a
|
| 147 |
+
`kernels-community` repo (`get_kernel("kernels-community/relu", version=1)`) before suspecting
|
| 148 |
+
your own build, and use the override below until the kernels live in an org.
|
| 149 |
|
| 150 |
Test without the Hub at all:
|
| 151 |
|
|
|
|
| 166 |
Reaching an op through an attribute on an object makes dynamo graph-break at every call, which costs
|
| 167 |
more than the kernel saves — the same reason the fakes matter. Treat "no kernel" as a normal outcome:
|
| 168 |
return `None`, log why, and fall back to the dense path.
|
| 169 |
+
|
| 170 |
+
## 8. Sanity numbers
|
| 171 |
+
|
| 172 |
+
From this build, so you know what "working" looks like:
|
| 173 |
+
|
| 174 |
+
| | |
|
| 175 |
+
| --- | --- |
|
| 176 |
+
| compile, one variant | ~150 s (5 translation units, 5 SM archs) |
|
| 177 |
+
| all variants | ~8 min for 6 |
|
| 178 |
+
| the whole exercise | dominated by rebuilds after each build.toml/builder discovery, not by compiling |
|
| 179 |
+
|
| 180 |
+
End state worth reproducing: `pytest tests/` green against a built variant, `fullgraph=True` compile
|
| 181 |
+
green, and the calling library loading the same artifact through `get_kernel` — for GGUF that was a
|
| 182 |
+
4B Q4_K_M model at 3.07 GB and 187 tok/s instead of 8.4 GB dequantized.
|
flake.nix
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
description = "GGUF kernels: compute on the packed blocks of a GGUF checkpoint";
|
| 3 |
inputs = {
|
| 4 |
-
kernel-builder.url = "github:huggingface/kernels
|
| 5 |
};
|
| 6 |
outputs = { self, kernel-builder }:
|
| 7 |
kernel-builder.lib.genKernelFlakeOutputs {
|
|
|
|
| 1 |
{
|
| 2 |
description = "GGUF kernels: compute on the packed blocks of a GGUF checkpoint";
|
| 3 |
inputs = {
|
| 4 |
+
kernel-builder.url = "github:huggingface/kernels";
|
| 5 |
};
|
| 6 |
outputs = { self, kernel-builder }:
|
| 7 |
kernel-builder.lib.genKernelFlakeOutputs {
|