十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ik_llama.cpp 的 IQ3_S 行交织优化:CPU Prompt Processing 提速 10 倍的原理与复现

ik_llama.cpp 的 IQ3_S 行交织优化:CPU Prompt Processing 提速 10 倍的原理与复现 人工智能大模型推理引擎本地部署模型量化模型优化【免费下载链接】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 #518《IQ3_S: much faster CPU prompt processing》展开讲解该项目如何通过行交织重打包row-interleaved repacking 统一 Q8 GEMM的技术路线将 IQ3_S 量化的 LLaMA-3.1-8B 在 Ryzen-7950X 纯 CPU 场景下的 Prompt ProcessingPP吞吐从主线条 llama.cpp 的约 28 t/s 提升到约 295 t/s。读完本文你将理解这类慢速解包量化类型在 CPU 上为何慢、ik_llama.cpp 用什么机制规避该瓶颈以及如何使用仓库自带的 sweep-bench 工具复现并验证这一性能差异。一、问题背景IQ 量化在 CPU 上慢在何处IQInteger Quantization系列是 ik_llama.cpp 中一组面向低比特的量化方案。以 IQ3_S 为例它属于约 3.44 bpwbits per weight的量化类型对应定义可见于 examples/quantize/quantize.cpp{ IQ3_S, LLAMA_FTYPE_MOSTLY_IQ3_S, 3.44 bpw quantization, }, { IQ3_S_R4, LLAMA_FTYPE_MOSTLY_IQ3_S_R4, IQ3_S repacked, },这类量化为了在极低比特下保持精度采用紧凑的码本与打包布局。代价是在 CPU 上执行矩阵乘法GEMM/GEMV前必须先把压缩后的权重解包unpack成可供int8_t点积直接消费的格式。解包过程涉及位运算、查表与缩放换算开销远高于直接读取普通量化数据。当左矩阵权重行数很多、右矩阵激活列数很多时解包成本会反复叠加Prompt Processing 这种大批量、大 ubatch的计算形态尤其敏感。PR #515IQ2_XXS 优化的作者即 ik_llama.cpp 维护者 ikawrakow在实验 trellis 量化时发现对于解包慢的量化类型与其反复对每个输出列执行慢速路径不如先把给定数量的行解包成行交织的 8-bit 类型如Q8_0_R8再一次性用该 8-bit 表示与右矩阵的全部列做 GEMM。PR #518 正是把这条思路推广到 IQ3_S。注意本 PR 的标题强调 CPU但该优化仅在特定指令集路径下生效。PR #515 明确说明该技术目前只对AVX2/Zen4启用IQ2_XS、IQ2_S、IQ3_S 等使用 16 元素块block of 16的类型需要新的块大小为 16 的行交织 8-bit 类型才能复用同一套方法这一点在 PR #515 的描述中被明确点出IQ3_S 正是块大小为 16 的类型。二、核心机制is_dequant_better 与行交织 Q8 重打包要理解 PR #518 做了什么最直接的证据在当前仓库的矩阵乘法调度代码中。ik_llama.cpp 的 CPU 端 IQK 乘法内核位于 ggml/src/iqk/iqk_mul_mat.cpp其中is_dequant_better()函数决定了某个量化类型在给定条件下是否应该先被转成 Q8 表示再做乘法static inline ggml_type is_dequant_better(ggml_type type, int nrc_y) { #ifdef __AVX2__ #ifdef HAVE_FANCY_SIMD auto q8_k_type GGML_TYPE_Q8_K_R16; #else auto q8_k_type GGML_TYPE_Q8_K_R8; #endif switch (type) { ... case GGML_TYPE_IQ3_S : return nrc_y 32 ? q8_k_type : type; case GGML_TYPE_IQ3_S_R4: return nrc_y 32 ? q8_k_type : type; case GGML_TYPE_IQ1_S : return nrc_y 32 ? q8_k_type : type; ... } }见 ggml/src/iqk/iqk_mul_mat.cpp从中可以读出几层关键信息触发条件与阈值当右矩阵的行数nrc_y即激活侧参与乘法的规模大于等于 32 时IQ3_S/IQ3_S_R4会返回一个行交织的 Q8 类型而不是原类型nrc_y 32时保持原类型走慢速解包路径。这与 PR #515/#517 描述的策略一致——批量足够大时重打包的固定开销才值得摊薄。目标类型随 SIMD 能力变化编译时定义了HAVE_FANCY_SIMD对应 Zen4 等具备更强 SIMD 指令的 CPU时使用Q8_K_R1616 行交织的 Q8_K否则回退到Q8_K_R88 行交织。也就是说同一份 IQ3_S 模型在不同 CPU 上会得到不同的运行时重打包目标。该方法覆盖面广除IQ3_S外IQ2_XXS、IQ2_XS、IQ2_S、IQ3_XXS、IQ1_S、IQ1_M以及大量 k-quantsIQ2_KS、IQ3_K、IQ4_K等都出现在同一张调度表中。可以推断PR #518 的技术后来被扩展成了 ik_llama.cpp 中一套通用的行交织重打包加速基础设施而非 IQ3_S 专属的临时优化。行交织类型Row-Interleaved是什么Q8_K_R8/Q8_K_R16中的R8/R16表示行交织因子把连续 8 行或 16 行权重按行交错排布成一个连续的 8-bit 块使解包后的数据在内存布局上更利于向量化点积与缓存利用。对应的 GEMM 内核如Q8_0_R8 x Q8_2_X4在 ggml/src/iqk/iqk_gemm_iquants.cpp 中有具体实现其中iqk_convert_iq3_s_q8_k_r8将 IQ3_S 转为 8 行交织 Q8_K等转换函数负责在运行时完成重打包case GGML_TYPE_IQ3_S : iqk_convert_iq3_s_q8_k_r8 (n, vx, bx, vy, nrc_x); break; case GGML_TYPE_IQ3_S_R4: iqk_convert_iq3_s_r4_q8_k_r16(n, vx, bx, vy, nrc_x); break;见 ggml/src/iqk/iqk_gemm_iquants.cpp这一套运行时重打包 统一 Q8 GEMM的机制正是 PR #518 在代码中的最终落点把 IQ3_S 的慢速解包开销从每个输出列都付一次变成按批付一次固定转换成本后续所有列的计算都走高速的 Q8 点积路径。三、基准测试PR #518 的实测收益PR #518 的作者使用 sweep-bench 对 LLaMA-3.1-8B 的 IQ3_S 量化在Ryzen-7950X CPU上做了全上下文扫描并同**主线条 llama.cppbuild 5635commit 3069e3169**做了同条件对比。PR #518ik_llama.cpp实测结果PPTGN_KVT_PP sS_PP t/sT_TG sS_TG t/s51212801.733295.368.23915.545121285121.805283.628.39815.2451212810241.857275.738.56114.9551212815361.905268.748.43015.1851212820481.954261.978.56314.95主线条 llama.cppbuild 5635实测结果PPTGN_KVT_PP sS_PP t/sT_TG sS_TG t/s512128018.26128.047.93316.1451212851218.70827.378.33515.36512128102419.04826.888.54714.98512128153619.48026.288.73914.65512128204819.67026.038.91214.36结果解读PPPrompt Processing空 KV 缓存时S_PP 从 28.04 t/s 提升到 295.36 t/s约 10.5 倍即便 N_KV 增长到 2048仍有 261.97 t/s 对 26.03 t/s 的差距约 10 倍。这正是 PR 标题10X faster PP的数据来源。TGToken GenerationS_TG 两者基本持平15.54 vs 16.14 t/s说明该优化只作用于解包/GEMM 密集的批量计算路径对单 token 生成的 GEMV 形态没有显著收益也没有带来回退。随 N_KV 增大 PP 略有下降这是注意力KV cache 读取成本随上下文增长的正常现象两个实现表现一致。需要强调的边界上述数据来自 PR 提交时的特定软硬件组合Ryzen-7950X、AVX2/Zen4 路径、LLaMA-3.1-8B、PP512 的 ubatch 扫描实际加速比会随 CPU 指令集、批大小与模型架构变化不能外推为所有环境下的恒定倍数。四、如何使用 sweep-bench 复现对比PR 作者使用的 sweep-bench 工具就位于本仓库 examples/sweep-bench。它会在整个上下文上按 ubatch 窗口滑动依次测量每个窗口内的 prompt processing 与 token generation 性能从而可视化性能随上下文长度如何变化而不是把整个上下文平均成一个数。构建与运行在项目构建完成后运行示例命令来自 examples/sweep-bench/README.md./llama-sweep-bench -c 8704 -ub 512 -m models/Meta-Llama-3.2-3B-Instruct-Q8_0.gguf参数含义参数含义-c上下文长度context size决定扫描的总窗口数-ububatch 大小即每个扫描窗口的批处理规模-m模型路径GGUF 格式sweep-bench 的扫描流程是在每个 ubatch 窗口内先生成ubatch/4个 token 并测量生成性能然后从 KV cache 中移除这些 token准备一批随机 token 做 prompt processing 并测量其性能。输出表中的字段含义见 examples/sweep-bench/README.mdPP每 ubatch 的 prompt token 数TG每 ubatch 的生成 token 数N_KV当前 KV cache 大小T_PPprompt processing 耗时即首个 token 的等待时间S_PPprompt processing 速度(B*PP)/T_PP或PP/T_PPT_TG生成全部 batch 的耗时S_TG生成速度(B*TG)/T_TG如果希望输出机器可读的 JSONL 格式便于脚本汇总与绘图可追加--output-format jsonl每行包含n_kv_max、n_batch、n_ubatch、flash_attn、n_gpu_layers、n_threads、pp、tg、n_kv、t_pp、speed_pp、t_tg、speed_tg等字段。复现 PR #518 对比的要点准备 IQ3_S 量化模型使用仓库的 examples/quantize/quantize.cpp 把 FP16/FP32 GGUF 转为IQ3_Stype参数填IQ3_S约 3.44 bpw。如需降低量化误差可用--imatrix file_name传入重要性矩阵。同一模型、同一机器上分别跑两套二进制一套为当前 ik_llama.cpp 构建另一套为主线条 llama.cpp如 PR 中使用的 build 5635保持-c、-ub与模型一致直接对比两个表中的S_PP列。确认 CPU 路径生效优化依赖__AVX2__以及可选HAVE_FANCY_SIMD纯 CPU 推理时应确保未使用-nglGPU 层或将全部层留在 CPU。关于行交织重打包与 GPU 混用的注意事项见下一节。五、使用前提与注意事项指令集前提行交织重打包加速在源码中由__AVX2__条件编译控制ggml/src/iqk/iqk_mul_mat.cpp即至少需要 AVX2 指令集Zen4 等支持更先进 SIMDHAVE_FANCY_SIMD的 CPU 会进一步使用 16 行交织Q8_K_R16路径。批量阈值前提只有当右矩阵行数nrc_y 32时IQ3_S才会走重打包路径小批量场景仍走原类型解包路径。可以推断这是为了避免在批量过小时为摊薄固定转换成本而得不偿失。模型加载时的重打包-rtr与 CPU/GPU 混用本仓库 README 明确指出对于 MoE 模型如果部分专家留在 CPU不要轻易使用-rtr——该选项会在加载模型时把所有留在 RAM 的张量重打包为行交织格式而并非所有量化类型都有对应的 GPUCUDA行交织实现可能导致本该卸载到 GPU 的矩阵乘法被强制留在 CPU 上反而降低 prompt processing 速度。PR #518 讨论的机制属于运行时按需重打包is_dequant_better调度与-rtr的加载期全量重打包是两条不同的路径。量化类型形态IQ3_S原版与IQ3_S_R4repacked 变体都出现在is_dequant_better的调度表中说明两个变体均可受益IQ3_S_R4在 examples/quantize/quantize.cpp 中被标注为 IQ3_S repacked即预先以行交织布局存储的版本。六、从 PR #518 看 ik_llama.cpp 的优化方法论PR #518 是 ik_llama.cpp 一个连续优化系列#515 → #516 → #517 → #518的组成部分同系列分别覆盖了IQ2_XXSPP 提升近 3 倍且优于既有IQ2_XXS_R4和IQ1_SPP 提升近 2 倍等低比特量化。从当前仓库源码看这套机制已经沉淀为iqk_mul_mat.cpp中一张覆盖十余种量化类型的统一调度表属于项目量化 性能路线上的基础能力。其方法论可以概括为三步识别瓶颈低比特量化尤其是 trellis 类紧凑码本在 CPU 上的主要成本是解包而非点积本身。转换视角与其优化每种类型的解包指令序列不如把慢速类型先解包成快速 8-bit 表示再用统一 GEMM 吃掉右矩阵所有列把成本从 O(列数 × 解包) 摊薄为 O(解包 列数 × 高速点积)。阈值化调度用nrc_y批量规模作为开关让大小批量各走各的最优路径同时按 SIMD 能力选择不同的行交织目标类型R8/R16兼顾兼容性与极致性能。对于关注 CPU 推理吞吐的读者理解这一模式也有助于判断何时该选择IQ3_S、IQ1_S这类低比特类型它们的收益高度依赖运行时的批量形态与指令集环境建议用 examples/sweep-bench 在自己机器上做一次全上下文扫描后再下结论。赞分享人工智能大模型推理引擎本地部署模型量化模型优化【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp点击查看免费下载相关推荐ik_llama.cpp 的 IQ4_NL_R4原名 IQ4_NL_X4交错行重打包让 Prompt 处理提速 1.5 倍的原理与实践ik_llama.cpp 的 IQ4_NL_R4原名 IQ4_NL_X4交错行重打包让 Prompt 处理提速 1.5 倍的原理与实践 本篇文章聚焦 ik人工智能大模型推理引擎本地部署模型量化模型优化ik_llama.cpp CPU Prompt Processing 加速一基于 Q8_K_R8 运行时重打包的量化矩阵乘法优化ik_llama.cpp CPU Prompt Processing 加速一基于 Q8_K_R8 运行时重打包的量化矩阵乘法优化 导读 本文围绕 ik_l人工智能大模型推理引擎本地部署模型量化模型优化embedmd告别代码复制粘贴让Markdown文档与源代码自动同步的终极工具embedmd告别代码复制粘贴让Markdown文档与源代码自动同步的终极工具 embedmd是一款专为开发者打造的实用工具能够将文件或文件片段嵌入到Ma人工智能大模型推理引擎本地部署模型量化模型优化上一篇MariaDB并行复制性能优化从单线程到多线程的飞跃下一篇Brotli性能基准测试终极指南research目录中的专业测评工具详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表