ik_llama.cpp 行交错量化解析:IQ3_S_R4 如何把 sub-4bpw i-quants 的 CPU 性能提升近一倍
【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp
导读
本文以 ik_llama.cpp 仓库中 PR #162(IQ3_S_R4)为切入点,讲解该项目为解决 sub-4 bpw(bits-per-weight)i-quants 量化类型在 CPU 上矩阵乘性能低下而引入的行交错(row-interleaved)重打包思路:在不改变权重位宽的前提下,通过把IQ3_S的权重按 4 行交错重新布局,换取向量化与缓存友好度的大幅提升。读完本文,你将理解 R4 系量化类型的来历、它在 ggml.h 与 llama-quantize.cpp 中的落地方式、-rtr/--run-time-repack运行时重打包参数的正确用法与限制,以及 PR 162 给出的三平台实测性能数据。
一、背景:为什么 sub-4 bpw 的 i-quants 在 CPU 上"跑不动"
在 ik_llama.cpp 的量化体系中,IQ3_S属于i-quants(improved quants)家族,名义位宽 3.44 bpw(见 quantize.cpp 中的类型描述),它比传统 k-quants 更依赖码本(codebook)查表与非线性编码来压缩权重。
PR #162 的出发点非常直白:"Sub-4 bpw i-quants have a terrible CPU performance"(低于 4 bpw 的 i-quants 在 CPU 上性能很差)。原因从实现结构上可以推断:极低位宽意味着每个 block 中打包了大量细粒度比特信息,解包时指令密度高、内存访问模式碎片化,导致 SIMD 向量化效率低、缓存利用率差。这在 prompt processing(PP,即预填充阶段的大矩阵乘法 GEMM)中体现得尤为明显,因为 PP 是典型的计算密集型负载,SIMD 吞吐直接决定吞吐量。
作者因此产生了一个直觉:如果对权重行做交错(interleaving rows)重排,是否能把性能救回来?于是就有了 PR #162 的核心产物——IQ3_S_R4。
二、什么是 R4:对 IQ3_S 的 4 行交错重打包
IQ3_S_R4不是一种新的压缩算法,而是IQ3_S权重的 4 行交错(4-row interleaved)重打包版本。这一点在仓库中有三处相互印证的证据:
类型定义:在 ggml.h 中,
GGML_TYPE_IQ3_S_R4 = 221与GGML_TYPE_IQ1_S_R4 = 219等并列,属于独立的张量类型枚举;对应文件类型GGML_FTYPE_MOSTLY_IQ3_S_R4 = 220(见 ggml.h)。重打包映射表:在 llama-quantize.cpp 的
interleaved_properties()中,有一个全局映射表记录每个交错类型对应的基础类型与交错行数:
{ GGML_TYPE_IQ3_S_R4, { GGML_TYPE_IQ3_S, 4} }, // 基础类型 IQ3_S,4 行交错 { GGML_TYPE_IQ1_S_R4, { GGML_TYPE_IQ1_S, 4} }, { GGML_TYPE_IQ2_K_R4, { GGML_TYPE_IQ2_K, 4} }, { GGML_TYPE_IQ4_KS_R4, { GGML_TYPE_IQ4_KS, 4} }, ...- 量化工具声明:在 quantize.cpp 中,
IQ3_S_R4被直接描述为"IQ3_S repacked",位宽仍保持 3.44 bpw——即存储成本不变,只是数据布局改变。
其原理可以概括为:把多个相邻行的量化数据按行交错方式重新排列,使得 GEMM 内核在加载权重块时能一次取到更多连续有效数据、更好地利用 SIMD 寄存器与缓存行。类似的 R4 族还包括IQ4_KS_R4、IQ2_K_R4、IQ3_K_R4、IQ4_K_R4、IQ5_K_R4等(详见 README.md 的量化列表),而 PR #162 是这一思路在 sub-4 bpw i-quants 上的首次验证。
三、实测性能:PP-512 与 TG-128 三平台数据(PR #162 原文)
PR #162 给出了在 LLaMA-3.1-8B 上、三大 CPU 平台的完整基准数据。以下数据直接继承自该 PR 的 Description,单位均为 tokens/s(数值格式为均值 ± 标准差)。
3.1 Prompt Processing(PP-512):核心收益场景
| 平台 | 线程数 | IQ3_S | IQ3_S_R4 | 加速比 |
|---|---|---|---|---|
| ARM_NEON(M2-Max) | 8 | 42.97 ± 1.28 | 80.61 ± 0.41 | 1.876 |
| Zen4(Ryzen-7950X) | 16 | 104.66 ± 0.68 | 159.08 ± 0.57 | 1.520 |
| AVX2(Ryzen-5975WX) | 32 | 132.50 ± 0.37 | 231.41 ± 0.45 | 1.746 |
PP 阶段收益显著:ARM_NEON 接近 1.9 倍,AVX2 约 1.75 倍,即使本身吞吐已很高的 Zen4 也有 1.52 倍。这与前面分析的"GEMM 对数据布局敏感"的推断一致——交错布局直接提升了矩阵乘的向量化效率。
3.2 Token Generation(TG-128):多线程扩展下的收益
TG(自回归解码)阶段是 GEMV(矩阵-向量乘)负载,瓶颈更偏向访存带宽,因此收益普遍小于 PP,但依然可观,尤其 AVX2:
| 平台 | 线程数 | IQ3_S | IQ3_S_R4 | 加速比 |
|---|---|---|---|---|
| ARM_NEON | 2 | 3.00 ± 0.00 | 3.40 ± 0.00 | 1.133 |
| ARM_NEON | 4 | 5.74 ± 0.02 | 6.60 ± 0.01 | 1.150 |
| ARM_NEON | 8 | 9.25 ± 0.83 | 12.27 ± 0.33 | 1.326 |
| Zen4 | 2 | 4.17 ± 0.00 | 4.38 ± 0.01 | 1.050 |
| Zen4 | 4 | 7.82 ± 0.05 | 8.14 ± 0.01 | 1.041 |
| Zen4 | 8 | 14.29 ± 0.02 | 14.41 ± 0.02 | 1.008 |
| AVX2 | 2 | 1.98 ± 0.00 | 3.31 ± 0.00 | 1.672 |
| AVX2 | 4 | 3.87 ± 0.00 | 6.49 ± 0.00 | 1.677 |
| AVX2 | 8 | 7.13 ± 0.01 | 11.63 ± 0.02 | 1.631 |
| AVX2 | 16 | 12.97 ± 0.00 | 15.81 ± 0.00 | 1.219 |
规律清晰:AVX2 平台受益最大(TG 加速 1.2~1.7 倍),ARM_NEON 次之,Zen4 在 TG 阶段几乎无增益(约 1.0 倍),这是因为 Zen4 的 AVX-512 原生吞吐已足够高,GEMV 的访存瓶颈盖过了布局优化收益。这也提醒使用者:R4 收益与平台、负载类型强相关,PP 是它的主战场。
四、仓库落地:从类型定义到运行时重打包
IQ3_S_R4在仓库中并非孤立实验,而是完整的工程链路:
4.1 张量类型与文件类型
- 张量类型
GGML_TYPE_IQ3_S_R4与文件类型GGML_FTYPE_MOSTLY_IQ3_S_R4定义于 ggml.h。 - GEMM 内核在 iqk_gemm_iquants.cpp 与 iqk_gemm_iquants.cpp 中为该类型提供专门的矩阵乘分支;相关量化/解包逻辑分布在 ggml-quants.c 及 iqk 目录 中。
- 模型加载时,llama-model-loader.cpp 与 llama-model.cpp 均注册了该类型的处理路径。
4.2 两种使用方式
方式一:直接量化/重打包。使用llama-quantize工具把模型输出为IQ3_S_R4类型(或对已有 IQ3_S 模型做纯重打包):
# 直接从 f16 模型量化为 IQ3_S_R4(3.44 bpw) llama-quantize /models/model-f16.gguf /models/model-iq3-s-r4.gguf IQ3_S_R4 # 或先量化为 IQ3_S,再重打包 llama-quantize /models/model-f16.gguf /models/model-iq3-s.gguf IQ3_S llama-quantize /models/model-iq3-s.gguf /models/model-iq3-s-r4.gguf IQ3_S_R4llama-quantize的完整类型清单见 quantize.cpp,其中IQ3_S_R4描述为"IQ3_S repacked"。
方式二:运行时重打包(-rtr)。无需重新生成模型文件,在推理时通过-rtr, --run-time-repack参数让加载器把支持的量化类型自动重打包为行交错格式(见 parameters.md):
llama-cli -m /models/model.gguf -rtr注意:-rtr是 ik_llama.cpp独有参数(见 parameters.md 中 "Unique parameters" 一节),并非 llama.cpp 上游能力。
4.3 重要限制:混合 CPU/GPU 推理时慎用-rtr
README.md 给出了明确警告:如果 MoE 模型采用混合 CPU/GPU 推理且部分专家留在 CPU 上,不要使用-rtr,除非你清楚自己在做什么。原因有二:
-rtr会把留在 RAM 中的所有张量在加载时重打包为行交错格式;- 并非所有量化类型都有行交错的 CUDA 实现(典型如
Q2_K、Q3_K、Q4_K、Q5_K、Q6_K等 k-quants 缺少 CUDA 行交错内核)。
一旦张量被重打包而 GPU 侧没有对应内核,这些张量的矩阵乘将永远只能在 CPU 上执行——即使原本可以更优地卸载到 GPU,最终反而导致 prompt processing 变慢。换句话说:-rtr适合纯 CPU 推理或张量确实驻留 CPU 的场景,在异构卸载场景下必须谨慎评估。
五、后续演进:R4 思路与"超快 CPU PP"的汇合
PR #162 于 2024-12-23 提交后被关闭(State: Closed),但行交错思路在 ik_llama.cpp 中持续生长:README 中记录了后续大量同族实现,例如IQ4_KS_R4(PR 150)、IQ2_K_R4(PR 146)、IQ3_K_R4(PR 145)、IQ4_K_R4(PR 138)、IQ4_KSS(PR 89)等,CUDA 端也陆续补齐了IQ1_S_R4(PR 492)、IQ1_M_R4(PR 494)、IQ4_KS_R4 / IQ5_KS_R4(PR 493)等内核。
更重要的是,PR #515 / #531 提出了"对所有非交错量化类型大幅加速 CPU prompt processing"的新方案,并经由后续多个 PR 推广到全部量化类型与三个受支持的 CPU 平台(Zen4 / AVX2 / NEON,见 README.md)。从代码结构看,可以推断:R4 系交错布局与"非交错类型本身提速"两条路线在仓库中是并行演进的——前者通过布局换取收益,后者则直接优化解包与向量化路径;两者最终都指向同一个目标:让低比特量化在 CPU 上真正可用。
六、小结:何时选择 IQ3_S_R4
结合 PR #162 的实测数据与仓库实现,可给出如下实用判断:
- 优先使用场景:纯 CPU 推理、以 prompt processing 为主的工作负载(长上下文预填充、批量推理),此时
IQ3_S_R4相比IQ3_S可获 1.5~1.9 倍 PP 加速,而位宽不变(仍为 3.44 bpw),是"零精度代价换性能"的布局优化; - 收益有限场景:Zen4 平台的 TG 阶段收益接近 1.0 倍,若你的负载以长流式生成为主,收益将不明显;
- 慎用场景:混合 CPU/GPU 卸载推理——除非确认相关张量没有 GPU 行交错内核的兼容性问题,否则避免
-rtr自动重打包; - 工程入口:类型定义见 ggml.h,重打包映射见 llama-quantize.cpp,量化命令见 quantize.cpp,运行时参数见 parameters.md。
一句话总结:IQ3_S_R4 证明了"不改精度、只改布局"也能在 CPU 上释放近一倍的算力,它是理解 ik_llama.cpp 整个 R4 量化家族与-rtr运行时重打包机制的绝佳起点。
【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考