ik_llama.cpp 的 Metal 后端 Trellis 量化(IQ_KT)实现解析
【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp
Trellis 量化是 ik_llama.cpp 引入的一系列新型低比特量化格式(IQ1_KT、IQ2_KT、IQ3_KT、IQ4_KT),其核心思想来自 QTIP 论文的"3INST"算法:用一个 L 比特的 seed 通过线性同余递推一次性生成一组 N 个(近似正态分布的)量化值,从而以极低的比特率描述权重。本文以 PR #475(Metal implementation for the trellis quants)为核心,结合仓库内ggml/src/ggml-metal.m与ggml/src/ggml-metal.metal的源码实现,讲解 Trellis 量化在 Apple GPU(Metal)后端上的落地方式、kernel 种类与当前状态,帮助读者理解如何在 macOS 上让 IQ_KT 量化模型跑起来,以及为什么部分类型尚未启用。
背景:Trellis 量化(IQ_KT)是什么
Trellis 量化最初由 PR #113(Trellis quantization)引入 ik_llama.cpp。与主流 i-/k-quants 不同,它不是为每个权重独立存储量化值,而是借助 QTIP 论文提出的"3INST"算法:给定一个 seed,通过以下递推产生 N 个量化值:
uint32_t u32; float16_t * h = reinterpret_cast<float16_t*>(&u32); for i in 0...N-1 seed = a * seed + b u32 = (mask1 & seed) ^ mask2 q_i = h[0] + h[1] end其中a、b、mask1、mask2为合适的常数,生成的序列近似正态分布。这样一组 N 个量化值只需用一个 L 比特的 seed(索引)描述,量化时仍按 i-/k-quants 的风格组织为 block 与 super-block,并用聚类算法搜索最优 seed。新增的量化类型与比特率如下:
| 类型 | seed 位数 L | 每组量化数 N | block 大小 | block scale | 总比特率 |
|---|---|---|---|---|---|
IQ2_KT | 16 bit | 8 | 32 | 4 bit | 2.125 bpw |
IQ3_KT | 12 bit | 4 | 32 | 4 bit | 3.125 bpw |
IQ4_KT | 15 bit | 4 | 32 | 8 bit | 4.0 bpw |
IQ1_KT | — | — | — | — | 1.75 bpw(后续由 PR #616 加入) |
在 ggml/include/ggml.h 中可以确认这些类型在 GGML 中的枚举值:GGML_TYPE_IQ2_KT = 153、GGML_TYPE_IQ3_KT = 154、GGML_TYPE_IQ4_KT = 155、GGML_TYPE_IQ1_KT = 158;对应的 GGUF 文件类型常量GGML_FTYPE_MOSTLY_IQ2_KT = 142至GGML_FTYPE_MOSTLY_IQ1_KT = 147也定义在同一文件中。
需要注意的是,后续 PR #529 将实现换成了全新的"integer-based trellis"(整数基 Trellis),使得 CPU 上也能获得合理性能(详见 README.md)。这意味着当前仓库中的IQ_KT系列是整数递推版本,Metal 后端实现基于这套新方案。
PR #475 的核心内容与状态
PR #475(Metal implementation for the trellis quants)的目标是为 Trellis 量化在 Apple GPU 上提供推理支持。作者在描述中明确给出了当时的进展状态:
IQ2_KT与IQ3_KT可以正常工作,其中IQ2_KT已有相当不错的性能("has a pretty decent performance");IQ4_KT存在一个未能定位的 bug,因此在 Metal 后端被暂时禁用("disabled for now as there is a bug that I don't find")。
该 PR 的状态为Closed(创建于 2025-05-30,更新于 2025-06-01)。README 的 Quantization additions 一节也将 Metal 列为 Trellis quants 的补充实现之一:Additional implementations: Metal [PR 475], Neon [PR 471], CPU [PR 441](README.md)。
这个"部分可用、部分禁用"的状态在今天的源码中仍然清晰可见,是理解 Metal 后端 KT 支持现状的钥匙。
源码验证:Metal 中的 kernel 注册与禁用痕迹
kernel 枚举与注册
在 ggml/src/ggml-metal.m 中,Trellis 相关的 kernel 枚举分布于多个类别,且IQ4_KT全部以注释形式禁用:
- GET_ROWS(取行/embedding 操作):
GGML_METAL_KERNEL_TYPE_GET_ROWS_IQ2_KT、GET_ROWS_IQ3_KT正常,GET_ROWS_IQ4_KT被注释(ggml/src/ggml-metal.m); - MUL_MV(矩阵-向量乘,token 生成阶段核心):
MUL_MV_IQ2_KT_F32、MUL_MV_IQ3_KT_F32注册,MUL_MV_IQ4_KT_F32注释(ggml/src/ggml-metal.m); - MUL_MV_ID(MoE 专家矩阵的 MUL_MV 变体):同上,IQ2/IQ3 正常、IQ4 注释(ggml/src/ggml-metal.m);
- MUL_MM(矩阵-矩阵乘,prompt 处理阶段核心):
MUL_MM_IQ2_KT_F32、MUL_MM_IQ3_KT_F32、MUL_MM_IQ2_KT_F16、MUL_MM_IQ3_KT_F16正常,IQ4 版本注释(ggml/src/ggml-metal.m); - MUL_MM_ID(MoE 的 MUL_MM 变体):同上(ggml/src/ggml-metal.m)。
对应的 kernel 加载使用GGML_METAL_ADD_KERNEL宏,例如:
GGML_METAL_ADD_KERNEL(GGML_METAL_KERNEL_TYPE_GET_ROWS_IQ2_KT, get_rows_iq2_kt, true); GGML_METAL_ADD_KERNEL(GGML_METAL_KERNEL_TYPE_GET_ROWS_IQ3_KT, get_rows_iq3_kt, true); //GGML_METAL_ADD_KERNEL(GGML_METAL_KERNEL_TYPE_GET_ROWS_IQ4_KT, get_rows_iq4_kt, true);(ggml/src/ggml-metal.m)
在 dispatch 阶段,ggml-metal.m中同样用注释保留了IQ4_KT的分支(例如 ggml/src/ggml-metal.m 的 MUL_MM_F32 选择逻辑、ggml/src/ggml-metal.m 的 MUL_MV 选择逻辑),并在一些条件判断中写为src0t == GGML_TYPE_IQ2_KT || src0t == GGML_TYPE_IQ3_KT // || src0t == GGML_TYPE_IQ4_KT(ggml/src/ggml-metal.m)。这些被注释的代码正是 PR #475 中"bug 未定位、先禁用"决策的源码级痕迹:实现本身已经写了大半,只差最后一步问题排查。
Metal shader 实现
在 ggml/src/ggml-metal.metal 中,三个类型的 kernel 均有实际实现:
kernel_mul_mv_iq2_kt_f32_impl:针对block_iq2_kt,每行有一个额外的 float scale(row_size = sizeof(float) + nb*sizeof(block_iq2_kt)),并在读取 block scale 时乘上系数1.05f(ggml/src/ggml-metal.metal)。去量化核心调用Trellis3::gen8(q2[2*it+0]+4096, v1, v2),一次生成 8 个量化值(ggml/src/ggml-metal.metal);kernel_mul_mv_iq3_kt_f32_impl:结构类似,行大小同样带一个 float scale(ggml/src/ggml-metal.metal);kernel_mul_mv_iq4_kt_f32_impl:注意其行大小是2*sizeof(float) + nb*sizeof(block_iq4_kt),即每行带两个float scale(ggml/src/ggml-metal.metal),这与IQ4_KT8-bit block scale 的格式细节相关。
此外还有:
- 模板化的去量化器
dequantize_iq2_kt/dequantize_iq3_kt/dequantize_iq4_kt(ggml/src/ggml-metal.metal),供 GET_ROWS 与 MUL_MM 复用; - MUL_MM 的模板实例化:
kernel_mul_mm_iq2_kt_f32/f16、kernel_mul_mm_iq3_kt_f32/f16、kernel_mul_mm_iq4_kt_f32/f16(ggml/src/ggml-metal.metal); - MUL_MM_ID(MoE)模板实例化(ggml/src/ggml-metal.metal)与 MUL_MV_ID 模板实例化(ggml/src/ggml-metal.metal)。
可以看到 Metal 侧的 shader 体系是完备的(IQ4 的 shader 同样存在,只是没有被.m侧注册和 dispatch),与 CUDA 侧的mmq-instance-iq*_kt.cu、mmvq-instance-iq*_kt.cu模板实例(位于 ggml/src/ggml-cuda/template-instances/)形成对应:不同后端各有一套 KT kernel。
如何在 macOS 上使用 IQ_KT 量化模型
构建 Metal 后端
构建时通过 CMake 选项启用 Metal(这是 ggml 的通用 Metal 构建路径,ggml/src/CMakeLists.txt 中可见GGML_METAL、GGML_METAL_NDEBUG、GGML_METAL_EMBED_LIBRARY、GGML_METAL_SHADER_DEBUG、GGML_METAL_MACOSX_VERSION_MIN、GGML_METAL_STD等选项):
cmake -B build -DGGML_METAL=ON -DGGML_METAL_EMBED_LIBRARY=ON cmake --build build --config Release -jGGML_METAL_EMBED_LIBRARY将编译好的.metallib嵌入二进制,便于分发;调试 shader 时可关闭它以从外部加载。需要说明的是,本文讨论的是源码层面的 kernel 支持情况,实际运行效果以你的 macOS 硬件与系统版本为准。
生成 IQ_KT 模型
examples/quantize/quantize.cpp已注册全部 KT 类型对应的LLAMA_FTYPE:
{ "IQ3_KT", LLAMA_FTYPE_MOSTLY_IQ3_KT, " 3.125 bpw trellis quantization" }, { "IQ4_KT", LLAMA_FTYPE_MOSTLY_IQ4_KT, " 4.0 bpw trellis quantization" }, { "IQ1_KT", LLAMA_FTYPE_MOSTLY_IQ1_KT, " 1.75 bpw trellis quantization" }, { "IQ2_KT", LLAMA_FTYPE_MOSTLY_IQ2_KT, " 2.125 bpw trellis quantization" },(examples/quantize/quantize.cpp 与 examples/quantize/quantize.cpp)
使用方式:
./build/bin/llama-quantize ./model.gguf ./model-IQ2_KT.gguf IQ2_KT生成IQ2_KT(2.125 bpw)或IQ3_KT(3.125 bpw)模型后,即可在 Metal 后端上以正常的llama-cli/llama-server流程加载推理。根据 PR #475 与当前源码状态,IQ2_KT、IQ3_KT是 Metal 上已验证可用的类型,而IQ4_KT建议在 CUDA/CPU 后端使用(那里有完整的 mmq/mmvq 实例),等待 Metal 侧的 bug 修复。
从 PR #475 看 Trellis 量化的工程落地要点
- Trellis 的"按需解压"特性决定了 kernel 形态:
3INST递推在 kernel 内通过Trellis3::gen8等函数即时生成量化值,而不是预先把整个 block 解量化到显存,因此 MUL_MV/MUL_MM kernel 都直接操作block_iq*_kt的原始内存并内联生成值,这对寄存器与 threadgroup 内存(threadgroup int8_t * shared_values)的利用提出了要求; - 每个类型的存储布局有细微差异:如
IQ2_KT/IQ3_KT每行一个 float scale(并带1.05f修正系数),IQ4_KT每行两个 float scale,kernel 中通过row_size精确计算行偏移——新增量化类型时后端 kernel 必须逐一对齐这种布局; - 禁用不是删除:
IQ4_KT的 shader、枚举、dispatch 分支都以注释形式保留在 ggml/src/ggml-metal.m 与 ggml/src/ggml-metal.metal 中,方便后续排查修复后直接恢复注册,这也是 PR #475 留下的明确工程痕迹。
小结
PR #475 为 ik_llama.cpp 的 Trellis 量化补齐了 Metal 后端:IQ2_KT与IQ3_KT的 GET_ROWS、MUL_MV、MUL_MV_ID、MUL_MM、MUL_MM_ID 全套 kernel 已注册并可用,其中IQ2_KT性能表现不错;IQ4_KT因未知 bug 被暂时禁用,但 shader 与 dispatch 代码均已保留。若要继续深入,可对比阅读 CPU(ggml/src/iqk/iqk_mul_mat.cpp)、CUDA(ggml/src/ggml-cuda/mmq.cu)中的对应实现,以及后续的 PR #505(New IQ4_KT trellis implementation) 与 PR #529(New IQ2_KT, IQ3_KT and IQ4_KT V2),了解 Trellis 量化在后来的演进。
【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考