网站首页 > 技术文章 正文
本文将为你介绍如何利用 Arm i8mm 指令,具体来说,是通过带符号 8 位整数矩阵乘加指令 smmla,来优化 llama.cpp 中 Q6_K 和 Q4_K 量化模型推理。
llama.cpp 量化
llama.cpp 是一个开源的 C++ 库,用于运行大语言模型 (LLM),针对加速 CPU 推理进行了优化。通过量化等技术(例如 8 位或 4 位整数格式)来减少内存占用并加快计算速度,从而实现在消费级和服务器级硬件上高效部署模型。
llama.cpp 支持多种量化方式。量化可在模型精度和性能之间取得平衡。数据量越小,推理速度越快,但可能会因困惑度升高而致使精度降低。例如,Q8_0 采用 8 位整数表示一个数据点,而 Q6_K 则将数据量缩减至 6 位。
量化以块为单位进行,同一个块中的数据点共享一个缩放因子。例如,Q8_0 的处理以 32 个数据点为一个块,具体过程如下:
从原始数据中提取 32 个浮点值,记为 f[0:32]
计算绝对值的最大值,即 mf = max(abs(f[0:32]))
计算缩放因子:scale_factor = mf / (max(int8)) = mf / 127
量化:q[i] = round(f[i] / scale_factor)
反量化:v[i] = q[i] * scale_factor
Q6_K 则更为复杂。如下图所示,数据点分为两个层级:
一个超级块包含 256 个数据点,并对应一个浮点格式的超级块缩放因子
每个超级块由 16 个子块组成。每个子块包含 16 个数据点,这些数据点共享一个整数格式的子块级缩放因子。
图 1:Llama.cpp Q6_K 量化
利用 Arm i8mm 指令
优化 llama.cpp
与大多数人工智能 (AI) 工作负载相同,在 LLM 推理过程中,大部分 CPU 周期都耗费在矩阵乘法运算上。Arm i8mm(具体是指 smmla 指令)能够有效加速 8 位整数矩阵乘法运算。
为了说明 smmla 指令的作用及其高效性,假设我们要对下图中的两个矩阵进行乘法运算。
图 2:矩阵乘法
按照教科书上的方法,我们可以逐一计算输出矩阵中的四个标量,即第一个输出标量是矩阵 x 的第一行与矩阵 y 的第一列的内积。依此类推,需要进行四次内积运算。
还有一种更高效的方法,即外积法。如下图所示,我们可以用矩阵 x 的第一列乘以矩阵 y 的第一行,一次性得出四个部分输出标量。将这两个部分输出相加就能得到结果,这样只需要两次外积运算即可。
图 3:外积
smmla 指令实现了向量级别的外积运算,如下图所示。请注意,vmmlaq_s32 是实现 smmla 指令的编译器内建函数。
每个输入向量 (int8x16) 被拆分为两个 int8x8 向量
计算四对 int8x8 向量的内积
将结果存储到输出向量 (int32x4) 的四个通道中
图 4:smmla 指令
借助 smmla 指令,我们可以通过同时处理两行和两列来加速矩阵乘法。如下图所示,计算步骤如下:
从矩阵 x 中加载两行数据 (int8x16) 到 vx0 和 vx1,从矩阵 y 中加载两列数据到 vy0 和 vy1
对 vx0 和 vx1 进行“压缩”操作,将这两个向量的下半部分合并为一个向量,上半部分合并为另一个向量。这是确保 smmla 指令正确工作的必要步骤。对 vy0 和 vy1 执行相同操作
使用两条 smmla 指令计算四个临时标量结果
处理下一个数据块并累积临时结果,直到处理完所有数据
图 5:使用 smmla 指令进行矩阵乘法
我们利用 smmla 指令对 llama.cpp 的 Q6_K 和 Q4_K 矩阵乘法内核进行了优化,并在 Arm Neoverse N2 平台上进行了测试,观察到性能有显著提升。下图展示了 Q6_K 优化前后 llama.cpp 的性能对比,其中:
S_TG 代表词元生成速度,数值越高代表性能越好
S_PP 代表提示词预填充速度,数值越高代表性能越好
图 6:Arm i8mm 提升 llama.cpp Q6_K 模型性能
猜你喜欢
- 2025-08-06 什么是高级驾驶辅助系统:ADAS 概述
- 2025-08-06 C语言实现:见缝插针游戏!代码思路+源码分享
- 2025-08-06 机器人首次打通视觉感知与运动断层,华人博士让宇树G1现场演示
- 2025-08-06 DBSCAN聚类算法的理解与应用
- 2025-08-06 人形机器人首次打通视觉感知与运动断层,UC伯克利华人博士让宇树G1现场演示
- 2025-08-06 小美的陡峭值操作【C++实现】
- 2025-08-06 C++性能优化:分支预测
- 2025-05-22 嵌入式C语言常用的5类预处理
- 2025-05-22 微软开源“原生1bit”三进制LLM:2B参数,0.4GB内存/单CPU就能跑
- 2025-05-22 新代数控车宏程序说明
- 08-06中等生如何学好初二数学函数篇
- 08-06C#构造函数
- 08-06初中数学:一次函数学习要点和方法
- 08-06仓颉编程语言基础-数据类型—结构类型
- 08-06C++实现委托机制
- 08-06初中VS高中三角函数:从"固定镜头"到"360°全景",数学视野升级
- 08-06一文讲透PLC中Static和Temp变量的区别
- 08-06类三剑客:一招修改所有对象!类方法与静态方法的核心区别!
- 1531℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 696℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 536℃MySQL service启动脚本浅析(r12笔记第59天)
- 502℃启用MySQL查询缓存(mysql8.0查询缓存)
- 500℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 487℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 469℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 467℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- windowsscripthost (69)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (70)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)