Pantheon/docs/architecture/gguf.md

6.0 KiB

GGUF Format Support

Pantheon's GGUF parser (src/formats/gguf.c) provides zero-copy model loading via mmap. Both GGUF v2 and v3 are supported.

File Layout

┌──────────────────────────┐
│  Magic: "GGUF" (4 bytes) │
│  Version: uint32         │  v2 or v3
│  Tensor count: uint64    │
│  Metadata count: uint64  │
├──────────────────────────┤
│  Metadata entries         │  typed key-value pairs
│  (variable length)        │
├──────────────────────────┤
│  Tensor info entries      │  name, dims, quant type, offset
│  (variable length)        │
├──────────────────────────┤
│  Padding                  │  aligned to 32 bytes (GGUF_DEFAULT_ALIGNMENT)
├──────────────────────────┤
│  Tensor data (binary)     │  mmap'd, accessed via offset
└──────────────────────────┘

Alignment

The tensor data section starts at a 32-byte aligned offset from the file start. This is the GGUF spec default (GGUF_DEFAULT_ALIGNMENT = 32). Files can override this via the general.alignment metadata key.

v2 vs v3

Both versions use the same binary layout. The parser accepts either. v3 added big-endian support (Pantheon assumes little-endian). The version field is stored in pn_gguf_file.version.

Metadata Types

13 value types supported:

Type ID Size
UINT8 0 1
INT8 1 1
UINT16 2 2
INT16 3 2
UINT32 4 4
INT32 5 4
FLOAT32 6 4
BOOL 7 1
STRING 8 8 + len
ARRAY 9 variable
UINT64 10 8
INT64 11 8
FLOAT64 12 8

Strings are length-prefixed (uint64 length + raw bytes). Arrays contain a type tag + uint64 count + elements.

Quantization Types

Implemented (full dequant + fused matvec)

Type ID Bits/Weight Block Size Bytes/Block Notes
F32 0 32 1 4 Passthrough
F16 1 16 1 2 IEEE 754 half
Q4_0 2 4 32 18 scale + 4-bit nibbles
Q8_0 8 8 32 34 scale + int8 values
Q4_K 12 4.5 256 144 8 sub-blocks, 6-bit scales
Q5_K 13 5.5 256 176 5-bit values (4 low + 1 high)
Q6_K 14 6.5 256 210 6-bit values (4 low + 2 high)

Format-only (defined but no dequant kernel)

Q4_1, Q5_0, Q5_1, Q8_1, Q2_K, Q3_K, Q8_K

Mixed Quantization (Q4_K_M, Q5_K_M)

GGUF files labeled "Q4_K_M" use Q4_K for most layers with Q6_K for attention output and FFN down projections. This is handled transparently — each tensor has its own quant type, and pn_qmv dispatches per-row based on the weight's type field.

Key Metadata Keys

Model Architecture

Key Type Description
general.architecture string "llama", "qwen2", "phi", "mistral"
general.name string Human-readable model name
general.alignment uint32 Data section alignment (default 32)

Model Dimensions

Prefixed by architecture name (llama.*, qwen2.*, etc.):

Key Type Description
{arch}.embedding_length uint32 Hidden size (n_embd)
{arch}.attention.head_count uint32 Query heads
{arch}.attention.head_count_kv uint32 KV heads (for GQA)
{arch}.block_count uint32 Number of layers
{arch}.feed_forward_length uint32 FFN intermediate size
{arch}.context_length uint32 Max context length
{arch}.rope.freq_base float32 RoPE theta (default 10000)
{arch}.attention.layer_norm_rms_epsilon float32 RMSNorm epsilon
{arch}.attention.sliding_window uint32 Sliding window size (Mistral)

Tokenizer

Key Type Description
tokenizer.ggml.model string "gpt2" (BPE) or "llama" (SentencePiece)
tokenizer.ggml.tokens string[] Vocabulary tokens
tokenizer.ggml.merges string[] BPE merge rules (BPE only)
tokenizer.ggml.scores float[] Unigram scores (SentencePiece only)
tokenizer.ggml.bos_token_id uint32 BOS token ID
tokenizer.ggml.eos_token_id uint32 EOS token ID

Tensor Naming Convention

GGUF tensors follow the pattern blk.{layer}.{component}.weight:

token_embd.weight                    embedding table
blk.0.attn_norm.weight               pre-attention RMSNorm
blk.0.attn_q.weight                  query projection
blk.0.attn_k.weight                  key projection
blk.0.attn_v.weight                  value projection
blk.0.attn_output.weight             output projection
blk.0.attn_q.bias                    query bias (Qwen2 only)
blk.0.ffn_norm.weight                pre-FFN RMSNorm
blk.0.ffn_gate.weight                SwiGLU gate projection
blk.0.ffn_up.weight                  SwiGLU up projection
blk.0.ffn_down.weight                down projection
output_norm.weight                   final RMSNorm
output.weight                        LM head (may be tied to token_embd)

Quantized Weight Loading

Weights are loaded as pn_qweight structs that point directly into mmap'd data:

typedef struct {
    const void   *data;       /* raw quantized blocks in mmap */
    pn_gguf_quant type;       /* quant format */
    int           rows;       /* output dimension */
    int           cols;       /* input dimension */
    size_t        row_stride; /* bytes per row */
} pn_qweight;

GGUF stores dimensions innermost-first. The loader reverses them to C outermost-first: dims[n_dims-1] = rows, dims[0] = cols. row_stride is computed from cols and the quant format's bytes-per-block.

Matrix-vector multiply (pn_qmv) iterates rows, dequantizing one block (32 or 256 elements) at a time and accumulating the dot product. No full dequantization ever occurs for projection weights.