ik_llama.cpp PR #72 深度解析Zen4/AVX2 上重写 IQ4_NL 矩阵乘实现PP-512 提速 22%【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp本篇技术指南围绕 ik_llama.cppllama.cpp 的性能优化分支中的 PR #72 记录文档 展开剖析作者 ikawrakow 如何在 Zen4/AVX2 指令集上重构IQ4_NL量化格式的矩阵乘法mul_mat内核使 LLaMA-3.1-8B 的 PP-512512 token 前缀处理吞吐从 133.2 t/s 提升至 162.6 t/s约 22% 加速。读完本文你将理解IQ4_NL的底层数据布局、AVX2 下非线性量化的高效反量化技巧以及该 PR 为后续将IQ4_NL用于 KV-cache 所做的基础铺垫并掌握如何在当前仓库源码中定位与验证这些优化。一、PR #72 是什么一次为 KV-cache 铺路的矩阵乘内核重写1.1 PR 基本信息项目内容PR 编号#72作者ikawrakowik_llama.cpp 作者状态已关闭Closed创建 / 更新时间2024-10-01核心内容iqk_mul_mat中针对 Zen4/AVX2 的IQ4_NL矩阵乘实现重写效果LLaMA-3.1-8B PP-512 从 133.2 t/s 提升到 162.6 t/s22%1.2 两个关键动机PR 描述中作者明确给出了两层动机直接收益IQ4_NL矩阵乘在 Zen4/AVX2 上获得约 22% 的吞吐提升对正在使用它的人来说相当有用。长期铺垫该工作主要是为调查IQ4_NL用于 KV-cache 做准备mostly as preparation for investigatingIQ4_NLusage for KV-cache。第二个动机尤为重要。从当前仓库源码看这个铺垫确实落地了在 iqk_flash_attn.cpp 的 Flash Attention 支持类型列表中GGML_TYPE_IQ4_NL与GGML_TYPE_F16、GGML_TYPE_Q8_0、GGML_TYPE_Q4_0等并列作为可用的 KV cache 类型该文件第 294 行还提示若想启用q4_0、q4_1、iq4_nl这几类 KV cache需要用-DGGML_IQK_FA_ALL_QUANTSON重新编译。这说明 PR #72 优化的IQ4_NL反量化与点积路径正是后续量化 KV-cache 功能的基础构件。二、背景知识IQ4_NL 到底是什么2.1 非线性 4-bit 量化格式IQ4_NLNon-Linear是 ik_llama.cpp 引入的一种 4-bit 量化格式与线性量化的Q4_0等不同它通过非线性查找表lookup table把 4-bit 索引映射为浮点值从而让量化网格更贴合实际权重分布在同等位宽下获得更低的困惑度perplexity。其内存布局定义在 ggml-common.h// Non-linear quants #define QK4_NL 32 typedef struct { ggml_half d; // fp16 块尺度 uint8_t qs[QK4_NL/2]; // 32 个权重打包为 16 字节每权重 4 bit } block_iq4_nl; static_assert(sizeof(block_iq4_nl) sizeof(ggml_half) QK4_NL/2, wrong iq4_nl block size/padding);关键参数块大小QK4_NL 32每 32 个权重共享一个 fp16 尺度d打包方式每个权重占 4 bit32 个权重共 16 字节即每个 block 总计 18 字节存储代价18 / 32 4.5bit/权重4 bit 数据 0.5 bit 尺度的均摊反量化方式qs中的 4-bit 索引不直接线性缩放而是先查表得到符号值再乘以尺度d。2.2 与后续衍生格式的关系同一个头文件中还能看到IQ4_NL的衍生变体ggml-common.htypedef struct { ggml_half d[4]; uint8_t qs[2*QK4_NL]; } block_iq4_nl_r4; // 4 行共享打包R4 typedef struct { ggml_half d[8]; uint8_t qs[4*QK4_NL]; } block_iq4_nl_r8; // 8 行共享打包R8这些_r4/_r8后缀格式把多行权重打包成更大块配合 SIMD 批量处理是后续进一步提升吞吐的方向。本文 PR #72 聚焦的是基础IQ4_NL在 AVX2 上的实现但源码中mul_mat_iq4_nl_r4_q8_2、mul_mat_iq4_nl_r8_q8_2等函数见 iqk_gemm_legacy_quants.cpp都复用同一套反量化准备逻辑。三、核心优化AVX2 上如何高效处理 IQ4_NL 反量化3.1 反量化的 AVX2 实现原理IQ4_NL反量化的难点在于4-bit 索引需要经过非线性表映射如果逐元素查表会非常慢。PR #72 的新实现当前仓库中即为其成果形态采用了_mm256_shuffle_epi8向量洗牌 预加载查找表的组合一次处理 16 个权重。核心代码在 iqk_common.h 的prepare_iq4_nl_quantsstatic IQK_ALWAYS_INLINE void prepare_iq4_nl_quants( const int8x16_t values, // 预加载的非线性查找表256 字节向量 const uint8x16_t m4, // 掩码用于分离高/低 4 bit const uint8x16x4_t bits, // 打包的 4-bit 数据 int8x16_t * qx) { ... }其工作流程可概括为解包把每个字节中的两个 4-bit 索引高 4 位 / 低 4 位分离到独立字节查表用_mm256_shuffle_epi8(values, index)一步完成 16 个元素的非线性映射values即load_iq4nl_values_256()预加载的表符号处理映射结果带符号int8随后在点积阶段与量化激活值相乘累加。对应地在 iqk_gemm_legacy_quants.cpp 中可以看到两种反量化器struct IQ4_NL_DequantizerU { Dequantizer4bit b4; const __m256i values load_iq4nl_values_256(); inline __m256i dequant(const block_iq4_nl * x) const { return _mm256_shuffle_epi8(values, b4.dequant(x-qs)); } }; struct IQ4_NL_DequantizerS { Dequantizer4bit b4; const __m256i values load_iq4k_values_256(); inline __m256i dequant(const block_iq4_nl * x) const { return _mm256_shuffle_epi8(values, b4.dequant(x-qs)); } };IQ4_NL_DequantizerUUnpacked使用iq4nl专属查找表用于逐块反量化IQ4_NL_DequantizerSSymmetric使用对称量化表用于可复用的打包路径。两者都把4-bit 索引 → 非线性浮点值翻译成了单条 SIMD shuffle 指令这正是 AVX2 上相比逐元素查表大幅提速的关键反量化不再是标量循环而是纯向量化、无分支的操作。3.2 为什么是 Zen4/AVX2从 iqk_config.h 可以看到 IQKIwan Kawrakows Kernels内核的启用条件#if defined __AVX2__ || defined __ARM_FEATURE_DOTPROD #define IQK_IMPLEMENT #endif即 IQK 内核体系在x86 的 AVX2或ARM 的 DotProd指令集上默认生效。PR #72 标题中的 Zen4/AVX2 指的是 AMD Zen4 架构在 AVX2非 AVX-512路径下的优化分支。在 ggml/src/CMakeLists.txt 中也有相应说明当HAVE_FANCY_SIMDAVX-512 全家桶关闭时IQK GEMM 内核为 Zen4 / Sapphire Rapids 等平台准备的路径会被启用。因此该 PR 的收益面向的是大量仍以 AVX2 为主力指令集的 x86 CPU而不是只对特定旗舰硬件生效。3.3 数据流的配合激活侧的 Q8_0 转换矩阵乘的收益不只在权重侧反量化。在 iqk_mul_mat.cpp 的类型选择逻辑中可以看到case GGML_TYPE_IQ4_NL : return nrc_y 32 ? GGML_TYPE_Q8_0_R8 : type;也就是说当输出行数nrc_y达到 32 及以上时IQ4_NL权重会配合Q8_0_R88 行打包的 Q8_0 激活量化执行乘法小批量场景则退回普通类型路径。这种权重反量化 激活 8-bit 量化的组合让 AVX2 的整数点积指令如_mm256_maddubs_epi16/_mm256_madd_epi16得以全速运行避免 fp16/fp32 乘法的吞吐瓶颈。四、性能结果PP-512 的 22% 提升意味着什么4.1 PR 报告的原始数据指标优化前优化后提升LLaMA-3.1-8B PP-512t/s133.2162.622%注以上数据来自 PR #72 描述作者自测具体硬件为支持 AVX2 的 Zen4 平台测试负载为 512 token 前缀处理PP-512。数值属于特定软硬件配置下的实测结果不同机型、模型量化配置下的绝对数值会不同但相对增益的方向具有参考意义。4.2 为什么前缀处理PP最受益PPPrompt Processing是纯计算密集型任务512 个 token 的前缀需要一次性完成大批量矩阵乘batch size 大、nrc_y高正好落入第 3.3 节所述nrc_y 32的高性能路径。而 token 生成TG阶段 batch 极小矩阵乘形状完全不同因此该 PR 的加速主要体现在 PP 阶段——这也解释了作者为何用 PP-512 作为基准指标。五、如何验证与使用在当前仓库中定位这条优化路径5.1 编译期开关IQK 矩阵乘内核包含本文的IQ4_NL实现由 CMake 选项控制见 ggml/src/CMakeLists.txtset (GGML_SOURCES_IQK iqk/iqk_quantize.cpp iqk/iqk_cpu_ops.cpp) if (GGML_IQK_MUL_MAT) add_compile_definitions(GGML_USE_IQK_MULMAT) set(GGML_SOURCES_IQK_MM iqk/iqk_mul_mat.cpp ...) ... endif()GGML_IQK_MUL_MAT默认开启启用iqk_mul_mat替代 ggml 默认矩阵乘GGML_IQK_FLASH_ATTENTION启用 IQK Flash Attention 内核GGML_IQK_FA_ALL_QUANTS启用全部 KV cache 量化类型含iq4_nlKV cache。构建示例在仓库根目录执行cmake -B build -DGGML_IQK_MUL_MATON cmake --build build --config Release -j如果后续想实验IQ4_NL作为 KV cache需要额外打开-DGGML_IQK_FA_ALL_QUANTSON见 iqk_flash_attn.cpp 的编译提示。5.2 运行时验证通过llama-benchexamples/llama-bench可以复现 PP-512 场景加载 LLaMA-3.1-8B 的IQ4_NL量化模型设置-p 512观察 PP 吞吐对比开关GGML_IQK_MUL_MAT前后的 PP 数值即可在本机验证该内核的收益需确保模型权重为IQ4_NL格式可通过llama-quantizeexamples/quantize将模型转换为IQ4_NL因为该优化只作用于该量化类型。5.3 相关源码速查表关注点文件位置IQ4_NL块结构与尺寸ggml-common.h矩阵乘入口 APIiqk_mul_mat.h类型映射与降级逻辑iqk_mul_mat.cppAVX2 反量化准备iqk_common.h反量化器与 GEMM 变体iqk_gemm_legacy_quants.cppIQK 启用条件AVX2/DotProdiqk_config.h编译选项ggml/src/CMakeLists.txtIQ4_NLKV-cache 支持iqk_flash_attn.cpp六、总结与延伸PR #72 是一次典型的小而关键的内核优化它没有改变IQ4_NL的量化格式本身而是把反量化从标量路径改写为AVX2 向量洗牌 查找表的零分支实现并在激活侧配套Q8_0_R8最终为 LLaMA-3.1-8B 的 PP-512 带来约 22% 的吞吐提升。从仓库现状看这次重写不仅直接加速了IQ4_NL权重的矩阵乘也为后续IQ4_NL进入 KV-cache 类型清单见 iqk_flash_attn.cpp铺平了道路——当 KV-cache 以IQ4_NL存储时Flash Attention 内核中 K 矩阵的反量化同样依赖这套高效的 AVX2 查表路径。对于希望在自己的 Zen4/AVX2 机器上复现收益的开发者建议路径是编译时确认GGML_IQK_MUL_MATON默认开启→ 用llama-quantize准备IQ4_NL模型 → 用llama-bench -p 512对比实测。如果你想继续深入可以顺着上表从 iqk_gemm_legacy_quants.cpp 的mul_mat_iq4_nl_r4_q8_2/mul_mat_iq4_nl_r8_q8_2追下去那里是 R4/R8 打包格式下同一思路的进一步演进。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考