Step Edge 要放进 App 里跑、参数量定内存,量化定能否落地

文章导读
想在 App 里内置 Step Edge,判断顺序应该反过来:先量出这个应用还能给模型留多少内存,再决定参数量级和量化档位。参数量决定权重的量级,量化档位决定每参数占几个字节,两者相乘才落到内存上;而能不能真的上线,取决于放进去之后低档位还能不能完成原本的功能目标。先用配置和日志把「可用内存上限、组合内存区间、功能表现、两段延迟」四件事记下来,再挑档位,比先选一个模型再想办法塞进去省事得多。
📋 目录
  1. 一 把应用的内存预算按模块拆开
  2. 二 把参数量与量化档位换算成内存占用区间
  3. 三 用固定输入对比不同组合的可用性
  4. 四 记录冷启动与持续使用两段延迟
  5. 五 把选型结论和放弃的档位写下来
A A

想在 App 里内置 Step Edge,判断顺序应该反过来:先量出这个应用还能给模型留多少内存,再决定参数量级和量化档位。参数量决定权重的量级,量化档位决定每参数占几个字节,两者相乘才落到内存上;而能不能真的上线,取决于放进去之后低档位还能不能完成原本的功能目标。先用配置和日志把「可用内存上限、组合内存区间、功能表现、两段延迟」四件事记下来,再挑档位,比先选一个模型再想办法塞进去省事得多。

先把应用的内存上限算清楚:从系统给出的可用内存里,扣掉宿主常驻、业务常驻、缓存和推理峰值临时区,再留一段回收余量,剩下的才是模型权重能用的部分。然后用「参数量 × 每参数字节 + 运行开销」估出各组合的内存区间,用同一批输入对比功能和失败样例,分别记录冷启动与持续调用两段延迟。量化越低通常越省内存,但输出是否还能用必须以自己设备上的记录为准,不要用别人的数字替代。

把应用的内存预算按模块拆开

先别管模型,把应用自己的内存账做成一张分项表。建议至少分这几块:宿主框架与界面常驻、业务与网络模块常驻、图片与磁盘缓存、模型权重常驻、推理时的临时缓冲、会话态(KV cache 之类,会随上下文长度增长),最后是给系统内存压力留的安全余量。前几项可以在不加载模型的空跑状态下量出来,模型相关项用「加载后」减去「空跑」得到差值。

量的时候分三个采样点。基线:应用启动到主页稳定后采一次。加载后:模型权重与运行时初始化完成、还没发起推理时采一次。推理中:一次真实长度输入的推理过程中采峰值。Android 侧可以先看 ActivityManager 给出的内存等级与 Runtime 的可用上限,再用下面的命令核对进程实际驻留,注意不同系统版本和厂商策略会改这些值,需要结合目标机型确认:

adb shell dumpsys meminfo <your.package.name>
adb shell cat /proc/<pid>/status | grep -E "VmRSS|VmHWM"
# VmHWM 是进程历史峰值,适合看推理时的临时缓冲

iOS 侧可以先看内存占用仪表和进程可用内存的查询接口,同样是量实际值而不是套经验值。关键在于安全余量:不要把可用上限用满,要按系统可能触发内存回收的档位往下留一截,比如在内存压力回调里被降到低优先级时,仍在推理的应用很容易被回收。余量留多少,取决于你的应用是前台独占使用还是可能随时被切到后台,这一步只能自己量。

把参数量与量化档位换算成内存占用区间

权重部分的估算很小学生:参数量乘以每参数字节。fp16 大致按每参数 2 字节,int8 按 1 字节,int4 常在 0.5 到 0.7 字节之间(量化参数本身也要占位),具体以你用的导出格式和运行时为准。

权重字节 ~= 参数量 x 每参数字节
常驻占用 ~= 权重字节 + 运行时/框架开销 + 会话态
峰值占用 ~= 常驻占用 + 单次推理临时缓冲
内存区间 = [常驻占用, 峰值占用]  # 报告区间,不报单点

举例只是算术换算,不是实测值:10 亿参数在 int8 下权重约 0.93 GiB,在 fp16 下约 1.86 GiB。把这两个数和上面第一段量出的权重预算对照,往往就能砍掉一半组合;剩下的再叠加运行时开销和会话态,才是要写进记录的区间。

  • 小参数量 + 高精度:权重不大但可能功能偏弱,先看是否满足功能目标。
  • 小参数量 + 低精度:内存最省,优先验证输出是否还能用。
  • 大参数量 + 低精度:内存压力大,需要确认峰值时是否触到回收阈值。
  • 大参数量 + 高精度:除非预算很宽,一般先放弃。

用固定输入对比不同组合的可用性

功能对比要有固定输入集,别每次换几句话就下判断。建议准备四类样本各若干条:短的常规输入、接近上下文上限的长输入、业务里真实的疑难样本、以及会被拒答或格式要求的边界输入。同一批样本依次跑每个候选组合,记录字段至少包括组合标识、样本标识、是否完成、失败类型(截断、格式错、超长、崩溃、明显跑偏)、以及人工判定是否可用。

Step Edge 要放进 App 里跑、参数量定内存,量化定能否落地

对比时只看两件事:低档位组合是否还能达到你原本的功能目标,以及失败样例是集中在哪一类输入上。如果失败总是出现在长输入,那问题可能不在量化,而在上下文与显存/内存预算的分配方式。这里不做任何效果承诺,跑出来的记录才是你唯一能依据的东西。

记录冷启动与持续使用两段延迟

只测单次推理速度会误导选型,因为用户真正感知的是首次打开和连续使用两种体验。冷启动这一段建议这样打点:进程冷启开始 → 运行时初始化完成 → 权重加载就绪 → 首次推理返回。持续调用这一段:先做若干次预热调用,再连续调用足够多次(次数按你的交互模型定),记录每次耗时,看中位数和尾部,而不是只取一个平均值。

timestamp_ms, phase           # 打点用
load_ms                       # 冷启动:初始化到权重就绪
first_infer_ms                # 冷启动:首次推理返回
call_ms[]                     # 持续:每次调用耗时,保留原始数组
mem_peak_mb                   # 与延迟同一时刻采的峰值内存

把耗时和峰值内存放在同一张表里看,能发现一些单看延迟看不到的问题,比如加载时间可接受但推理峰值越过了你在第一步留的余量。

把选型结论和放弃的档位写下来

最后留一页记录,交给下一个改版本的自己。不用长,一张表就够:

  • 最终选择:参数量 + 量化档位 + 导出格式,以及它对应的常驻与峰值内存区间。
  • 被放弃的组合:写清档位、放弃理由(内存超预算、功能不达标、冷启动过长、失败样例集中在业务关键输入等)。
  • 复现信息:输入样本集标识、打点字段与采集命令、目标机型与系统版本。
  • 待复查项:这次没量到的部分,比如后台被切走后的内存表现、更长上下文的占用。

这样下次要换档位,先翻这页看当初为什么被淘汰,往往不用重跑一遍全组合。