☰
量化反而慢 47 倍,端侧选型要先看指令集
2026/11/2 2:35:26 网站建设 项目流程

量化反而慢 47 倍,端侧选型要先看指令集

摘要:买端侧设备,先看指令集,再看核数。

本文为本地实测记录,含完整命令与原始数据,可按步骤复现。

关键数据:同一个 512³ 的矩阵乘:fp32 用 0.02090 秒,int8 用 0.98182 秒。

我一直默认一件事:量化到 8 位,一定比 fp32 快。

这个默认在昨晚被推翻了。一台 4 核 ARM 小机器上,int8 矩阵乘比 fp32 慢了 47 倍。

不是慢一点,是慢到没法用。

① 先说这台机器是什么

它不是开发板,是一台二手智能小盒子,型号 OneThingCloud OEC,也就是常说的玩客云。我把它刷成了标准 Linux,当端侧试验机用。

先看身份,命令原文:

$uname-aLinux FN036.18.18.c1090-trim#1090 SMP ... aarch64

体系结构 aarch64,4 核。再看内存和盘:

内存3900MB total /2473MB available Swap0系统盘6.8G total5.2G used1.4G avail80% 数据盘 224G total 187G used 37G avail84% 温度43.75℃(空载) Python3.11.2(系统自带,无 numpy)

两个细节值得单独拎出来。

Swap 是 0。这台机器没有交换分区。内存吃到顶,内核直接 OOM 杀进程,中间没有减速带。很多人调参时习惯「先跑跑看,不够就用 swap」,在这类端侧设备上没有这个选项。

系统盘只剩 1.4G。装个带 CUDA 的运行时都不够,更别说编译工具链。所以它只能跑纯 CPU 的东西。

CPU 特性这一行是今天整篇文章的钥匙:

fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp

有 asimd、asimddp、asimdhp,没有 i8mm,没有 bf16,没有 SVE。

当时我没在意这一行。我是后来才回过神来的。

② 我以为量化一定更快,实测反了

端侧跑模型的标准套路是量化:fp32 太重,换成 int8,体积小四倍,算得也快。这是常识。

我按这个常识写了个基准脚本,三个尺寸、三种精度,全跑一遍:

fp32 matmul2560.00609s →5.508GFLOPS fp32 matmul5120.02090s →12.843GFLOPS int8 matmul5120.98182s →0.273GOPS fp16 matmul2560.21986s → 支持,但走软件路径

同一个 512³ 的矩阵乘:fp32 用 0.02090 秒,int8 用 0.98182 秒。

0.98182 / 0.02090 = 47 倍。

我第一反应是脚本写错了,把 dtype 传成了 object 之类。跑第二遍,数值一样。换矩阵尺寸再跑,还是这个方向。

然后我想起了前面那行 CPU 特性:这台机器的指令集里没有i8mm。

i8mm 是 ARMv8.6 引入的 int8 矩阵乘指令,专门给量化推理用的。有它,int8 走硬件矩阵通道,比 fp32 快数倍;没有它,numpy 只能走整数模拟路径——把一个 int8 乘加拆成若干条通用整数指令。

而这颗 A55 核有asimddp(点积指令),fp32 的向量路径是硬件加速的。

于是一个反直觉的结果:软件模拟的 int8,跑不过硬件加速的 fp32。

🔴 这里必须把口径写清楚,不然容易被读成「量化没用」:

这是numpy 层的验证。numpy 在无 i8mm 的机器上走的是它自己的整数内核,不等于 llama.cpp 里 GGUF Q8 的实际表现——llama.cpp 有自己的 QGEMM 实现,路径完全不同,未必走 numpy 这条。

所以能说的结论只有一句:在缺 i8mm 的设备上,「量化就变快」这条经验不成立,得实测。不能推广成「量化在端侧都没用」。

③ 同一个「4 核」,架构差 2.4 倍

前面那组对比里混了两个变量:精度和指令集。为了把变量压到一个,我另写了一个只用 Python 标准库的脚本——不 import numpy,不做任何优化,纯 CPython 循环。

两台机器,都是 4 核:

指标FN03 aarch64(Cortex-A55)对照机 x86_64倍数
标量 200 万次乘加0.4314 s0.1799 sx86 快 2.40×
标量吞吐4.636 Mops11.115 Mopsx86 快 2.40×
纯 Python 128³ 矩阵乘0.6038 s0.2829 sx86 快 2.13×
zlib 压缩吞吐151.81 MB/s207.01 MB/sx86 快 1.36×

同一句for循环,同一个 Python 版本语义,同一个核数,差 2.4 倍。

这组数字比任何跑分表都干净,因为变量只剩一个:架构。没有 GPU、没有 BLAS、没有编译优化开关,就是最笨的标量运算。

对照组那台是台老 x86 小主机,内存 7G,跑 Python 3.14.4。它装不上 numpy(venv 缺 ensurepip,No module named ensurepip),所以只能用标准库跑——反而正好,两边条件一致。

这里有个很多人会忽略的点:「4 核」这个参数,在跨架构时基本没有可比性。一颗 A55 和一颗 x86 大核,IPC 差一大截,主频也差一截,乘起来就是 2.4 倍。参数表上它们都写着「4 核」。

④ 后来我又踩了一次:大矩阵直接把机器跑死了

既然小数快,我顺手试了 2048³。理论算一下不复杂:2 × 2048³ = 17.2 GFLOP,按前面实测的 12.8 GFLOPS 推,大约 1.3 秒。

结果 10 分钟没跑完,我手动中止了。

一开始我以为脚本死锁。查下来不是——4G 内存、零 swap 的机器上跑 2048³,光一个 fp32 矩阵就 16GB,早就超出物理内存。numpy 进入分块 + 反复换入换出,实际吞吐从小矩阵的 12.8 GFLOPS 断崖掉下去。

这个「断崖」比那个「1.3 秒」有用得多。它划出了这类机器的真实算力边界:512³ 还能跑,2048³ 就跑不动了。中间不是平滑衰减,是掉下去。

我没拿到确切完成时间,因为是我掐掉的。这条如实记在这里,不补估算值。

⑤ 端侧 decode 的天花板是内存带宽,不是算力

前面三节都在讲「算得快不快」。但跑模型真正卡住的,是另一件事。

大模型 decode 阶段每生成 1 个 token,必须把权重整体读一遍。这是 memory-bound,跟矩阵乘的算力关系不大,跟内存带宽直接挂钩。

所以我把「读」单独测了,读的是真实字节,不是空转:

读100MB0.0770s →1.268GB/s 有效读带宽 读200MB0.1543s →1.266GB/s

稳定在 1.27 GB/s。有了这个数,tok/s 上限就能直接算:

权重体积tok/s 上限
100 MB12.98
200 MB6.48

这是物理天花板。模型再优化,只要权重有 200MB,这台机器就上不了 6.5 tok/s。想更快,唯一的路是减权重量——也就是量化,而量化恰恰是第二节里那个变慢的方向。

这就是端侧的真实困境:要么权重太大读不完,要么量化了算不动。

顺手把存储也测了,因为权重加载走的是盘:

写 256MB541MB/s 读 256MB direct1.1GB/s 读 256MB buffered1.0GB/s

direct 和 buffered 只差 10%。说明这块闪存的页缓存几乎帮不上忙——读权重的时候,缓存层没有加速空间,就是老老实实按 1.1 GB/s 算。

对比我昨天在 DGX Spark 的 NVMe 上测到的 8.9 GB/s,差 8 倍以上。同样一份 100GB 权重,这边要 91 秒,那边 11 秒。

⑥ 落到你身上:这台机器能干什么,不能干什么

现在把这堆数字翻译成人话。

这台机器跑不了任何主流对话模型。一个 7B 模型量化到 Q4 大约 4GB,比它 3.9GB 的总内存还大,且它零 swap——直接 OOM。差的那一点不是「调调参数就能塞进去」,是物理上不存在。

它也不是完全没用。按 1.27 GB/s 的读带宽和 4 核算力,它能干的是:几百 MB 级的小模型、关键词抽取、文本分类、简单的本地 ASR 后处理。上限就在 200MB 权重 / 6.5 tok/s 这一档。

更重要的是一条选型结论:买端侧设备,先看指令集,再看核数。

电商页面会告诉你几核、几 GHz、多少 GB 内存。它不会告诉你有没有 i8mm。而这台机器实测出来的教训是——缺 i8mm 时,量化这条路会反向,你按「int8 快四倍」做的容量规划,落地可能慢 47 倍。

一颗带 i8mm 的 4 核,实战里可能打赢一颗没有 i8mm 的 8 核。这个参数不在商品页上,得去查 CPU 的 feature flags——cat /proc/cpuinfo那一行,就是买之前的尽调。

⑦ 这组数据的边界

三件事必须说清,不然容易被当成普适结论转发:

第一,int8 慢 47 倍是 numpy 层的结论。它证明的是「在这台缺 i8mm 的机器上,numpy 的 int8 内核比 fp32 慢」。llama.cpp 的 QGEMM 是另一条路径,没测过,不下判断。

第二,跨架构对照只有两台机器。一台 4 核 A55、一台 4 核 x86。它能说明「同核数跨架构可以差 2.4 倍」,不能说明「所有 ARM 都这样」——带 i8mm 的 ARMv8.6 设备是另一回事。

第三,Kimi Linear 那个模型我没跑成。它权重 98.25GB,这台机器数据盘只剩 37GB,装不下。这是体积层面的确定出局,不是技术失败,也不是我没试。

参考来源

  • 全部数据来自本机实测:端侧机 FN03(OneThingCloud OEC / 4 核 aarch64 / 3900MB / 零 swap),2026-11-01 00:00-00:38 采样
  • 跨架构对照组:x86_64 小主机(4 核 / 7G / Python 3.14.4),同一脚本、纯标准库
  • 算力基准使用 numpy 2.4.6(端侧沙箱 venv);跨架构对照刻意不用 numpy,避免库实现差异
  • CPU 指令集特性来自cat /proc/cpuinfo的 Features 行原文
  • 存储对照:DGX Spark NVMe 8.9 GB/s 为 2026-10-31 本机 dd 实测

声明

本文所有数字来自真机实测,命令原文与原始输出均已给出,可自行复跑核对。未能测到的部分(2048³ 完成时间、llama.cpp 实际 tok/s、Kimi Linear 本体)已在文中逐条标注为未测,未用估算值代替。不同固件、不同内核版本下具体数值会有差异,判断方法不变。


原创声明:本文为本人原创实测记录,转载请注明出处与作者(丘山录)。

复现说明:文中脚本与原始数据留在本机,参数不同结论会变,请自行复测。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询