ik_llama.cpp CUDA 量化 GEMMMMQ补全IQ2_KS / IQ2_K / IQ3_K 内核实现与性能解析【免费下载链接】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 #418 展开该 PR 为IQ2_KS、IQ2_K、IQ3_K三种IQX_K系量化类型补上了 CUDA 上的原生量化矩阵乘法Quantized Matrix Multiplication简称 MMQ内核使 prompt processingPP不再需要先把权重反量化到 fp16 再交给 cuBLAS 计算。读完本文你将理解 MMQ 相对反量化 cuBLAS路径的优势、为什么IQ2_KS能拿到约 35% 的性能提升而IQ2_K/IQ3_K只有约 10%、以及该系列工作在仓库源码mmq.cu、mmq.cuh、ggml-common.h中的实际落地形态与后续演进。一、背景从反量化 cuBLAS到原生量化 GEMM在 MMQ 系列工作落地之前CUDA 上处理IQX_K这类新量化类型的矩阵乘法采用的是间接路径先把量化权重反量化dequantize为 fp16再调用 cuBLAS 执行矩阵乘法。这条路径存在三个痛点性能损失多了一次显存驻留的反量化中间张量读写且 cuBLAS 对这类非标准量化布局没有原生支持数值问题PR #417 中明确提到DeepSeek 系列模型在使用 fp16 算术时观测到数值问题反量化到 fp16 再计算会放大这类风险显存开销反量化后的中间缓冲区需要额外占用 CUDA compute buffer。PR #374 启动了该系列的第一阶段工作PR #417IQ4_K、IQ5_K、IQ6_K作为第二阶段实测带来了约 15% 的性能提升。PR #418 则是该系列的第三阶段目标是几乎完成IQX_K全系的 MMQ 实现——它在 #417 的基础上补齐了IQ2_KS、IQ2_K、IQ3_K仓库 README 的 Quantization 章节也将这三个类型明确记录为 PR #418 的 CUDA 实现成果见 README.md。PR #417 的对比实验环境为模型 LLaMA-3.1-8B、GPU RTX-4080所有量化均使用--output-tensor-type q6_K --pure生成。本文引用这些数字时均沿用 PR 原文口径。二、PR #418 的核心内容2.1 完成了什么PR #418 为IQ2_KS、IQ2_K、IQ3_K提供了 CUDA 量化 GEMM 内核。作者明确表示这是 #417 的后续补完后IQX_K系列中唯一缺失的只剩IQ4_KSS——而作者决定不再为IQ4_KSS实现 MMQ原因是其数据打包packing过于复杂实现成本与收益不成比例。2.2 性能收益与原因PR 原文给出的关键数据量化类型性能提升幅度原因IQ2_KS约35%块大小block size为 32可以直接复用更高效的 GEMM 内核IQ2_K约10%块大小为 16只能使用较低效的 16 元素内核IQ3_K约10%同上块大小为 16作者特别指出IQ2_KS之所以收益明显更大正是因为它的块大小为 32能够套用与Q4_0相同的type-0内核路径而IQ2_K/IQ3_K属于块大小 16 的类型只能走较慢的内核详见 #417 中的讨论。2.3 顺带提出的新构想基于 #417 与 #418 两张性能对比图作者在 PR 中表达了下一步构想考虑新增IQ3_KS和IQ5_KS分别作为 3-bit 与 5-bit、块大小为 32 的量化类型从而让更多低比特量化吃到 32 块内核的性能红利。这一构想在其后的版本中确实演化为一系列新量化类型IQ2_KS/IQ3_KS/IQ4_KS/IQ5_KS家族README 中均有记录但本文不将其作为 PR #418 本身的已交付成果描述。三、源码落地验证MMQ 在仓库中的真实实现PR #418 虽然标注为 Closed但其成果已经合入当前仓库。下面从源码层面逐层验证。3.1 分派入口mmq.cuMMQ 的 GEMM 路径入口是 mmq.cu 中的ggml_cuda_op_mul_mat_q它根据src0的张量类型走switch分派。在类型分支中可以看到case GGML_TYPE_IQ2_KS: mul_mat_q_caseGGML_TYPE_IQ2_KS(ctx, args, stream); break; case GGML_TYPE_IQ2_K: mul_mat_q_caseGGML_TYPE_IQ2_K(ctx, args, stream); break; case GGML_TYPE_IQ3_K: mul_mat_q_caseGGML_TYPE_IQ3_K(ctx, args, stream); break;见 mmq.cu这些分支与 PR #418 的目标类型一一对应说明其实现已稳定合入主线。3.2 内核模板实例template-instances每种类型的 MMQ 内核实例由模板文件自动生成。仓库 ggml/src/ggml-cuda/template-instances 下存在mmq-instance-iq2_ks.cu→DECL_MMQ_CASE(GGML_TYPE_IQ2_KS);mmq-instance-iq2_k.cu/mmq-instance-iq2_k_id.cummq-instance-iq3_k.cu/mmq-instance-iq3_k_id.cu这些文件头部注明autogenerated by generate_cu_files.py即由 generate_cu_files.py 生成说明内核代码是模板化、可扩展的——后续新增IQ3_KS、IQ5_KS等类型时只需在生成脚本中登记即可。3.3 Tile 布局选择mmq.cuhMMQ 性能差异的根源在于 tile 布局。在 mmq.cuh 中mmq_get_dp4a_tile_x_sizes与mmq_get_mma_tile_x_k为每种量化类型选择共享内存 tile 的组织方式IQ2_KS映射到MMQ_DP4A_TXS_Q8_0与MMQ_MMA_TILE_X_K_Q8_0——即与Q4_0、Q8_0相同的32 元素友好型 tile这正是 PR 中所说块大小为 32、能使用更高效 GEMM 内核的代码级体现IQ2_K/IQ3_K在 dp4a 路径映射到MMQ_DP4A_TXS_Q8_0_16在 MMA 路径映射到MMQ_MMA_TILE_X_K_Q3_K即Q3_K的 16 元素 tile——与 PR 所述块大小为 16一致。此外 mmq.cuh 定义了三个关键编译期常量MMQ_DP4A_MAX_BATCH_SIZE 64dp4a 内核的最大 batch 上限、MMQ_ITER_K 256、MMQ_NWARPS 8它们共同决定了内核的 tile 遍历与 warp 组织方式。3.4 何时走 MMQggml_cuda_should_use_mmqmmq.cu 中的ggml_cuda_should_use_mmq决定了给定类型、计算能力与 batch 大小时是否启用 MMQ。从当前源码可以归纳其决策逻辑若定义了GGML_CUDA_FORCE_CUBLAS则一律不走 MMQ强制走 cuBLAS 反量化路径类型必须位于 MMQ 支持清单中——IQ2_KS、IQ2_K、IQ3_K均在其中若定义了GGML_CUDA_FORCE_MMQ则强制走 MMQ否则在 NVIDIA 侧Volta 之前的架构或 batch 列数ne11 MMQ_DP4A_MAX_BATCH_SIZE时使用 MMQTuring 及以上架构在大 batch 场景下则回退到 fp16 cuBLAS借助临时缓冲区以规避 dp4a 内核在大 batch 时的劣势。这一逻辑也解释了为什么 MMQ 主要服务于 prompt processingPPbatch 较大但仍在 64 以内与 token generationTG场景。用户可以通过编译选项GGML_CUDA_FORCE_MMQ/GGML_CUDA_FORCE_CUBLAS在两个路径之间强制切换。3.5 量化块的数据结构ggml-common.h三种类型的底层 block 布局定义在 ggml-common.h// IQ2_KS块大小为 32 的 2-bit 量化 typedef struct { uint16_t extra; uint8_t scales[QK_K/64]; uint8_t qs[QK_K/4]; // 每个值 2 bit } block_iq2_ks; // IQ2_K2-bit 量化 typedef struct { ggml_half d; uint16_t extra; uint8_t scales[QK_K/32]; uint8_t qs[QK_K/4]; } block_iq2_k; // IQ3_K3-bit 量化 typedef struct { ggml_half d; uint16_t extra; uint16_t scales_h; uint8_t scales_l[QK_K/32]; uint8_t qs[QK_K/4]; // 低 2 bit uint8_t qh[QK_K/8]; // 高 1 bit } block_iq3_k;从布局可以看出IQ2_KS的 scale 粒度更粗QK_K/64即 64 个值共享 1 字节 scale内含 2 个 4-bit 半字节、无逐块d缩放属于为 32 元素子块设计的紧凑格式而IQ2_K/IQ3_K携带ggml_half d全局缩放与更细的 scale 数组解包成本更高。MMQ 内核的load_tiles_*函数需要把这些紧凑位域展开为int8_t瓦片供矩阵乘使用展开unpacking成本的高低正是 PR 中反复强调的性能差异来源之一。四、性能差异的机理块大小 16 vs 32 与解包成本PR #417 的图表RTX-4080、LLaMA-3.1-8B、--output-tensor-type q6_K --pure给出了一个清晰的性能分层PR #418 与之同源可直接用于理解 #418 的结果Q4_0最快使用块大小 32 的type-0内核且Q4_0结构极其简单解包成本几乎为零IQ4_KS与Q4_0共用同一内核但因解包成本更高性能低约 10%Q3_K解包成本低但受限于块大小 16 的内核性能相比Q4_0下降约 30%IQ4_K/IQ5_K/IQ6_K即 PR #417 新增同样是块大小 16 内核叠加更高的解包成本比Q3_K再慢约 7%9%。对照来看PR #418 中IQ2_KS的约 35% 提升正是因为它跳出了块 16 高解包成本的组合直接落入Q4_0那条块 32 高效内核路径而IQ2_K/IQ3_K仍受块 16 内核约束只能获得约 10% 的提升主要来自省去 fp16 反量化与 cuBLAS 路径的中间开销。作者在 #417 中给出了两个面向未来的优化方向同样适用于理解 #418 的取舍摊销解包成本Q4_0在 llama.cpp 中受到大量关注块 32 内核大概率是为其特调的当解包成本很高时更合理的做法是让同一个 tile 被复用更多次把解包开销摊薄。作者提到这在 CPU 实现中已经做到多数量化类型与Q4_0持平甚至更快收窄 16/32 块差距块 16 带来约 30% 的性能落差被认为不合理——在 CPU 实现中块 16 的量化类型比块 32 最多慢约 10%。这暗示 CUDA 侧块 16 内核仍有特调空间。这两个方向与新增IQ3_KS/IQ5_KS3/5-bit、块 32的构想共同构成了该系列后续工作的路线图。五、实操如何构建、运行与验证5.1 构建 CUDA 版本MMQ 内核随 CUDA 后端默认编译。若需强制所有矩阵乘法走 MMQ例如对比测试、或在大 batch 下观察 dp4a 路径行为可在 CMake 配置阶段加入编译宏cmake -B build -DGGML_CUDAON -DCMAKE_CUDA_FLAGS-DGGML_CUDA_FORCE_MMQ cmake --build build --config Release反过来若希望完全禁用 MMQ、回归反量化 cuBLAS路径做对照实验可使用-DGGML_CUDA_FORCE_CUBLAS。5.2 选择合适模型与量化类型IQ2_KS、IQ2_K、IQ3_K属于IQX_K低比特家族。使用仓库自带工具可以完成量化与推理量化build/bin/llama-quantize 输入模型 输出模型 IQ2_KS类型名替换为IQ2_K/IQ3_K即可推理/PP 测试build/bin/llama-bench -m 模型 -p 512 -n 128观察 prompt processing 的 token/s混合量化与逐层对比可参考 examples/sweep-bench 的 sweep 脚本思路。注意IQ2_KS/IQ2_K/IQ3_K均为较新的 GGUF 量化类型需确认所用转换/量化工具版本与此仓库一致GGUF 版本兼容性以仓库当前实现为准。5.3 如何确认 MMQ 生效通过GGML_CUDA_FORCE_MMQ与GGML_CUDA_FORCE_CUBLAS两种构建分别跑llama-benchPP 吞吐的差异即 MMQ 的收益在日志开启-v或环境变量时可观察是否存在 fp16 反量化中间缓冲区的分配/释放日志MMQ 路径下这部分开销应消失。六、后续演进MMQ 系列的完整脉络PR #418 不是终点。仓库 README 的量化章节README.md记录了该系列在 CUDA 侧的完整落地序列PR #417IQ4_K、IQ5_K、IQ6_KPR #418IQ2_KS、IQ2_K、IQ3_K本文主题其后的 PR #461IQ2_K_R4等_R4家族、#462 / #493IQ4_KS_R4、IQ5_KS_R4、#492 / #494IQ1_S_R4、IQ1_M_R4等把 MMQ 覆盖范围进一步扩展到行交织row-interleaved与 1-bit 量化性能侧的微调也没有停止PR #468 针对iq2_ks的 TG 性能做了约 2% 的 CUDA 改进README 中另有记录README 总结称所有量化类型现在都拥有 CUDA 量化矩阵乘内核见 README.mdPR #418 正是这条全类型 MMQ路线上的关键一环。结合 PR #418 作者本人的表述与上述演进可以推断块 32 的KS系列含后来出现的IQ2_KS/IQ3_KS/IQ4_KS/IQ5_KS在 CUDA 上拥有系统性性能优势是低比特推理在 NVIDIA 平台上的推荐关注方向。七、总结PR #418 为 ik_llama.cpp 补齐了IQ2_KS、IQ2_K、IQ3_K的 CUDA 量化 GEMM使IQX_K全系除打包过于复杂的IQ4_KSS都能绕过fp16 反量化 cuBLAS间接路径直接以量化数据完成矩阵乘法。其核心结论可归纳为三点收益来自内核路径的选择块大小 32 的IQ2_KS复用Q4_0高效内核拿到约 35% 的 PP 提升块大小 16 的IQ2_K/IQ3_K受限于较慢内核约 10%源码已完整落地分派逻辑mmq.cu、tile 布局mmq.cuh、block 结构ggml-common.h与模板实例均可直接查阅验证路线图清晰块 32 的KS家族与解包成本摊销是后续优化的两个主攻方向这一判断已被仓库后续 PR 的演进所印证。对于希望在 NVIDIA GPU 上以 23 bit 低比特运行模型的用户优先关注IQ2_KS系列并保持 MMQ 路径开启是当前仓库代码下性价比最高的选择。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考