ik_llama.cpp 为 IQ1_S 实现优化的 GEMM/GEMV:直接运行 Unsloth DeepSeek-R1 量化模型的加速实践 ik_llama.cpp 为 IQ1_S 实现优化的 GEMM/GEMV直接运行 Unsloth DeepSeek-R1 量化模型的加速实践【免费下载链接】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 #212「Optimized GEMM/GEMV for IQ1_S」讲解如何在不重新量化的前提下为 Unsloth 发布的IQ1_SDeepSeek-R1 模型提供原生高性能的矩阵乘GEMM/GEMV支持。文章将带你梳理该 PR 的技术动机、iqk_mul_mat.cpp中的实现路径与源码细节、作者在 Zen4 / AVX2 / NEON 三种 CPU 平台上的实测数据以及它与此前主推的IQ1_S_R4量化方案之间的关系。读完后你将理解为什么IQ1_S的 GEMM/GEMV 会比主线程实现快 1.05~3.49 倍以及如何在该仓库的 iqk 子系统中定位与验证这一能力。PR #212 概述为现成 IQ1_S 模型补齐高性能计算路径项目内容PR 标题Optimized GEMM/GEMV for IQ1_S作者ikawrakow状态❌ Closed创建时间2025-02-20更新时间2025-02-20该 PR 的动机非常直接有大量用户希望直接运行 Unsloth 发布的IQ1_SDeepSeek-R1 模型而不是先将其重新量化成本仓库主推的IQ1_S_R4格式。虽然IQ1_S_R4在模型质量与推理速度上都有明显优势但现成的IQ1_S模型文件有着更小的文件体积与更低的显存/内存占用对于只需要开箱即用的场景来说更具吸引力。为此作者在iqk_mul_mat.cpp中为IQ1_S提供了一套新的实现。这一点在仓库根目录的 README.md 中也有对应记录量化/性能特性清单里明确列出了Fast GEMM/GEMV forIQ1_S[PR 212]同时 README 还说明IQ1_S_R4的 CUDA 实现来自 PR 492可见整个 1-bit 量化家族IQ1_S/IQ1_S_R4/IQ1_M/IQ1_M_R4/IQ1_BN在 iqk 子系统中是成体系地被支持的。IQ1_S 量化与它的块大小 256约束IQ1_S属于极低比特1-bit 级别量化格式。在本仓库中与它相关的 GEMM/GEMV 内核被集中放在ggml/src/iqk/目录下其中负责 1-bit 量化计算的文件是 ggml/src/iqk/iqk_gemm_1bit.cpp。从iqk_set_kernels_1bit的分发逻辑可以看到GGML_TYPE_IQ1_S要求ne00 % QK_K 0即权重列数必须是块大小QK_K的整数倍激活侧类型为Q8_K或Q8_2_X4GGML_TYPE_IQ1_S_R4要求ne00 % 128 0使用mul_mat_iq1_s_r4_q8_1内核且行交错因子为 4见MulMat::num_rows对GGML_TYPE_IQ1_S_R4返回 4。PR 文档特别指出一个实际使用中的约束IQ1_S的块大小为 256因此当某些张量的行大小不是 256 的整数倍时这些张量无法按IQ1_S存储会退化为按IQ4_NL量化。这意味着在实际部署时模型文件中的纯IQ1_S性能并非在所有张量上都成立——这正是作者在测试说明中强调我们并没有测试到 100% 纯粹的IQ1_S性能的原因。源码剖析iqk_mul_mat 中 IQ1_S 的三条关键路径在 ggml/src/iqk/iqk_mul_mat.cpp 中IQ1_S被纳入 iqk 统一调度框架主要有三条路径1. 反量化更优判定is_dequant_betterggml/src/iqk/iqk_mul_mat.cpp#L238-L345 中的MulMat::is_dequant_better用于在直接计算与先反量化再计算之间做选择。对GGML_TYPE_IQ1_S在AVX2HAVE_FANCY_SIMD路径下当nrc_y 32时返回Q8_K类型实际为Q8_K_R8/Q8_K_R16否则保持IQ1_S直接计算在非 AVX2 路径下同样在nrc_y 32时改用Q8_K_R8。nrc_y表示一次处理的激活行数当激活批量足够大时先把IQ1_S权重反量化为Q8_K再做点积可以避免反复从查找表中重建权重值从而获得更高的吞吐而在小批量如解码阶段的单 tokennrc_y很小时则保持IQ1_S直接点积避免反量化开销。2. 权重转换iqk_convert_1bit_q80_r8在 ggml/src/iqk/iqk_mul_mat.cpp#L501-L508 中GGML_TYPE_IQ1_S与GGML_TYPE_IQ1_M被路由到iqk_convert_1bit_q80_r8转换函数实现于 ggml/src/iqk/iqk_gemm_1bit.cpp#L1980-L1988。该函数要求n % QK_K 0且nrc_x % 8 0将IQ1_S块转换为便于 SIMD 计算的Q8_K布局供后续 GEMM 使用。3. 核心 GEMM 内核mul_mat_iq1_s_q8_K真正性能密集的部分是 ggml/src/iqk/iqk_gemm_1bit.cpp#L793-L865 中的mul_mat_iq1_s_q8_K内核其关键设计包括16 位查找表iq1s_grid_us2048 项IQ1_S的每个 32-bit 子块通过qs量化索引qh高 3 位组合索引到预计算的 16 位权重值即iq1s_grid_us[qs[3] | ((qh[ib] 1) 0x700)]这类表达式把解码过程转化为一次查表 SIMD 重排符号位的 delta 处理qh的最高位0x8000标记符号代码用delta qh 0x8000 ? -9 : -7的方式对每个 2-bit 组施加 -9 或 -7 的偏移配合dpbusd有符号-无符号点积累加与dpwssd指令完成 8 组并行累加scale 与激活侧复用Q8nrc_y, block_q8_K模板类按nrc_y展开循环每个权重块可以同时服务多行激活每个块的 scale 通过GGML_FP16_TO_FP32(iq1s[ibl].d)还原最终结果乘上0.125f系数写入累加器HAVE_FANCY_SIMD分支在支持 AVX-512 或更新的指令集时使用_mm256_dpbusd_epi32/_mm256_dpwssd_epi32见 ggml/src/iqk/iqk_config.h#L43-L47 对HAVE_FANCY_SIMD的定义否则退化为_mm256_maddubs_epi16_mm256_madd_epi16的组合。另外该文件还包含 NEON__aarch64__分支对应的mul_mat_iq1_s_q8_K变体与iq1s_grid_us_neon查找表这解释了 PR 性能表中 M2-MaxNEON平台上的加速效果来源。测试方法论为何用 DeepSeek-Lite 作为替代PR 文档中作者明确说明了自己无法运行完整的 DeepSeek-R1671B因此选择DeepSeek-Lite 作为替代模型进行性能测试理由是它与 DeepSeek-R1 具有相同的模型架构DeepSeek 系列的 MLA 架构。测试平台覆盖三种典型的 CPU 指令集平台CPU指令集Zen4Ryzen-7950XAVX-512 / Fancy SIMDAVX2Ryzen-5975WXAVX2NEONApple M2-MaxARM NEON测试负载分为两类pp512512 token 的预填充prompt processing衡量 GEMM 吞吐与tg128128 token 的文本生成token generation衡量 GEMV 延迟。测试中还用到不同线程数2/4/8/16/32以观察扩展性。实测性能数据PR 文档报告下表完整摘录自 PR #212 文档对比了主线main与本 PR 实现PR在deepseek2 16B IQ1_S模型上的吞吐tokens/s与加速比。请注意这些数据由 PR 作者在其指定硬件平台上测得不同环境的结果会有差异请将其作为相对参考而非绝对指标。modelbackendthreadstestt/s (main)t/s (PR)Speedupdeepseek2 16B IQ1_SAVX232pp512209.49 ± 0.61484.99 ± 4.612.315deepseek2 16B IQ1_S2tg12812.13 ± 0.0115.74 ± 0.011.298deepseek2 16B IQ1_S4tg12821.26 ± 0.0126.29 ± 0.051.237deepseek2 16B IQ1_S8tg12830.85 ± 0.0736.24 ± 0.131.175deepseek2 16B IQ1_S16tg12840.04 ± 0.0142.00 ± 0.011.049deepseek2 16B IQ1_SZen416pp512142.33 ± 1.06496.32 ± 1.753.487deepseek2 16B IQ1_S2tg12814.15 ± 0.0219.08 ± 0.011.348deepseek2 16B IQ1_S4tg12824.34 ± 0.0131.31 ± 0.081.286deepseek2 16B IQ1_S8tg12835.64 ± 0.0142.48 ± 0.021.192deepseek2 16B IQ1_S16tg12844.37 ± 0.0847.84 ± 0.181.078deepseek2 16B IQ1_SNEON8pp51288.77 ± 0.30229.23 ± 1.532.582deepseek2 16B IQ1_S2tg12817.80 ± 0.0122.72 ± 0.001.276deepseek2 16B IQ1_S4tg12829.80 ± 0.1337.27 ± 0.241.251deepseek2 16B IQ1_S8tg12849.28 ± 0.0759.28 ± 0.271.203从数据中可以读出几个关键结论预填充pp512收益最大Zen4 上提速高达3.487 倍AVX2 为 2.315 倍NEON 为 2.582 倍。这是因为预填充阶段是典型的 GEMM 场景批量大、权重复用充分本 PR 的 SIMD 化点积与查表解码优势被完全释放。生成tg128收益随线程数递减线程从 2 增到 16加速比从 ~1.3 下降到 ~1.05~1.2。这与 GEMV 的内存带宽瓶颈特性一致——单 token 生成时计算密度低优化空间有限且线程增多后受限于内存带宽边际收益变小。三平台普遍受益无论是 x86 的 AVX2/Zen4 还是 ARM 的 NEONIQ1_S的 GEMM/GEMV 都有实质性提升说明这套实现是跨指令集可移植的。作者在文档中还提到一个后续优化方向我认为可以通过在计算时即时交错 4 行interleaving 4 rows on the fly做得更好但这留待以后再说。——结合源码中IQ1_S_R4的行交错因子为 4 可以看出这正是后来IQ1_S_R4量化与内核mul_mat_iq1_s_r4_q8_1所落实的思路把多行权重视为交错布局一次加载更多有效数据从而进一步摊薄解码与访存开销。社区反馈为什么用户需要这条路径PR 的对话区有一条来自用户godrosev的反馈恰好印证了该 PR 的实用价值用户并非不愿意使用IQ1_S_R4而是需要更小的文件体积与更低的内存占用其当前工作场景要求直接运行 Unsloth 现成的 DeepSeek-R1IQ1_S模型用户计划在工作完成后按照作者建议自行量化IQ1_S_R4并相信新量化方式IQ1_S_R4在质量与速度上都会有更好表现还会在 671B 的 R1 上回报测试结果。这段对话展示了该仓库中现成模型兼容路径IQ1_S与自量化最优路径IQ1_S_R4两条路线的分工前者保证开箱即用后者追求极致质量与速度二者共同覆盖了不同用户的实际需求。总结从 IQ1_S 到 IQ1_S_R4 的完整性能路线图PR #212 解决了手头只有现成IQ1_S模型时的性能痛点把IQ1_S的 GEMM/GEMV 从主线程实现提升到与IQ1_S_R4接近的量级同时保持文件与内存占用不变。结合本仓库现状可以归纳出两条清晰的路径直接运行现成IQ1_S模型启用本 PR 带来的 iqk 内核已合入 ggml/src/iqk/iqk_gemm_1bit.cpp 与 ggml/src/iqk/iqk_mul_mat.cpp无需重新量化即可获得最高约 3.5 倍的预填充加速与 1.05~1.35 倍的生成加速追求更高质量与速度按作者的推荐将模型重新量化为IQ1_S_R4行交错 4 行布局内核mul_mat_iq1_s_r4_q8_1在模型质量与推理性能上更进一步代价是需要自行完成量化流程。如果你正在评估 DeepSeek 系列模型的极低比特部署方案可以从这份 PR 及其源码实现出发在自己的硬件上复现 pp512 / tg128 两组基准再结合文件体积、显存占用与质量诉求在现成IQ1_S与自量化IQ1_S_R4之间做出选择。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考