尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ik_llama.cpp 修复实录:ARM 上 q4_1/q5_1 量化矩阵乘的 vec_dot_type 失配与回退方案(PR 348)

ik_llama.cpp 修复实录:ARM 上 q4_1/q5_1 量化矩阵乘的 vec_dot_type 失配与回退方案(PR 348) ik_llama.cpp 修复实录ARM 上 q4_1/q5_1 量化矩阵乘的 vec_dot_type 失配与回退方案PR #348【免费下载链接】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 #348 记录github-data/pull_requests/348 - Fix q4_1 and q5_1 on Arm.md展开深入剖析一次发生在 ARM NEON 平台上的量化矩阵乘回归当 x86 AVX2 路径将若干 legacy 量化类型q4_1、q5_1的激活侧量化类型从q8_1_x4切换到q8_2_x4时ARM NEON 实现未同步更新导致 q4_1/q5_1进而波及 q4_0/q5_0 的部分 ffn_down 层损坏最终通过让 Arm 回退到q8_1_x4修复。读完本文你将理解 ik_llama.cpp 中vec_dot_type类型匹配机制、IQK 量化矩阵乘的 kernel 分发流程以及这类 SIMD 平台差异 bug 的排查与修复思路。背景legacy quants 与 IQK 量化矩阵乘架构ik_llama.cpp 的核心亮点之一是在 ggml 之上实现了独立的 IQKInteger Quantized Kernel矩阵乘加速框架相关代码集中在 ggml/src/iqk/ 目录下。其中iqk_gemm_kquants.cpp 处理 K-quantsq2_Kq6_K 系列iqk_gemm_ktquants.cpp 处理 KT-quantsiq1_ktiq4_kt 系列iqk_gemm_iquants.cpp 处理 I-quantsiqk_gemm_1bit.cpp 处理 1-bit 量化iqk_gemm_legacy_quants.cpp 则专门处理legacy quants——即 ggml 最原始的量化类型q4_0、q4_1、q5_0、q5_1、q6_0、q8_0以及 ik_llama.cpp 新增的 q4_0_r8、q5_0_r4、q6_0_r4、iq4_nl、iq4_nl_r4、mxfp4、mxfp4_r8 等变体。在 GGML 的计算模型中量化权重的矩阵乘并不是直接在原量化表示上运算而是将一侧激活/权重重新量化为另一侧的辅助量化类型后做整数点积。这个辅助量化类型由 ggml 类型特征表中的vec_dot_type字段决定PR 描述中写作vet_dot_type对应源码中的.vec_dot_type见 ggml/src/ggml.c 的类型注册表。vec_dot_type激活侧量化类型是如何决定的在 ggml/src/ggml.c 的类型特征表中每个量化类型都声明了自己的vec_dot_type并且在开启GGML_USE_IQK_MULMAT时按 SIMD 指令集分派权重类型条件vec_dot_typeq4_0__AVX2__Q8_2_X4q4_0其他含 ARMQ8_0_X4q4_1__AVX2__Q8_2_X4q4_1其他含 ARMQ8_1_X4q5_0__AVX2__Q8_2_X4q5_0其他含 ARMQ8_0_X4q5_1__AVX2__Q8_2_X4q5_1其他含 ARMQ8_1_X4q6_0__AVX2__Q8_2_X4其余为Q8_0_X4可以看到当前仓库中Q8_1_X4只在非 AVX2 路径下被 q4_1/q5_1以及 q8_1使用而Q8_2_X4是 AVX2 路径下的统一选择。这正是 PR #348 修复之后的状态。这些*_x4类型是 ik_llama.cpp 引入的4 合 1超级块结构将 4 个普通 block 合并成一个更大的块减少 scale 加载次数、提升 NEON/AVX 向量化效率。其定义位于 ggml/src/ggml-common.h// block_q8_1_x44 个 q8_1 块合并含 8 个 fp16 scaled 与 m 各 4 个 typedef struct { ggml_half d[8]; int8_t qs[4*QK8_1]; } block_q8_1_x4; static_assert(sizeof(block_q8_1_x4) 4*sizeof(block_q8_1), wrong q8_1_x4 block size/padding); // block_q8_2_x44 个 q8_2 块合并 typedef struct { uint16_t d[8]; int8_t qs[4*QK8_2]; } block_q8_2_x4; static_assert(sizeof(block_q8_2_x4) 4*sizeof(block_q8_2), wrong q8_2_x4 block size/padding);对应的量化函数quantize_row_q8_1_x4与quantize_row_q8_2_x4实现于 ggml/src/iqk/iqk_quantize.cpp二者共用同一个模板quantize_row_q8_1_x4_TBlock, BlockX4说明它们在数据布局上的差异仅在于 block 内部 scale/zero 的表示方式。Bug 根因AVX2 与 ARM NEON 的修改不同步PR #348 的描述给出了明确的因果链作者此前在一次改动中将使用q8_1_x4的量化类型即 q4_1、q5_1、q8_1的vec_dot_type改成了q8_2_x4以便统一走更高效的q8_2点积路径但只改了 x86 AVX2 侧的 kernel 分发逻辑忘记同步修改ARM_NEON实现结果在 ARM 平台上类型表中 q4_1/q5_1 的vec_dot_type指向Q8_2_X4而 ARM NEON 的 IQK legacy quants kernel 仍然期望/输出Q8_1_X4格式两者失配q4_1和q5_1的矩阵乘直接损坏连锁反应由于q4_0/q5_0会在少数ffn_down层FFN 下投影层见 src/graphs/ 下各build_*.cpp中model.layers[il].ffn_down的使用借用q4_1/q5_1的路径执行因此q4_0与q5_0也随之损坏。在 IQK 框架中这个失配发生在 kernel 选择阶段。iqk_mul_mat入口ggml/src/iqk/iqk_mul_mat.cpp将 q4_0/q4_1/q5_0/q5_1/q6_0/q8_0/iq4_nl/mxfp4 等类型统一交给iqk_convert_legacy_quants_q8_r8做激活重量化随后由iqk_set_kernels_legacy_quants校验权重类型 B 是否与预期辅助类型一致不一致即返回失败、退化为通用实现——而失败恰恰意味着无法走到经过优化的整数点积 kernel。修复方案Arm 回退到 q8_1_x4PR #348 的修复策略非常务实不追求在 ARM 上强行支持q8_2_x4作者评估改动过大而是让 ARM NEON 实现回退到原有的q8_1_x4。这一决策在 iqk_gemm_legacy_quants.cpp 中体现为 x86 与 aarch64 两套不同的iqk_set_kernels_legacy_quants实现x86__x86_64__分支L2479-L2552bool iqk_set_kernels_legacy_quants(int ne00, int typeA, int typeB, std::arraymul_mat_t, IQK_MAX_NY kernels, mul_mat_t func16) { if (ne00%QK8_0 ! 0) return false; auto expected_typeB GGML_TYPE_Q8_2_X4; // x86 统一期望 q8_2_x4 ... return ggml_type(typeB) expected_typeB; }ARM__aarch64__分支L3715-L3773bool iqk_set_kernels_legacy_quants(int ne00, int typeA, int typeB, std::arraymul_mat_t, IQK_MAX_NY kernels, mul_mat_t func16) { if (ne00%QK8_0 ! 0) return false; auto etypeA ggml_type(typeA); auto expected_typeB etypeA GGML_TYPE_Q4_1 || etypeA GGML_TYPE_Q5_1 || etypeA GGML_TYPE_Q8_1 ? GGML_TYPE_Q8_1_X4 : GGML_TYPE_Q8_0_X4; // Arm 回退q4_1/q5_1/q8_1 用 q8_1_x4 if (ggml_type(typeB) ! expected_typeB) return false; ... }也就是说修复后的匹配规则是x86 AVX2q4_0/q4_1/q5_0/q5_1/q6_0/q8_0 等全部期望Q8_2_X4ARM NEONq4_1/q5_1/q8_1 期望Q8_1_X4其余q4_0/q5_0/q6_0/q8_0 等期望Q8_0_X4。这正好与 ggml.c 中非 AVX2 平台 q4_1/q5_1 的 vec_dot_type 保持Q8_1_X4的类型声明严格对齐保证 kernel 分发与类型声明在任何平台上都不再失配。从 kernel 实现侧也能看到同样的平台分化ARM 分支为 q4_1/q5_1 注册的是基于q8_1系列的mul_mat_qX_1_q8_1如 L3872-L3879 中MAKE_FUNCS(mul_mat_qX_1_q8_1DequantizerQ41, nq)其内部通过Q8nrc_y, block_q8_1_x4 q8(info)消费q8_1_x4数据见 L3470-L3473而非 aarch64 分支则使用基于q8_2系列的mul_mat_qX_1_q8_2_TQ4_1_Unpacker, nq。两条 SIMD 路径各自维护一套数据布局与 kernel正是本次 bug 产生的结构性土壤。影响面与使用建议本次修复的波及范围与用户影响如下直接修复ARM 平台上q4_1、q5_1的量化矩阵乘恢复正常连带修复由于 q4_0/q5_0 在部分模型的少数 ffn_down 层会复用 q4_1/q5_1 路径q4_0、q5_0的异常也随之消除保持不变的取舍ARM NEON 侧继续使用q8_1_x4而非与 x86 统一的q8_2_x4这是作者在修复正确性与避免大规模重写 NEON kernel之间的明确取舍。PR 同时给出使用建议如果某些模型在修复后仍无法正常推理直接不要为这些模型使用 legacy quants例如改用 K-quants 或 ik 系列新量化不必等待 Arm 侧补全q8_2_x4支持。值得注意的是GGML_IQK_FA_ALL_QUANTS编译选项见 iqk_gemm_legacy_quants.cpp会把 legacy quants 也纳入 IQK 的 flash-attention 路径此时 kernel 选择同样遵循上述 x86/ARM 分支mul_mat_kernel中 q4_1 在__aarch64__下走mul_mat_qX_1_q8_1。因此无论是普通 matmul 还是 FA 路径平台一致性都由同一个expected_typeB校验兜底。排查同类问题的通用思路从这次回归中可以沉淀出一条针对 ik_llama.cpp / ggml 类项目的排查路径对读者定位量化后输出乱码/数值错误类问题同样适用对照类型声明与 kernel 期望先看 ggml.c 中目标权重类型的vec_dot_type再在对应iqk_gemm_*.cpp的iqk_set_kernels_*中找到expected_typeB确认两者一致留意条件编译分支同一份代码里__AVX2__/__x86_64__/__aarch64__/HAVE_FANCY_SIMD/GGML_USE_IQK_MULMAT等宏会派生多套实现改动任何一处类型映射时务必同步检查所有平台分支这正是 PR #348 的教训关注复合类型流转legacy quants 之间会互相借用如 q4_0 借用 q4_1 的 kernel、q8_1 与 q8_1_x4 的转换一个类型损坏可能波及其他类型排查时要把类型依赖链一起纳入以数据块结构为锚点block_q8_1_x4与block_q8_2_x4的布局ggml-common.h决定了 kernel 读取 scale/quants 的方式类型失配往往表现为 scale 错位导致的数值爆炸可以从这里验证数据格式是否对得上。小结PR #348 是一次典型的平台条件编译不同步回归修复x86 侧先行切换到q8_2_x4ARM NEON 侧回退保持q8_1_x4最终通过vec_dot_type类型声明与expected_typeBkernel 校验的双重对齐恢复了 ARM 上 q4_1/q5_1及其连带影响的 q4_0/q5_0的正确性。它在仓库当前代码中留下了清晰的对照痕迹——iqk_gemm_legacy_quants.cpp 里 x86 与 aarch64 两套iqk_set_kernels_legacy_quants各司其职既是修复证据也是后续维护者修改量化类型映射时务必同步双端的最佳反面教材。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表