当前位置:首页 > 中国芯 > 端侧AI
[导读]导读: 端侧模型装不装得下,常被当成内存容量问题。真正决定上限的是另一条式子:可用于钉住权重的缓存,等于权重循环块总大小减去存储带宽乘允许延迟。一台 8 GB 的手机,系统只划出 256 到 512 MB 给这段循环;int4 量化下 15 亿参数要 0.75 GB,超出的部分每次换入就要几百毫秒,而生成一个 token 只给 80 到 125 毫秒。

端侧模型装不装得下,常被当成内存容量问题。真正决定上限的是另一条式子:可用于钉住权重的缓存,等于权重循环块总大小减去存储带宽乘允许延迟。一台 8 GB 的手机,系统只划出 256 到 512 MB 给这段循环;int4 量化下 15 亿参数要 0.75 GB,超出的部分每次换入就要几百毫秒,而生成一个 token 只给 80 到 125 毫秒。

八 G 内存里的半 G

一台 8 GB 的安卓手机,内存分配大致是这样:系统与常驻服务吃掉 3.5 GB,应用与图形占 2.5 GB,页缓存和后台留 1 GB,还有 0.5 GB 没被回收,落到模型权重上的只剩 0.5 GB。

这个 0.5 GB 不是随手划的,它对应的是操作系统的缓冲区缓存配额。真实机型上这个数字在 256 MB 到 512 MB 之间,取决于系统版本和后台压力。

而模型那边,一个 10 亿参数的模型做 int4 量化,权重是 0.5 GB;15 亿参数是 0.75 GB。两者在容量上都不算大,问题出在它们必须同时满足另一个条件:每一次生成 token,都要把整个权重循环扫一遍。

图 1 8 GB 内存的去向与模型实际拿到的份额(依据原文第 4 节容量模型与第 5 节平台参数绘制)

手机的内存管理里有两个容易混淆的量:空闲内存和可缓存内存。

空闲内存是没被任何进程占用的部分,系统会尽量把它压到很低,因为空着就是浪费。可缓存内存是操作系统拿来放文件页的那一块,模型权重文件被 mmap 进来之后,落在这一块里。

关键在于,可缓存内存的大小不等于空闲内存加上文件页。它是一个有配额的池子,池子的替换策略决定哪些页留下。空闲的 0.5 GB 不会自动变成模型能用的 0.5 GB,它要经过一轮分配和替换才可能被借用,而在内存压力下随时会被收回。

论文的做法是把这个问题拆成文件级的访问模式。作者用 strace 在真机上按 4 KB 粒度采了一段 trace,发现模型应用的 I/O 有三类形态:初始化时的一次性顺序扫描,常驻的小热集,以及推理过程中反复循环的权重块。

三类要区别对待。一次性扫描应该尽快驱逐,否则会把缓存冲干净;热集是分词器、元数据和索引文件,总共不到 4 MB,必须完整保留;权重块的循环要钉住,保证每一轮都确定性命中。

带宽乘延迟才是抵扣项

把三类分开之后,容量关系可以写成一条很干净的式子:

S ≥ max{ 0, N 降 BW × T }

S 是需要钉住的循环缓存,N 是权重循环块的总大小,BW 是存储的持续读带宽,T 是允许的取数延迟。

这条式子的意思是,缓存不必装下全部循环,只要装下带宽在允许延迟内取不回来的那部分。带宽越高、延迟越松,需要钉住的比例越小。

T 的取值由解码速度定。论文实测的生成速度是每秒 8 到 12 个 token,换算成每个 token 80 到 125 毫秒,取整之后把 T 定在 0.1 秒。

分词器那 4 MB 不能丢

三类访问模式里,最不起眼的是热集,代价最高的也是它。

热集是分词器、模型元数据和索引文件,加起来不到 4 MB。它的特点是每个 token 都要访问,而且访问位置分散,不像权重那样按顺序扫。

这 4 MB 如果被权重挤出去,下一次生成 token 时就要重新读回来。按 50 微秒的 I/O 建立延迟算,就算数据只有几百 KB,一次重新加载也要几毫秒,而且是在最关键的路径上。

所以论文的策略是先划出 4 MB 把热集钉死,剩下的才分给权重循环。这个顺序不能反。反过来做,缓存看着用满了,命中率反而掉。

手机上还有一块内存会来分食这个池子,就是 KV 缓存。

以 10 亿参数、16 层、16 个注意力头、每头 128 维、上下文 2048 为例,KV 缓存按半精度存,占用是两次乘层数乘头数乘维度乘序列长度乘两字节,算下来 256 MB。

0.5 GB 的 int4 权重,加 256 MB 的 KV 缓存,一共 0.75 GB。这个数字已经超过了手机能给模型的 512 MB 上界。

在手机的统一内存架构里,这两块从同一个物理池子里拿。KV 缓存随上下文长度线性增长,用得越久越大,权重文件占的页缓存份额就被一点点挤出去。这也是为什么长对话跑到后面会明显变慢,不是算力不够,是权重页被换出去了。

手表级差在带宽不在容量

同一条式子,代进两类平台,结果差一个数量级。

手表级:内存 1 到 2 GB,能给模型的缓存约 64 MB,热集占 4 MB,剩下 60 MB。存储是 eMMC,标称顺序读 200 到 300 MB/s,实际受热和功耗限制只有 100 MB/s 左右。按 T 等于 0.1 秒算,带宽能抵扣 10 MB,可承载的循环规模是 60 加 10,约 70 MB。

手机级:内存 6 到 8 GB,缓存池 512 MB 到 1 GB,划给模型的循环数据 256 到 512 MB。存储是 UFS 4.0,持续顺序读不低于 1 GB/s,抵扣 100 MB,可承载的循环规模 356 到 612 MB。

两边的缓存差 4 到 8 倍,可承载的循环规模差 5 到 8.7 倍。多出来的那部分不来自内存,来自存储带宽。把手表的 eMMC 换成 UFS,可承载规模立刻从 70 MB 跳到 160 MB 以上,而内存一个字节都没加。

图 2 可承载的循环规模随存储带宽的变化(依据原文第 4 节式 1 与第 5 节平台参数绘制)

一次换页就是半个 token 的时间

反过来说,超出缓存的那部分要付多少代价,可以直接算。

512 MB 的循环块,如果每次生成 token 都要从 UFS 全量读一遍,1 GB/s 下是 0.51 秒。而 token 的预算是 80 到 125 毫秒。也就是说,一次全量换入要花掉四到六个 token 的时间。

分块之后好一些。按 4 KB 一块算,每次只换入当前层要用的那几十兆,10 MB 在 1 GB/s 下是 10 毫秒,占 token 预算的 8% 到 12%,勉强能接受。代价是每层都要停下来等 I/O,流水线被打断,实际吞吐还要再降一档。

论文测的存储每次 I/O 建立延迟是 50 微秒。这个数字单看不小,但在 10 毫秒的传输面前只占 0.5%,可以忽略。真正的瓶颈是带宽本身,不是寻道。

记账本身的开销也要算。512 MB 按 4 KB 一块切开是 131072 块,每块一份元数据,合计 1 MB 上下,占容量的 0.2%。这块开销不能省,因为没有块级记账就没法判断哪一块该留。

模型规模被存储带宽定死

把这条链走完,会得到一个和常识相反的结论。

端侧模型能跑多大,不由内存容量决定,由存储带宽乘允许延迟这个乘积决定。同样 8 GB 内存,配 eMMC 的循环规模是 70 MB,折成 int4 权重约 1.4 亿参数;配 UFS 4.0 是 612 MB,约 12 亿参数。差 8.7 倍,内存完全一样。

这也解释了为什么很多标着 8 GB 内存的设备跑不动 15 亿参数的模型,而换一台同样 8 GB 但存储更快的机器就流畅。参数表上写的是容量,实际约束在带宽。

还有一层取舍。缓存开得越大,命中率越高,但留给应用的内存越少,系统会更容易杀后台。论文里提到,即使是 LRU 这种成熟策略,在缓存持续增大时也会出现收益停滞的区间,因为循环块本身比缓存大,怎么换都是全错过。这时候再加缓存没有意义,得改成分层加载,把不常用的层放到闪存上按需取。

一句话,端侧模型的容量上限写在存储带宽上,不写在内存条上。

内容来源:Mathematics(MDPI 开放获取,CC BY 4.0)

原文标题:Rethinking I/O Caching for Large Language Model Inference on Resource-Constrained Mobile Platforms

作者:Heejin Kim,Jeongha Lee,Hyokyung Bahn(韩国梨花女子大学)

DOI:10.3390/math13223689

配图说明:两张图均为自行绘制,为原理示意,非原文原图复制。

改动声明:本文为对开放获取文献的技术编译与解读,标题、行文与配图为原创。

来源:21ic电子网

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭