ik_llama.cpp 编译排错实录:`undefined reference to ‘iqk_mul_mat‘` 的成因、定位与解决
2026/9/19 10:46:32 网站建设 项目流程

ik_llama.cpp 编译排错实录:undefined reference to 'iqk_mul_mat'的成因、定位与解决

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

本文围绕 ik_llama.cpp 仓库中 Issue #59 记录的链接错误展开:当用户在特定服务器上执行make llama-server/make llama-bench时,链接器报出一连串undefined reference to 'iqk_mul_mat'类错误。文章将完整还原报错现场与排查过程,并结合仓库源码(ggml/src/iqk/ggml/src/CMakeLists.txtggml/src/ggml.c等)剖析 IQK 矩阵乘法内核的编译开关、CPU 指令集依赖与构建系统差异,最终给出可落地的排查与规避方案。读完本文,你将掌握此类“编译通过、链接失败”类问题在 llama.cpp 系项目中的标准分析路径。

一、问题现象:一次典型的“链接期”失败

2024 年 9 月,用户ndavidson19在 ik_llama.cpp 仓库提交了 Issue #59(2024-09-18 创建,2024-09-26 关闭)。其核心现象是:在Intel Xeon Gold 5117服务器上运行make llama-servermake llama-bench时,编译(compile)阶段一切正常,但在链接(link)阶段ld抛出大量未定义符号错误:

/usr/bin/ld: ggml/src/ggml.o: in function `ggml_compute_forward_flash_attn_ext_f16': ggml.c:(.text+0xbdde): undefined reference to `iqk_flash_attn_noalibi' /usr/bin/ld: ggml/src/ggml.o: in function `ggml_compute_forward_mul_mat': ggml.c:(.text+0x13aac): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml.c:(.text+0x14ae6): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml.c:(.text+0x15109): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml/src/ggml.o: in function `ggml_compute_forward_mul_mat_id': ggml.c:(.text+0x15c49): undefined reference to `iqk_mul_mat_moe' /usr/bin/ld: ggml/src/ggml-quants.o: in function `ggml_vec_dot_q4_0_q8_0': ggml-quants.c:(.text+0x24a06): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml/src/ggml-quants.o: in function `ggml_vec_dot_q4_1_q8_1': ggml-quants.c:(.text+0x24b86): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml/src/ggml-quants.o: in function `ggml_vec_dot_q5_0_q8_0': ggml-quants.c:(.text+0x24d16): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml/src/ggml-quants.o: in function `ggml_vec_dot_q5_1_q8_1': ggml-quants.c:(.text+0x24ee6): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml/src/ggml-quants.o: in function `ggml_vec_dot_q8_0_q8_0': ggml-quants.c:(.text+0x250d6): undefined reference to `iqk_mul_mat' /usr/bin/ld: ggml/src/ggml-quants.o:ggml-quants.c:(.text+0x28c26): more undefined references to `iqk_mul_mat' follow collect2: error: ld returned 1 exit status make: *** [Makefile:1458: llama-server] Error 1

值得注意的细节:

  • 报错集中在ggml 核心对象文件ggml/src/ggml.oggml/src/ggml-quants.o)中;
  • 涉及符号横跨矩阵乘法iqk_mul_mat)、MoE 门控路由乘法iqk_mul_mat_moe)与Flash Attentioniqk_flash_attn_noalibi)三类 IQK 内核;
  • 报错的调用点既出现在ggml_compute_forward_mul_mat(普通矩阵乘)与ggml_compute_forward_mul_mat_id(MoE 专家路由),也出现在ggml_vec_dot_q4_0_q8_0等量化向量点积函数中。

这些符号全部属于 ik_llama.cpp 的IQK(Iwan Kawrakow 量化内核)体系,即该 fork 的核心差异化资产。

二、环境对比:为什么“这台机器不行、另一台机器行”

Issue 中贴出了两台服务器的 CPU 信息,这组对比是定位问题的关键线索。

故障机:Intel Xeon Gold 5117(Skylake-SP,虚拟化环境)

  • 24 逻辑 CPU,Thread(s) per core: 1
  • 关键 CPU flags 只到avxf16cfmasse4_2一级,没有avx2bmi1bmi2
  • flags 中出现hypervisor,说明运行在虚拟机中,lscpu上报的特性可能被虚拟化软件过滤。

正常机:Intel Xeon Platinum 8380(Ice Lake-SP)

  • 160 逻辑 CPU(双路,每路 40 核、双线程);
  • 具备完整的高级指令集:avx2bmi1bmi2avx512favx512dqavx512vlavx512bwavx512vbmiavx512_vnni等;
  • 用户报告在这台机器上获得了prompt processing 与 token generation 超过 50% 的提升(这是 Issue 报告者在其环境中的实测观察,非仓库官方基准数据)。

用户还补充了版本信息:故障机上./llama-server --version输出version: 3432 (12bbdb8c),使用 Ubuntu 22.04 的 GCC 11.4.0 构建——也就是说,故障机上的二进制本身已经成功构建过,而“另一台服务器却构建不出来”。两台机器恰好走向了相反的结果,这本身就暗示问题与目标机器的 CPU 特性/编译参数强相关。

三、根因剖析:从源码看iqk_mul_mat为什么“只声明、未定义”

“编译通过、链接失败”通常意味着:声明(声明头文件)被无条件包含了,而实现(源文件)因条件编译被排除。ik_llama.cpp 的 IQK 代码恰好符合这一模式。

3.1 IQK 实现受IQK_IMPLEMENT宏门控

先看 ggml/src/iqk/iqk_config.h:

#if defined IQK_IMPLEMENT #undef IQK_IMPLEMENT #endif #if defined __AVX2__ || defined __ARM_FEATURE_DOTPROD #define IQK_IMPLEMENT #endif

IQK_IMPLEMENT只在两种情况下被定义:x86 平台启用__AVX2__,或 ARM 平台启用__ARM_FEATURE_DOTPROD(点积指令)。换句话说,在 x86 上若编译器没有以启用 AVX2 的方式(如-mavx2-march=native且 CPU 支持 AVX2)编译 IQK 源文件,IQK_IMPLEMENT就不会生效,相关内核实现会被条件编译逻辑整体排除。

对照 Issue #59 的两台机器:Xeon Gold 5117 的 flags 里根本没有avx2(且处于虚拟化环境,特性上报受限);而 Xeon Platinum 8380 完整支持 AVX-512 家族。可以推断:在 5117 上构建时,__AVX2__未被定义 →IQK_IMPLEMENT未定义 → ggml/src/iqk/iqk_mul_mat.cpp 等实现文件中的内核函数未产出符号,而 ggml/src/ggml.c 与 ggml/src/ggml-quants.c 在GGML_USE_IQK_MULMAT控制下仍会调用这些函数,于是链接器报出成片的undefined reference

3.2 调用方:ggml 核心无条件引用 IQK 符号

从当前仓库源码可以确认这些符号的真实调用点:

  • ggml/src/ggml.c:ggml_compute_forward_mul_mat中,#if GGML_USE_IQK_MULMAT分支直接调用iqk_mul_mat_4d(...)尝试走 IQK 路径,失败后回退常规路径;
  • ggml/src/ggml.c:ggml_compute_forward_mul_mat_id(MoE)中调用iqk_mul_mat_moe(...)
  • ggml/src/ggml.c:Flash Attention 路径调用iqk_flash_attn_noalibi(...)
  • ggml/src/ggml-quants.c 等(L4603、L4900、L5260、L5644、L5655、L12029):ggml_vec_dot_q4_0_q8_0ggml_vec_dot_q4_1_q8_1ggml_vec_dot_q5_0_q8_0ggml_vec_dot_q5_1_q8_1ggml_vec_dot_q6_0_q8_0ggml_vec_dot_q8_0_q8_0等向量点积函数中均调用iqk_mul_mat(...)

这与 Issue #59 报错栈中的函数名一一对应,佐证了“调用方已编译、实现方未编译”的判断。

3.3 声明方:完整的 C 接口头文件

所有被引用的符号都在 ggml/src/iqk/iqk_mul_mat.h 中以extern "C"声明,包括:

IQK_API bool iqk_mul_mat(long Nx, long Ny, long ne00, int typeA, const void * A, long strideA, int typeB, const void * B, long strideB, float * C, long stride_C, int ith, int nth); IQK_API bool iqk_mul_mat_4d(long Nx, long Ny, long ne00, ...); IQK_API bool iqk_mul_mat_moe(long Nx, long Ny, long ne00, int ne11, ...); IQK_API bool iqk_moe_fused_up_gate(long Nx, long Ny, long ne00, int ne11, int unary_op, ...); IQK_API bool iqk_flash_attn_noalibi(int type_q, int type_mask, float max_bias, ...);

头文件被 ggml/src/ggml.c 和 ggml/src/ggml-quants.c 无条件 include。这再次印证:声明永远在场,实现才是变量

3.4 构建侧:Makefile 与 CMake 的差异

Issue 中维护者ikawrakow的回复直接点出了第二层问题——顶层 Makefile 是从 llama.cpp 继承来的,并未反映真实构建产物依赖。他给出了反例:

ggml.o的 Makefile 构建规则只声明了两个依赖:

ggml/src/ggml.o: \ ggml/src/ggml.c \ ggml/include/ggml.h $(CC) $(CFLAGS) -c $< -o $@

而用gcc -Iggml/include -Iggml/src -MM ggml/src/ggml.c生成的真实依赖却是:

ggml.o: ggml/src/ggml.c ggml/src/ggml-impl.h ggml/include/ggml.h \ ggml/src/ggml-quants.h ggml/src/ggml-common.h ggml/src/ggml-aarch64.h

也就是说,Makefile 的依赖图是不完整的。在增量构建、并行构建(make -j)或跨机器构建时,极易出现部分对象文件过期、部分未重编的状态,最终把“新旧混杂”的.o交给链接器,产生与 Issue #59 完全一致的未定义符号错误。维护者本人也承认“我平时用 CMake,所以 Makefile 的健壮性不如预期”。

从当前仓库结构看,顶层 Makefile 已经不在仓库中(仅 examples/batched.swift/Makefile 等零星残留),构建已完全由 CMake 驱动,这也印证了维护者的路线选择。

四、解决方案与排查路径

4.1 标准流程:先做干净重建

无论根因是“依赖图不完整”还是“CPU 特性开关”,第一步都应该是彻底清理后重建,避免陈旧.o干扰:

make clean && make -j

在 Issue #59 中,用户反馈这条命令仍复现相同错误,随后表示将改用 CMake。这说明单纯的清理在该机器上不足以解决问题——进一步指向了 CPU 指令集门控(IQK_IMPLEMENT依赖 AVX2)这一深层原因。

4.2 首选方案:切换到 CMake 构建

维护者明确表示自己使用 CMake,且当前仓库的推荐构建方式也是 CMake(见 README.md 的构建章节):

cmake -B build -DGGML_NATIVE=ON cmake --build build --config Release -j$(nproc)

CMake 构建的差异在于:它通过 ggml/CMakeLists.txt 的option(GGML_IQK_MUL_MAT "ggml: use optimized iqk matrix multiplications" ON)显式控制 IQK 矩阵乘法,并在 ggml/src/CMakeLists.txt 中把实现源文件精确纳入编译单元

set (GGML_SOURCES_IQK iqk/iqk_quantize.cpp iqk/iqk_cpu_ops.cpp) if (GGML_IQK_MUL_MAT) message(STATUS "Using optimized iqk matrix multiplications") add_compile_definitions(GGML_USE_IQK_MULMAT) set(GGML_SOURCES_IQK_MM iqk/iqk_mul_mat.cpp iqk/iqk_kda.cpp iqk/iqk_flash_attn.cpp iqk/fa/iqk_fa_576_512.cpp iqk/fa/iqk_fa_512_512.cpp ...) endif()

一旦GGML_IQK_MUL_MAT打开,GGML_USE_IQK_MULMAT编译宏被定义,iqk_mul_mat.cppiqk_flash_attn.cppiqk_gemm_*.cpp等实现全部进入构建,链接器便不会再缺符号。CMake 的正确依赖追踪与源码清单管理,正是规避“Makefile 依赖图残缺”这类问题的关键。

4.3 深层规避:CPU 不支持 AVX2 时的选择

结合 ggml/src/iqk/iqk_config.h 的IQK_IMPLEMENT门控逻辑可以确认:在 x86 平台上,AVX2 是 IQK 内核生效的硬前提。对于 Xeon Gold 5117 这类(尤其是虚拟化环境中 flags 被过滤的)机器,合理的选择包括:

  1. 关闭GGML_IQK_MUL_MATcmake -B build -DGGML_IQK_MUL_MAT=OFF ...,退回通用的 ggml 矩阵乘法路径(性能会下降,但可构建、可运行);
  2. 显式指定兼容指令集:如-DCMAKE_C_FLAGS="-mavx2"之类,前提是 CPU 物理上支持 AVX2;
  3. 优先在支持 AVX2 / AVX-512 的机器上构建:Issue 中维护者与用户的实测均表明,完整指令集(如 Ice Lake 的 AVX-512 家族)下 IQK 内核带来的性能收益显著。

需要说明:仓库演进过程中曾有过移除GGML_IQK_MUL_MAT开关的讨论(见 PR #457 记录,其核心论点是“不使用GGML_IQK_MUL_MAT就没有使用 ik_llama.cpp 的意义”),但当前 ggml/CMakeLists.txt 中该选项仍以默认 ON 存在。这从侧面说明:IQK 矩阵乘法是 ik_llama.cpp 性能灵魂,正常情况下应保持开启,只有遇到构建兼容性问题时才考虑关闭。

4.4 定位此类问题的通用方法论

结合本次 Issue 的完整排查链条,可以沉淀出可复用的排错步骤:

  1. 区分编译期/链接期错误:全部为undefined reference时,先确认声明头文件与实现源文件是否都在编译单元内;
  2. 核对符号归属:用nm/objdump -T检查目标文件或库中是否产出该符号(如nm ggml/src/iqk/iqk_mul_mat.o | grep iqk_mul_mat);
  3. 检查条件编译开关:搜源码中的#if/#ifdef,确认宏定义路径(本例即GGML_USE_IQK_MULMATIQK_IMPLEMENT__AVX2__);
  4. 核对目标机 CPU 特性lscpu查看 flags,警惕虚拟化环境对指令集上报的过滤;
  5. 对比构建系统:若 Makefile 与 CMake 行为不一致,优先以依赖解析更严格的 CMake 为准,并检查 Makefile 依赖图是否完整(可用gcc -MM交叉验证);
  6. 完整日志是硬通货:维护者在 Issue 中反复索要完整make输出,因为只有完整日志才能定位是“哪个 .o 缺符号、哪个源文件没编译”。Issue #59 最终因用户未提供完整日志而关闭,这也提示我们:提交此类 Issue 时,务必附带make全量输出与lscpu信息,才能让维护者高效介入。

五、结语

Issue #59 是理解 ik_llama.cpp 构建体系的一扇窗口:它以一次典型的链接期报错,串联起了 IQK 内核的指令集门控IQK_IMPLEMENT依赖__AVX2__/__ARM_FEATURE_DOTPROD)、CMake 源码清单GGML_IQK_MUL_MATGGML_SOURCES_IQK_MM)以及Makefile 依赖图缺陷三条线索。对开发者而言,遇到同样的undefined reference to 'iqk_mul_mat'时,按“干净重建 → 改用 CMake → 核对 CPU 特性/关闭 IQK 选项 → 提供完整日志”的顺序排查,即可快速收敛问题;而对想深挖 ik_llama.cpp 性能内核的读者,ggml/src/iqk/iqk_mul_mat.h、ggml/src/iqk/iqk_config.h 与 ggml/src/iqk/ 目录下的iqk_mul_mat.cppiqk_gemm_*.cppfa/子目录,则是继续研究的上佳起点。

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

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

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

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

立即咨询