gguf-gemv

GEMV directly on packed GGUF blocks, aarch64-first. Weight rows stream as raw block bytes; each block decodes to f32 in a stack buffer and accumulates against the activations, so no dequantized weight tensor is ever materialized. This runs GGUF-quantized models inside transformers on CPU without the loader expanding a 4-bit checkpoint to fp first. Companions on the Hub: phanerozoic/bitnet-cpu, phanerozoic/quant-matmul (linears), phanerozoic/cpu-attn (int8 KV attention), phanerozoic/decode-ops (fused glue).

Supported ggml block types: Q4_0, Q8_0, Q4_K, Q5_K, Q6_K (ids 2, 8, 12, 13, 14), the mainline ternary TQ2_0 (35), and the BitNet.cpp ternary I2_S (36). The first six cover the common K-quant files (Q4_K_M, Q5_K_M) and reproduce the ggml / gguf-py reference dequantization bit for bit (max abs difference 0 on aarch64 and x86-64). I2_S follows the BitNet.cpp quantize_i2_s / vec_dot_i2_i8_s layout (128-element blocks, one f32 tensor-wide scale, codes {0,1,2} -> {-1,0,+1}), verified bit-exact against that spec and against the packed tensors of microsoft/bitnet-embedding-0.6b. This makes BitNet ternary GGUFs, the embedding and generative models distributed for BitNet.cpp, loadable through the Python stack.

Usage

import torch
from kernels import get_kernel

gg = get_kernel("phanerozoic/gguf-gemv", version=1, trust_remote_code=True)

tensors = gg.load_gguf("model.gguf")       # quantized 2D -> GGUFLinear
layer = tensors["blk.0.ffn_up.weight"]     # GGUFLinear
y = layer(x)                               # [..., N]

load_gguf reads tensor data through the gguf package's copy-on-write mmap, so weight bytes stay file-backed and the kernel reads them out of the page cache. version selects the release branch; trust_remote_code is required by kernels for publishers without the trusted-publisher mark.

API

Function Purpose
SUPPORTED_TYPES ggml type ids handled (Q4_0, Q8_0, Q4_K, Q5_K, Q6_K, TQ2_0, I2_S)
gguf_gemv(x, data, qtype, K) [..., K] activations x packed rows -> [..., N]
dequant(data, qtype, K) reference f32 dequantization [N, K]
GGUFLinear(data, qtype, K, N) nn.Module wrapper
load_gguf(path) tensor name -> GGUFLinear (quantized) or Tensor (f32/f16)

Performance

Raspberry Pi 5 (4x Cortex-A76 2.4 GHz) and Raspberry Pi 4 Model B (4x Cortex-A72 1.8 GHz), 64-bit Raspberry Pi OS, torch 2.13 CPU:

type, shape (M, N, K) Pi 5 Pi 4
Q4_K 1, 4096, 4096 2.94 ms 9.40 ms
Q4_0 1, 4096, 4096 3.62 ms 10.48 ms
Q8_0 1, 4096, 4096 3.35 ms 9.64 ms
Q6_K 1, 4096, 4096 8.12 ms 18.73 ms
Q4_K 1, 11008, 4096 7.66 ms 24.85 ms
Q4_K 8, 4096, 4096 9.53 ms 37.19 ms

Decode (M=1) is memory-bound at the block-byte stream; Q6_K is slower for its heavier per-block bit assembly. Correctness is decode exactness against gguf-py; speed is a decode-on-the-fly GEMV, not a repacked-format kernel.

Requirements

  • K a multiple of the block size (32 for Q4_0/Q8_0, 256 for the K-quants and TQ2_0, 128 for I2_S).
  • f32 or bf16 activations; output follows the input dtype.
  • load_gguf reads every type from the raw file, including I2_S, which gguf-py's own reader rejects; it needs no external package.
  • The block decode is scalar on both aarch64 and x86-64 (the f32 accumulate vectorizes); correct and bit-exact, not micro-tuned.

Scope

The zoo path: the GGUF catalogue is the largest quantized-model library in existence, and this kernel makes those files loadable for transformers-on-CPU without conversion, now including the BitNet ternary formats (TQ2_0 and the BitNet.cpp I2_S) that carry the BitNet embedding and generative models. A dedicated llama.cpp or BitNet.cpp runtime remains faster; this is for models composed in Python. A future version may add integer-domain block GEMV for the ternary types.

License

Apache-2.0.

Downloads last month
-
apache-2.0
OS
linux
Arch
x86_64aarch64
Kernel Builder
19aaa64