端侧LLM推理性能排查实录:0.5到30 token/s的优化之路 别人手机跑 7B 模型轻轻松松 40 token/s我手机装上同一个推理框架、同一个模型文件测出来 0.5 token/s 出头。两个数字摆一起差了 80 倍。这不是夸张是我最近调端侧推理时真实踩过的坑。当时第一反应是手机太老、硬件不行但仔细扒完日志才发现硬件只背了一小半锅真正的问题出在量化格式、线程配置和系统调度上。如果你也遇到过同一个模型在不同设备上速度天差地别的情况这篇排查实录应该能帮你少走不少弯路。先说结论端侧 LLM 的性能瓶颈从来不是手机不够好而是你的推理栈到底有没有跑在正确的路径上。下面我把这次排查的完整过程、对比数据、修复手段和事后整理的速查清单全部写出来照着做大部分性能问题都能在半小时内定位。1. 先搞清楚端侧跑 LLM 的正常到底是多少在排查之前得先建立一条基线。没有基线80 倍慢就没有参照物后面所有改动也说不清有没有效。1.1 端侧推理的两个阶段预填充与解码大模型推理分成两个明显不同的阶段。第一个是**预填充Prefill阶段模型一次性吃进你输入的整段上下文所有 token 并行计算这个阶段是典型的计算密集型操作。第二个是解码Decode**阶段模型一个一个往外蹦 token每蹦一个都要把全部权重从头读一遍所以这个阶段卡在内存带宽上。举个直观例子假设一个 7B 规模的模型int4 量化后权重大约是 3.5GB你手机内存带宽如果是 25GB/s解码阶段每蹦一个 token 就得花 3.5GB ÷ 25GB/s ≈ 0.14 秒换算过来也就 7 token/s 左右。带宽砍一半速度跟着砍一半。如果你用的还是 fp16 精度权重直接奔 14GB 去速度更是断崖式下跌。这就是为什么我测速时一定要把两个阶段分开看。很多框架默认打印的数字是prefill decode 混在一起的平均值上下文越长越失真。排查性能问题时只看 decode 阶段的 token/s 才是公平对比。1.2 建立对照设备基线我这次手里有两台设备。参考设备 A 是近两年的旗舰芯片对比设备 B 是台老款中端芯片系统、内存、存储都不太一样。同一个 int4 量化模型文件同一套推理框架放在两台设备上分别测。设备 A 用默认参数跑decode 稳定在 43~45 token/s设备 B 用同样参数跑只有 0.5~0.6 token/s。当时差点就把锅甩给硬件差距了但冷静算一下即便设备 B 芯片算力、带宽都弱一截再怎么样老款芯片跑 int4 7B 模型也不会只有 0.5 token/s。这个数字低得太反常反而说明问题不在硬件在软件栈。注意排查性能问题第一件事永远是建基线。找一台你心里有底的设备跑同一份模型文件、同一套框架版本把两个阶段的耗时分别记下来再开始动配置。没有基线的调优都是瞎猜。2. 慢 80 倍的根源从算子到系统的三层检查拿到异常数据后我按从代码到硬件的顺序一层层往下查总共锁定到三个层面的问题。这种自顶向下的排查方式推荐大家都试试比盲改配置高效得多。2.1 第一层框架状态与量化格式核对我第一个怀疑对象是量化格式。端侧推理框架为了压速度都会对 4bit 权重做特殊的内存布局和算子优化比如按组打包、矩阵分块等。如果模型文件虽然是 int4 命名但实际加载时框架没有走加速路径权重就可能在运行时被展开成 fp16 甚至 fp32 来计算。怎么验证框架启动后看到日志里其实写得很明白。正常走量化加速路径时日志会标注类似加载 4bit 权重已启用整型矩阵乘加速算子异常时通常会有一行警告提示当前算子不支持 int4回退到 fp16 展开或者干脆静默回退。我这次就撞上了第二种。设备 A 的芯片有完整的整型加速指令集框架自动走最优路径设备 B 的老款芯片缺了关键指令框架直接静默回退到 fp16 展开。一个 int4 模型实际跑出来是 fp16 的权重和算子内存开销翻到接近 3.5 倍速度自然按比例崩掉。排查方式很简单打开框架日志搜revertfallbackunsupported关键字看有没有算子回退告警加载模型后看内存占用。7B int4 模型理论占用约 3.5~4GB如果看到 7GB 以上基本可以断定实际在跑 fp16对比 CPU 占用特征。fp16 展开时多个大核都会接近满载int4 加速路径下占用率通常没这么夸张2.2 第二层线程数与调度策略第二层我查的是线程配置。推理框架的线程数不是越多越好尤其手机上的大小核混合架构线程要是全都轰到小核上速度能慢得让你怀疑人生。框架默认的线程数有两种极端一种是把 CPU 核数全部劈开用比如检测到 8 核就开 8 线程另一种是保守起见只开 2 线程。两种在我这台设备 B 上都会出问题——开满了线程会被系统调度到一堆小核上开少了矩阵乘算力又吃不满。而且这次还抓到一个隐蔽点框架把线程固定到了某个 CPU 分组里而设备 B 的大核和小核分属不同的调度域。明明有 4 个大核框架偏只认了默认组里的 2 个剩下几个线程全挤在小核上跑。0.5 token/s 里有相当一部分就是这么来的。排查办法用系统命令实时看每个线程跑在哪个核心上确认线程是不是都挤在小核看单线程和多线程的实际速度差异。如果 8 线程比 4 线程还慢说明调度出问题了2.3 第三层功耗与散热策略带来的隐形降频最后一个大坑在系统功耗管理。设备 B 当时处于默认的智能省电模式系统检测到持续高负载后直接把大核频率往死里压甚至短时间全核降频。我跑推理的时候核心频率被锁到标称的一半不到。这种问题最坑因为你看软件层完全正常量化也对、线程也对但帧率就是上不去。手机厂商为了续航和温控往往对持续重负载非常敏感而跑大模型恰恰是最典型的持续重负载。排查思路先排除软件层问题再怀疑散热降频。如果代码没有任何变化把设备放在冰袋上跑一次速度明显提升那基本就是温度墙在捣鬼。注意别把冷凝水弄进设备我就因為这个废过一台测试机。3. 逐步复现与修复把 0.5 token/s 拉回正常水平问题锁定之后剩下的工作就是一个个修。这个过程我强烈建议一次只改一个变量改完立刻测速记录在案再改下一个。不然多个变量混在一起你根本不知道是谁起了作用。3.1 第一步建立隔离环境在动手之前我先把设备 B 恢复成干净状态关掉所有后台应用切到性能模式关闭自动亮度把屏幕调暗开启飞行模式拔掉充电线。原因很简单——跑模型本身就是重负载后台刷信息流的 app、无线传输、充电发热都会干扰测试结果。然后我对模型文件做了一次 SHA 校验确认两台设备上跑的是同一个文件二进制级别一致。这个步骤很多人会跳过但我排查过不止一次设备 A 正常、设备 B 异常最后发现是两台设备上的模型文件根本不一样一个是 int4 一个是 fp16。3.2 修复一强制启用量化加速路径设备 B 的框架默认回退到 fp16我需要确认框架是否提供强制走加速路径的开关。翻了框架文档后发现确实有关联的编译选项但这个选项是编译期决定的设备 B 上跑的是别人预编译的包没法现场改。于是换了个思路换成有对应版本指令集优化的构建。同一套框架设备 A 的构建是完整版设备 B 我之前图省事装了个兼容性构建两者内部算子实现完全不同。换掉之后日志里终于出现了整型矩阵乘加速已启用的标记内存占用也从 7GB 多降回了 3.8GB——量化路径通了。实操心得端侧推理框架在不同处理器上的性能差异很大程度取决于你用的是哪个构建版本。全功能构建体积大但算子优化完整兼容性构建什么芯片都能跑但性能可能差数十倍。追求性能就选针对你芯片优化的版本别图省事。3.3 修复二手动绑定大核与调整线程数量化路径修好之后速度已经起来了一截但离设备 A 还有很大差距。接下来处理线程调度问题。框架支持通过配置文件或环境变量指定线程数和 CPU 亲和性。我直接指定优先使用高性能核心的策略并把线程数从默认值调到一个更合理的值。这里有个细节值得说一下线程数不是越大越好对于 4 个高性能核的手机推理线程设 4再留一两个线程给系统做调度、渲染效果往往比全开要好。调完之后运行 top确认推理线程确实都跑在大核上CPU 频率也稳定在较高档位速度又往上走了一截。3.4 修复三电源策略与模型加载优化最后处理散热和功耗策略。设备 B 性能模式下系统对大核频率的限制会放宽但代价是发热更快跑长文本时会撞到温度墙。另外一个容易被忽略的点是模型加载速度。设备 B 用的是慢速存储模型加载花了 30 多秒期间框架界面卡死。后来我把模型文件换成加载到内存后缓存权重的方式第二次加载只要 1~2 秒。最后看数据设备 B 从最初的 0.5 token/s一路修到 7 token/s再到 30 token/s。虽然没有完全追平设备 A 的 43 token/s但已经从完全不可用变成了能正常对话的水平。后端几个问题叠加起来还真能凑出 80 倍的差距。配置阶段速度 (token/s)主要瓶颈初始状态0.5~0.6fp16 展开、线程挤小核、降频更换量化版本约 7线程调度仍不理想绑核调线程约 22功耗/散热限制性能模式缓存30硬件上限4. 端侧推理性能排查清单与常用工具这次排查完之后我把经验整理成了一份快速自查清单。以后遇到类似的问题直接按顺序走一遍大多数情况 20 分钟内能定位。4.1 5 分钟快速自查表模型文件是否一致比文件大小、SHA、量化位数。两台设备跑不同精度还对比速度是排查里最常见的乌龙。日志搜回退告警找revertfallbackunsupportedunsupported kernel字样。看内存占用int4 模型却占用 fp16 级别的内存说明走了展开路径。看线程分布用监控工具实测推理线程是否都在大核还是挤在小核。关省电模式切到性能模式再测试排除频率墙。测温度设备发热严重时先降温再测避免把温度墙误判成代码问题。跑三次取中位数单次测速受后台任务、系统调度影响很大至少跑三次。4.2 排查过程中最常用的几个工具系统自带的 CPU 频率和核心状态查看命令实时看每个核的运行频率top/ 增强版进程监控看线程 CPU 占用率和进程状态日志抓取命令观察框架运行时的告警输出基准测试脚本反复压测记录 prefill 和 decode 的耗时分布独家技巧测速时不要用第一次的结果。模型加载后会有权重预热、缓存命中的过程直接测第一次通常偏慢。先空跑几轮把推理链路热起来再正式记录数据这样得到的数值才有参考价值。5. 复盘影响端侧 LLM 推理速度的几个底层因素如果你把上面所有问题剥开看真正决定手机跑 LLM 快不快的其实是几个底层因素。我顺手整理了一下方便以后选设备或调框架时心里有底。量化格式和算子支持同样的模型int4 在支持的芯片上是高速路径在缺指令集的芯片上可能回退成 fp16速度差可以到 5~10 倍。这个往往是最关键的一环。单次解码的内存搬运量解码阶段每生成一个 token 都要把全部权重读一遍内存带宽直接决定天花板。带宽翻倍解码速度就有可能翻倍。大小核调度策略手机芯片的小核跑 LLM 就是灾难线程被调度到小核上再好的量化也发挥不出来。系统功耗控制在散热有限的空间里塞进大模型推理温度墙是绕不开的。性能模式和智能省电模式下的速度差距实测能差到 3 倍以上。模型文件加载成本大模型文件从闪存加载到内存的时间在慢速存储上会明显放大。首 token 延迟和冷启动体验往往坏在这上面而不是推理本身。顺手再提一个很多人踩过的坑比较不同设备的性能时一定要用同一个框架版本、同一个模型文件、同一个量化位数。我见过太多设备 A 比设备 B 快几十倍的结论最后发现一个是 int4 模型、一个是 fp16 模型根本没可比性。屏蔽掉这些干扰项设备间真实的硬件差距往往远小于直觉判断。这次排查给我最大的一个体会是端侧推理的慢绝大多数不是硬件的硬伤而是软件栈没有吃满硬件。从回退掉型的量化算子到被调度错位的线程再到被系统悄悄压掉的频率任何一环出问题都会让你的手机看起来比别人的慢一个数量级。先查日志、再查线程、最后查功耗这个顺序能筛掉 95% 的问题。希望这份实录能帮你在类似的问题上省下几个通宵。