ik_llama.cpp IQ4_K_R4 量化格式详解:AVX2/Zen4 上的 R4 重排加速与性能提升
2026/9/19 1:30:49 网站建设 项目流程

ik_llama.cpp IQ4_K_R4 量化格式详解:AVX2/Zen4 上的 R4 重排加速与性能提升

【免费下载链接】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 #144「Slightly faster IQ4_K_R4 on AVX2/Zen4」 展开,讲解该仓库独有的R4(4 行重排,row-repacked)量化格式IQ4_K_R4的原理、实现与性能收益。读完本文,你将理解:什么是 R4 重排、IQ4_K_R4相比IQ4_K为何能提升矩阵乘性能、PR #144 在 AVX2/Zen4 上又做了哪些加速,以及如何用llama-quantize生成和实测该格式。

一、背景:从 IQ4_K 到 IQ4_K_R4

IQ4_K是 ik_llama.cpp 在标准 k-quants 基础上引入的「非线性(non-linear)量化」格式族成员,约 4.5 bpw(bits per weight)。在 examples/quantize/quantize.cpp 中可以确认它被描述为" 4.5 bpw non-linear quantization",而IQ4_K_R4的描述是"IQ4_K repacked"——即对IQ4_K数据重新打包后的变体。

为什么要重排(repack)?这要从矩阵乘的计算模式说起。llama.cppmul_mat中,权重量化块(block)通常按「一行一个 block」的布局存储(即block_iq4_k每块覆盖一行中QK_K个权重)。计算时每个 block 需要先**解包(unpack)**才能与激活值做点积。解包 k-quants/i-quants 的位运算代价高昂,因此在解包之后,应尽量让它与右侧矩阵(激活)的多个列相乘,摊薄解包开销;但累积结果必须能留在向量寄存器中,所以参与相乘的列数存在上限。

R4 格式的解法是:把相邻 4 行中同一位置的 4 个 block 打包成一个超级 block,让一次解包同时服务 4 行、且权重量化数据在内存中按更利于 SIMD 加载的顺序排列。从源码看,ggml/src/ggml-common.h 定义了block_iq4_k_r4

typedef struct { ggml_half d[4]; // 4 行各自的 scale(fp16) uint8_t extra[8]; // 4 行各 2 个 extra 标志位(每组是否使用高 16 个值) uint8_t scales_h[QK_K/16]; // 4 行合并后的 scale 高 2 位 uint8_t scales_l[QK_K/8]; // 4 行合并后的 scale 低 4 位 uint8_t qs[QK_K*2]; // 4 行合并后的量化权重 } block_iq4_k_r4; static_assert(sizeof(block_iq4_k_r4) == 4*sizeof(block_iq4_k), "wrong iq4_k_r4 block size/padding");

关键点:sizeof(block_iq4_k_r4) == 4 * sizeof(block_iq4_k),即 R4 格式不增加任何存储开销,纯粹是布局变换;内存占用与原IQ4_K完全一致,只是把 4 行数据交错打包,便于向量化取数和复用解包结果。

二、PR #144 做了什么:AVX2/Zen4 上的进一步提速

PR #138「IQ4_K_R4」 首次实现了IQ4_K_R4,当时的结论是:ARM_NEON 上收益非常显著,而 AVX2/Zen4 上收益较小,作者自评「AVX2/Zen4 的实现可能不是最优」。

PR #144「Slightly faster IQ4_K_R4 on AVX2/Zen4」 正是针对这一短板做的后续优化,核心目标就是专门打磨 AVX2/Zen4 的IQ4_K_R4计算路径。该 PR 由仓库作者 ikawrakow 提交,于 2024-12-16 创建并关闭(Closed,即已合入主干),说明中给出 LLaMA-3.1-8B、PP-512(512 token 的 prompt processing,即预填充阶段)的实测:

平台优化前PR #144 后
Ryzen-7950X(Zen4)232 t/s251 t/s
Ryzen-5975WX(AVX2)227 t/s249 t/s

即 Zen4 提升约8.2%,AVX2 提升约9.7%,把之前「AVX2/Zen4 上 R4 收益小」的短板补上了一大块。结合 PR #138 的基线(Zen4 16 线程 182.2 → 232.63 t/s,AVX2 32 线程 206.43 → 227.60 t/s),整个 R4 化过程在 Zen4 上累计从 182.2 t/s 提升到 251 t/s(约 1.38 倍),AVX2 上从 206.4 t/s 提升到 249 t/s(约 1.21 倍)。

PR #144 之所以效果明显,从仓库的实现结构可以推断:IQ4_K_R4的矩阵乘走的是iqk_mul_mat(iqk 后端),它针对不同 SIMD 架构维护了独立的 kernel 集合(详见下文第三节),AVX2/Zen4 的 kernel 在 PR #144 中得到重写/调优后才追上了 NEON 的水平。

三、源码级原理:iqk 后端如何加速 IQ4_K_R4

3.1 编译开关:GGML_USE_IQK_MULMAT

IQ4_K_R4的加速依赖 iqk 后端。在 ggml/src/iqk/iqk_quantize.cpp 中,vec_dot_iq4_k_r4_q8_k优先尝试 iqk 路径:

void vec_dot_iq4_k_r4_q8_k(int n, float * s, size_t bs, const void * vx, size_t bx, const void * vy, size_t by, int nrc) { #if GGML_USE_IQK_MULMAT if (iqk_mul_mat(1, 1, n, GGML_TYPE_IQ4_K_R4, vx, 0, GGML_TYPE_Q8_K, vy, 0, s, 0, 0, 1)) { return; } #endif ... }

也就是说,编译时开启GGML_USE_IQK_MULMAT后,IQ4_K_R4Q8_K的点积会路由到iqk_mul_mat;若未开启该宏,则回退到普通路径。

3.2 按架构分派 kernel:AVX2/Zen4 与 ARM_NEON 两条路线

iqk_mul_mat的 kernel 分派在 ggml/src/iqk/iqk_mul_mat.cpp 中体现:GGML_TYPE_IQ4_K_R4IQ2_K_R4IQ3_K_R4IQ5_K_R4IQ4_KS_R4IQ5_KS_R4等一同归入iqk_set_kernels_iqk_quants这一组。而该文件以#ifdef __aarch64__为界划分了两大实现分支:x86(AVX2/AVX-512/Zen4)与 ARM(NEON)各有独立的 kernel 选择逻辑,这正是 PR #138 中「NEON 收益大、x86 收益小」、以及 PR #144「专门优化 x86」得以成立的结构基础。

3.3 R4 重排的量化实现

量化侧的核心在 ggml/src/iqk/iqk_quantize.cpp:quantize_iq4_k_r4先把 4 行数据按普通IQ4_K量化,再调用repack_iq4_k重排为 R4 布局:

size_t quantize_iq4_k_r4(const float * src, void * dst, int64_t nrows, int64_t n_per_row, const float * imatrix, [[maybe_unused]] const quantize_user_data * user_data) { GGML_ASSERT(nrows%4 == 0); GGML_ASSERT(n_per_row%QK_K == 0); char * qcur = (char *)dst; auto row_size = ggml_row_size(GGML_TYPE_IQ4_K, n_per_row); std::vector<char> qtmp(4*row_size); for (int row = 0; row < nrows; row += 4) { quantize_iq4_k(src, (void *)qtmp.data(), 4, n_per_row, imatrix, user_data); repack_iq4_k(4, n_per_row, (const block_iq4_k *)qtmp.data(), (block_iq4_k_r4 *)qcur, false); qcur += 4*row_size; src += 4*n_per_row; } return nrows*row_size; }

repack_iq4_k(iqk_quantize.cpp)逐 block 地把 4 行的d(scale)、extrascales_l/scales_h、以及 16 个 4-bit 权重按位交错合并进block_iq4_k_r4qs[QK_K*2]中。反量化函数dequantize_row_iq4_k_r4(iqk_quantize.cpp)则按同样布局读回 4 行浮点值,保证格式可逆、数值等价于普通IQ4_K。由于重排是纯布局变换,IQ4_K_R4IQ4_K的量化精度完全一致,提速不牺牲质量

3.4 行数与列数的权衡:R4 系列的通用加速逻辑

值得说明的是,R4 提速并非IQ4_K_R4独有。从 iqk_mul_mat.cpp 的num_rows()可以看到,IQ2_K_R4IQ3_K_R4IQ4_K_R4IQ5_K_R4等大量 R4 类型都声明为每超级块 4 行(return 4),而IQ4_XS_R8Q8_K_R8等则是 8 行(return 8)、Q8_K_R16是 16 行。行数越多,一次解包可复用的机会越多,但寄存器压力也越大,因此作者针对不同量化格式选择了不同的重排行数。

在 PR #157「R4 i-quants improvements」 中作者进一步解释了这一权衡:解包IQ2_XXSIQ2_XSIQ2_SIQ3_XXS的代价极高,值得把累积结果在「寄存器 ↔ 内存」间搬进搬出,以便让解包结果被复用超过 8 次,因此该 PR 把列数从 8 提升到 16。这也说明 R4 系列的性能调优始终围绕「解包成本 vs 寄存器容量」这一核心矛盾展开,PR #144 正是这条优化路线在 AVX2/Zen4 上的延续。

四、实战:如何量化出 IQ4_K_R4 并验证性能

4.1 用 llama-quantize 生成 IQ4_K_R4

IQ4_K_R4是 GGUF 中注册的标准新类型(参见 gguf-py/gguf/constants.py 中的映射),可直接通过仓库的量化工具生成。用llama-quantize(源码位于 examples/quantize/quantize.cpp):

# 查看支持的类型列表(确认包含 IQ4_K_R4) ./build/bin/llama-quantize --help # 将 fp16 模型量化为 IQ4_K_R4 ./build/bin/llama-quantize ./models/llama-3.1-8b-f16.gguf ./models/llama-3.1-8b-iq4_k_r4.gguf IQ4_K_R4

类型表中IQ4_K_R4对应的 ftype 为LLAMA_FTYPE_MOSTLY_IQ4_K_R4,描述为"IQ4_K repacked"(quantize.cpp),量化后文件大小与IQ4_K相当(约 4.5 bpw),因为 R4 只是重排、不增加存储。

4.2 开启 iqk 后端与性能验证

  • 编译时:确保以GGML_USE_IQK_MULMAT构建(iqk 后端默认启用),使vec_dot_iq4_k_r4_q8_kiqk_mul_mat快速路径(iqk_quantize.cpp)。
  • 运行时:用 examples/main/main.cpp 或llama-bench做 PP(prompt processing)与 TG(token generation)基准测试。PR 中使用的指标是PP-512(512 token 预填充吞吐,单位 t/s),实测模型为 LLaMA-3.1-8B。
  • 平台差异:如 PR #138 表格所示,同一模型在 ARM_NEON(M2-Max)上 IQ4_K → IQ4_K_R4 可获约 1.86 倍 PP 提速,Zen4 约 1.28 倍,AVX2 约 1.10 倍(详见 PR #138);TG 阶段(TG-128)在低线程数下同样有明显收益(如 AVX2 2 线程 1.72 倍),线程数增多后收益缩小。而 PR #144 之后,x86 的 PP 数据进一步提升到 251/249 t/s。这些数字均为仓库文档记录的实测结果,实际性能取决于 CPU 型号、线程数与内存带宽,建议在目标机器上自行复测。

4.3 与 CUDA 等其他后端的对照

IQ4_K_R4并非只有 CPU 路径。在 ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_k_r4.cu 和 mmvq-instance-iq4_k_r4.cu 中存在对应的 CUDA kernel 实例(相关实现见 README.md 中记录的 PR 461),说明该格式在 GPU 后端同样有 mmq/mmvq 支持。本文聚焦 CPU 侧 AVX2/Zen4 的加速,若关注 GPU,可查看上述模板实例继续深入。

五、总结

  • IQ4_K_R4是 ik_llama.cpp 对IQ4_K(约 4.5 bpw 非线性量化)的4 行重排变体,存储开销与IQ4_K完全一致,数值精度等价。
  • R4 的核心价值在于:将解包好的权重与右侧矩阵的多个列复用相乘,摊薄昂贵的解包成本;block_iq4_k_r4将 4 行数据交错打包(ggml-common.h),配合 iqk 后端按架构分派的 kernel(iqk_mul_mat.cpp)实现加速。
  • PR #144 专门优化了此前偏弱的 AVX2/Zen4 路径:PP-512(LLaMA-3.1-8B)从 232/227 t/s 提升到251/249 t/s,补上了 PR #138 之后 x86 侧相对 NEON 的差距。
  • 结合 PR #157 的 16 列复用改进可以看出,R4 系列量化在 ik_llama.cpp 中是一条持续演进、以「解包复用」为纲的 CPU 推理加速技术路线,IQ4_K_R4是其中收益最直观的代表之一。

如果想在真实硬件上复现文中的数字,可以按第四节的方式用仓库自带的llama-quantize生成IQ4_K_R4模型,再用llama-bench/main对比IQ4_KIQ4_K_R4的 PP、TG 吞吐,亲身体验这套重排优化带来的实际收益。

【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询