Repository navigation
Step 25: Row-interleaved weight repacking — contiguous multi-row block layout #52
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestphase-3Phase 3: CPU performancePhase 3: CPU performance
Description
Roadmap Step
Step 25 — Row-interleaved weight repacking
Dependencies
- Depends on Step 14 (Q4_K_M dequantization — quantized weight format support)
Scope
At model load time, repack quantized weight matrices from row-major into row-interleaved format so SIMD lanes processing multiple output rows access contiguous memory instead of striding across rows.
- Row-interleaved format — For
Q8_0_R8: take 8 consecutive rows' blocks and pack them contiguously. Blockbof rowrin the original layout at offsetr * rowBytes + b * blockBytesmaps to interleaved offset(b * 8 + r%8) * blockBytes + (r/8) * 8 * rowBytes. SIMD loads 8 blocks from 8 rows in one contiguous read. - Format variants —
Q8_0_R4(4-row interleave, AVX2),Q8_0_R8(8-row, AVX-512). Similarly for Q4_K, Q5_K, Q6_K, Q5_0 formats. - Repacking at load time — After weight tensor metadata is resolved from GGUF, allocate a new
NativeMemory.AlignedAlloc(64)buffer and repack. Original mmap'd data still available for fallback. Repacking cost amortized over inference. TransformerWeightsintegration — Add repacked weight pointer field alongside original weight pointer. Kernels check for repacked pointer first.- M dimension alignment — Output dimension (M) must be padded to interleave factor (4 or 8). Handle tail rows with original non-interleaved kernel.
- Memory overhead — Repacked buffer is same size as original weights (just reordered). Total model memory ~2× during repacking, then original mmap can be released if not shared.
Key Files
src/DotLLM.Cpu/Kernels/WeightRepacking.cs(new — repacking logic per quant format)src/DotLLM.Models/Architectures/TransformerWeights.cs(add repacked weight pointers, trigger repacking after load)src/DotLLM.Models/Architectures/TransformerModel.cs(use repacked weights in Gemm/Gemv dispatch)src/DotLLM.Cpu/Kernels/MatMul.cs(add repacked-format GEMV/GEMM entry points)
Tests
tests/DotLLM.Tests.Unit/Cpu/Kernels/WeightRepackingTests.cs- Test: repack then unpack roundtrip produces identical bytes
- Test: Q8_0_R4 GEMV produces identical output to standard Q8_0 GEMV
- Test: Q8_0_R8 GEMV produces identical output to standard Q8_0 GEMV
- Test: tail rows (M not divisible by interleave factor) handled correctly
- Test: repacked buffer alignment is 64-byte
References
docs/QUANTIZATION.md— block layouts per quant type- llamafile/tinyBLAS — row-interleaved weight format for SIMD matmul
- ik_llama.cpp — repacked weight formats for outer-product kernels
Acceptance Criteria
-
WeightRepackingclass implements repack/unpack for Q8_0, Q5_0, Q4_K, Q5_K, Q6_K - Repacked weights allocated via
NativeMemory.AlignedAllocwith 64-byte alignment -
TransformerWeightsstores repacked pointers alongside originals - Repacking triggered at model load time (one-time cost)
- Tail row handling for M not divisible by interleave factor
- GEMV/GEMM with repacked weights produces bit-exact output vs. original layout
- Unit tests for roundtrip correctness and numerical equivalence
- Memory cleanup: repacked buffers freed on model dispose
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestphase-3Phase 3: CPU performancePhase 3: CPU performance
Activity
Step 25: Row-interleaved weight repacking — R4 contiguous multi-row b…
Fix R4 interleaved dispatch: parallel threading + small-model thresho…
Add explicit FMA and zero-byte allocation guard in Q8_0 kernels (#52)
Step 25: Row-interleaved weight repacking — contiguous multi-row bloc…
jamesburton commentedon Jun 16, 2026
Q4_K MMVQ decode path — implemented, validated, benchmarked
Done (branch
issue/52-kquant-mmvq, commits00b41abshader +b427abdkernel/wiring/test):matmul_q4_k_mmvq.comp— per-32-sub-block dp4a (dotPacked4x8AccSatEXT) over the raw 4-bit nibbles × Q8_1 activations, with the asymmetric min term− dmin·mn·sfolded in via the Q8_1 block-sum.MatMulQ4KMmvqKernel+RecordMatmulwiring (Q4_K + seqLen==1 → quantize Q8_1 + integer-dot GEMV; gated onHasIntegerDotProduct,DOTLLM_VULKAN_DISABLE_MMVQopt-out, F32-in fallback). Reuses the Q8_0 MMVQ activation scratch.VulkanMatMulQ4KMmvqKernelTests— argmax + tolerance vs the Q4_K-byte-identical CPU F32-in oracle, 7 shapes.Validation: Q4_K MMVQ 7/7, Q8_0 MMVQ + Q4_K GEMV regression 14/14, real-model Q4_K forward 3/3 (now routing through MMVQ).
Benchmark — Strix Halo (Radeon 8060S, gfx1151), Devstral-Small-2-24B Q4_K_M (dense), Release, clean A/B (same model/hardware):
+30% decode throughput (1.30×). Prefill unchanged (~1880 ms) — decode-only, as designed (Q4_K prefill still uses the F32 GEMM). The win is the integer-dot path removing the per-element float dequant+madd.
Follow-ups: Q4_K MMQ prefill (the seqLen>1 analogue); extend MMVQ to Q5_K/Q6_K/Q2_K/Q3_K (same nibble/sub-block structure).
jamesburton commentedon Jun 16, 2026
Fresh 7B/8B-class A/B (this build)
Re-ran on a freshly-downloaded dense model to anchor against the prior ~8.5 tok/s Mistral-7B baseline. Strix Halo (Radeon 8060S, gfx1151), Meta-Llama-3.1-8B-Instruct Q4_K_M (dense), Release, 64 decode steps:
+26.5% decode (1.27×); non-overlapping spreads (on 94.8–104.3 ms, off 119.8–135.0 ms). The MMVQ-off baseline (7.83) matches the prior pre-MMVQ Mistral-7B figure (~8.5); MMVQ-on clears it at 9.91 on a larger 8B.
Win is consistent across model sizes: +26.5% @ 8B, +30% @ 24B — the integer-dot path generalizes.