INT4量化并非4倍省:LLM推理Tile的SRAM存储账本全解析 前阵子评估一块开源 LLM 推理 Tile 时我被一个数字当头浇了一盆冷水。项目 README 里明明写着支持 INT4 量化、片上 SRAM 驻留权重我下意识就换算权重从 INT16 砍到 INT4不是应该省出 4 倍吗结果等我把这块 Tile 的 memory map 和 buffer 分配表逐条拉出来核算最后总账只有 3.16×。先别急着嘲笑这个数字。3.16× 并不是失败恰恰相反它给所有做推理加速的人提了个醒INT4 量化节省的是“权重仓库”的租金而 SRAM 账本里还住着激活、KV Cache、中间结果、对齐填充这些不肯走的租户。这篇内容就是完整记录这次复测的过程、账本明细以及我从里面总结出的取舍逻辑。如果你正在评估开源推理核或者打算自己设计一个小 LLM Tile应该能省下不少弯路。1. 一次复测先被直觉摆了一道INT4 的 4 倍去哪了1.1 理论上的 4 倍不假但它只属于“权重户头”先交代一下基线。假设原始网络权重全部以 INT16 存在片上 SRAM某个 Linear 层权重矩阵是 1024×1024那就是 1024×1024×2 字节约 2 MB。如果改成 INT4同样一个权重张量只需要约 0.5 MB。单看这一个参数块省 4 倍没有任何问题数学上非常干净。问题出在“单看”两个字。真实模型不是只有权重一个 LLM 推理 Tile 在跑 transformer 层的时候片上 SRAM 要同时容纳的东西远比“权重”两个字多。我一开始也以为只要把权重 buffer 替换成 INT4总容量就会自动变成四分之一但账算完才发现这个直觉只对了一半。如果想从系统层面估算总体 SRAM 压缩量不能用简单的“压缩 4 倍”去乘权重占比而要把每个 buffer 的压缩率分别列出来做加权。这也是这次复测最核心的动作给 SRAM 里的每个住户单独建一个户头。1.2 SRAM 不是单个仓库而是分了多个户头一块 LLM 推理 Tile 通常包含一块乘累加阵列比如 128×128 的 MAC 矩阵和若干 SRAM bank。运行一个 transformer 层时需要同时驻留以下几类数据权重矩阵Linear、MLP、Attention 里所有可量化的参数。激活和中间结果当前 token/序列块的输入、残差、RMSNorm 前后 buf。KV Cache按头存储的 Key/Value 向量至少驻留当前页。控制与描述符DMA 描述符、tile 索引、量化 scale/zero point 元数据。对齐填充和 bank padding为了让地址对齐、避免 bank 冲突而插入的“空隙”。权重缓存从 16 bit 坍缩到 4 bit后面几类并不会自动缩水。更现实的是SRAM 的物理容量是固定的比如标称 2 MB去掉控制区和保留区真正给计算数据用的可能是 1.6 MB。如果其中一大部分被激活、KV、padding 占着权重省下的 4 倍空间就会被摊薄。这也是我这次复测最直接的感受如果你只看权重 buffer 的替换结果会以为整个系统应该省 4 倍但打开完整地址分配表之后总倍率往往落在 2.5~3.5 之间。3.16× 已经算是一个做了很多额外优化的结果。1.3 拿 1.60 MB 基线算了一遍总账是 3.16×为了把问题说清楚我在复测时以这块开源 Tile 的一个中等配置为例把所有 buffer 的基线容量全 INT16/FP16和优化后容量INT4 混合方案列成了表模块/缓冲区全精度基线INT4 混合优化压缩倍数主 GEMM 权重Linear/MLP/Attention 中可 INT4 部分1.15 MB0.29 MB4×Embedding 与 LM Head 权重0.09 MB0.09 MB1×激活与中间结果含 RMSNorm 前后0.18 MB0.06 MB3×KV Cache片上驻留部分0.12 MB0.04 MB3×对齐填充 / bank pad / 描述符0.06 MB0.03 MB2×合计1.60 MB0.51 MB约 3.16×这里有个细节要特别说明主 GEMM 权重能拿到 4×是因为它占了大头而且确实以纯 INT4 格式存储。但激活和 KV 并没有享受 4 倍它们分别通过流式处理、低比特化和生命周期复用做到了 3 倍左右的压缩。所以 3.16× 不是“INT4 权重缩水”的结果而是“INT4 权重 激活流式化 KV 低比特 对齐优化”共同作用的合计。换句话说只做权重量化这块 Tile 大概率只能到 2.5× 附近能到 3.16×靠的是把其他租户也挨个谈了一遍房租。2. 逐户盘点 SRAM 租户谁在“原价续租”谁“打折了”2.1 权重区Embedding 与 LM Head 拒绝打四折很多人做 INT4 时第一反应是“所有权重都换掉”。但在真实推理 Tile 里Embedding 和 LM Head 往往被排除在 INT4 之外。我问过项目作者回答很直接这两个位置太敏感省下的空间又太少性价比不高。从原理上看Embedding 查表后直接进入神经网络第一层如果这里引入量化误差误差会顺着后续所有层放大。LM Head 则是最后生成 logits 的地方softmax 会放大 logits 上的细微差异本来一个 token 的分数差 0.1 可能没什么但量化误差叠加后可能直接改判输出。更重要的是Embedding 和 LM Head 在大多数 LLM 里是共享权重矩阵的它的体积虽然不小但相对整个模型来说通常不到 10%~15%。在这块 Tile 的布局里它只占 0.09 MB为了这么一点空间去赌整个模型精度不值得。还有一个容易被忽略的点INT4 量化并不是“每个权重铁定 4 bit”。分组量化需要存 scale 和 zero point。如果 group size 是 64每个组额外需要 2×4 字节的元数据摊到每个权重头上就是额外 1 bit。也就是说理论上的 4 bit 权重实际占用是 5 bit压缩率从 4× 掉到 16/5 3.2×。很多 README 只写“INT4”不写 group size等你把 scale 表算进 SRAM 账本才发现倍率缩了水。2.2 激活缓冲区不随权重变小反而吃掉了不少便宜激活 buffer 的位宽不取决于权重位宽而取决于计算精度。INT4 权重进入矩阵乘之前通常要先反量化到 FP16 或 INT8 才能和激活做乘加。所以激活区依然是按 FP16 或 INT8 的“原价”在占空间。这块开源 Tile 的基线配置里激活和中间结果占 0.18 MB。初看不大但注意它只是一个小模型的配置。如果模型层数变多、序列长度变长或者 batch 变大激活区会跟着涨。更麻烦的是流水线设计通常需要双缓冲前一块数据在计算时后一块数据已经搬进 SRAM于是激活区天然要多备一份。我在这块 Tile 里看到的一个优化是激活重算某些中间结果算完之后不保留等反向传播或者后续需要时再重新计算。对推理来说没有反向传播但很多层之间的中间结果也只是“用完即弃”。通过把大块激活切成小 tile 块跑完一块就释放能让激活驻留空间从 0.18 MB 压到 0.06 MB 附近。代价是某些数据可能要从外部存储重新读一遍属于用带宽换容量。2.3 KV Cache在预填充与解码之间反复横跳KV Cache 的大小公式很粗暴层数 × 头数 × 头维度 × 2K 和 V× 序列长度 × 每个元素字节数。一个 8 层模型8 个头head_dim 64sequence length 2048FP16 下就要 8×8×64×2×2048×2 字节约 33 MB。这个量级显然不能全部塞进 2 MB 的片内 SRAM。所以这块 Tile 的 KV 缓存做的是分页驻留当前正在计算的那一页放在 SRAM历史页在外部存储里按需搬入搬出。片上真正占掉的 KV 空间就小很多基线 0.12 MB属于只缓存少量层的短序列配置。KV Cache 能不能也做 INT4原理上可以但硬件上要比权重量化多一整套反量化逻辑而且 Attention 中 QK^T 的计算精度对量化误差更敏感。这块 Tile 最终采用的是组内 8 bit 或类似精度的方案而不是直接压到 4 bit。这样 KV 从 0.12 MB 降到 0.04 MB压缩 3 倍避免了精度崩掉的风险。2.4 对齐与 Bank 冲突看不见的开销也是账本缺口的一部分最后一个租户最隐蔽padding。SRAM 读取总线的位宽通常是 128 bit 或 256 bittensor 的 row stride 如果不对齐到 32/64 字节每次读取都会浪费一部分带宽。所以编译器会在每个 buffer 的起始地址和 stride 上插入 padding。另一部分 padding 来自 bank 冲突。多 bank SRAM 如果正好让两个连续读取命中同一个 bank流水线就卡住了。设计者会给 tensor 加一点偏移让数据交错分布在不同 bank这也会额外占用地址空间。从 memory map 看这部分开销可以是“逻辑上看不见”的某个 buffer 显示 0.05 MB实际分配了 0.07 MB。如果你不做底层对齐分析只按逻辑大小估算算出来的压缩倍数就会偏乐观。这也是我在前面表格里专门把 padding 单独列成一行、并给出 2 倍压缩率的原因优化后的地址布局把不少 padding 给抹掉了但它确实还占着空间。3. 复测过程复盘怎么把一块开源 Tile 的 SRAM 账本完整摊开3.1 第一步从 memory map 和配置 JSON 里找“户口本”开源推理 Tile 一般会在仓库里提供 memory map 或配置 JSON用来描述地址段、buffer 大小、位宽和 quant 配置。常见的名字是mem_config.json、layout.yaml或tile_config.py。我建议你拿到项目后先找这个文件而不是直接看 README。README 里的“INT4 support”通常只代表一个功能点不代表默认配置就是 INT4。我这次复测的基线最开始就被 README 误导过它说支持 INT4但示例模型的 memory map 其实跑在 INT16 上所有 buffer 都是按 16 bit 宽度分配的必须手动改配置后才进入真正的 INT4 布局。如果项目没有现成 memory map也可以从 Verilog/RTL 的寄存器定义里反推查找每个 buffer 的基址寄存器和深度寄存器再对照编译产物里的地址符号表。这个方法麻烦一些但能拿到最真实的分配信息。3.2 第二步用脚本把每个 Buffer 的位宽、容量、基地址拉成表格拿到 memory map 后手工看很容易漏。我写了个小脚本把所有 buffer 遍历一遍按位宽分类统计。核心逻辑很简单for buf in memory_map[buffers]: dtype_bytes bit_width_to_bytes(buf[bit_width]) size_bytes buf[count] * dtype_bytes total_by_type[bit_width_to_bytes(buf[bit_width])] size_bytes rows.append({ name: buf[name], base: hex(buf[base_addr]), size_bytes: size_bytes, quant: buf.get(quant, none), })关键是有意识地去看quant字段而不是只看位宽。很多 buffer 写的是“INT4”但后面跟着group_size64意味着 scale/zero point 的存储要被计入权重区。如果把这一部分漏了权重区的容量会被低估。3.3 第三步跑向量仿真拿运行时峰值验证静态统计静态 memory map 给的是“最大占用”但运行时部分 buffer 会复用地址区间。如果不跑一轮仿真会把实际空间算多。我这次用项目自带的仿真环境跑了一个 8 层小模型的 prefill/decode 流程打印激活 buffer 的 live range。结果很有意思静态表里 activation 0.18 MB仿真运行时的实际峰值只有 0.16 MB因为有多个中间 buffer 的生命周期不重叠。也就是说地址复用已经在静态上体现了一部分但动态峰值才反映真实“同时需要存在”的量。这也是为什么优化后 activation 能压到 0.06 MB设计者把 MLP 两个 GEMM 之间的中间结果共用了同一块地址区看起来每个 buffer 都声明了空间但同一时刻只有其中一份在真正使用。3.4 最容易踩的坑bank 分区让“可见容量”不等于“可用容量”复测过程中最大的坑是我一开始按 memory map 里每个 buffer 的逻辑大小求和结果发现 sum 和物理 SRAM 总容量对不上。差了大概 0.04 MB怎么找都找不到。后来翻 RTL 的地址译码逻辑才发现部分 buffer 在多个 bank 之间做了 interleave实际分配地址是带跨度的。比如某个激活 buffer 逻辑大小 0.05 MB但为了避开 bank 冲突它在地址空间里占了 0.07 MB。编译器的 padding 和链路延迟对齐也会在 buffer 尾部补空白。正确的做法是计算每个 buffer 的“结束地址减起始地址”用这个实际跨度作为容量而不是用count × dtype_bytes的逻辑大小。只有用实际跨度加总才能对上物理容量也才能得到真实的压缩率。4. 3.16× 不是缩水是 LLM 推理 Tile 的真实设计边界4.1 混合精度不是遮羞布是量化误差与硬件开销的现实平衡看到 3.16× 时可能有人会觉得“这项目不行啊INT4 明明应该省更多。”但如果真把所有权重都按 4 bit 硬塞进去你会面临一堆更麻烦的问题。不同层对量化误差的敏感度差异很大。第一层和最后一层最敏感RMSNorm 的 scale 参数也不能随便压。强行统一到 4 bit精度可能崩然后就得靠更复杂的校准流程把分数找回来。而硬件端每多一个低精度格式就要多一套反量化和缩放逻辑寄存器、控制状态、地址位宽都会增加。与其让整个系统为“名义上的 4×”付出这么多代价不如让能 4 bit 的部分 4 bit不能的部分维持高位宽。3.16× 是一个经过工程平衡的数字权重户头享受了 4 倍折扣关键户头原价保留其他户头适当打折。最终的模型精度和速度都处于一个可控状态这才是可量产的设计。4.2 总压缩倍率是“各户头的调和平均”不是权重户头的独角戏我强烈建议做推理优化的人都记住这个公式总压缩倍率 1 / (Σ(第 i 个 buffer 在基线中的占比 / 第 i 个 buffer 的压缩倍率))举个例子如果权重占基线 SRAM 的 80%INT4 压缩 4 倍其他 20% 完全不变总倍率是 1 / (0.8/4 0.2) 2.5。想达到 3.16只靠权重压缩是不够的还需要非权重组也做一定程度的削减。这也解释了为什么很多推理卡宣传 INT4 支持但实际端到端吞吐提升并没有 4 倍。因为 SRAM 不是全用来存权重还有激活、KV、控制数据在拖后腿。下次再看到“INT4 支持”“4 倍省略”的宣传语可以先在心里把这个公式过一遍问一句权重在你们的片上存储里占比多少其他 buffer 做了什么优化如果对方答不上来说明他们可能只在权重上做了功夫。4.3 这个数字对自研/选型 Tile 的指导意义如果你正在选型开源 LLM 推理 Tile不要只问“支不支持 INT4”要问三个问题INT4 覆盖到权重区的多大比例Embedding 和 LM Head 是否也包含在内激活和 KV Cache 在片上驻留多少有没有流式化、分页、低比特化这些额外手段总 SRAM 的 baseline 里非权重区的占比是多少如果非权重区占比超过 25%那么即使所有权重都无损压成 4 bit总倍率也上不了 3×。这时候你应该优先去看非权重区的优化而不是继续折腾权重的位宽。如果是自己设计 Tile我的建议是先画一张“buffer 生命周期图”找出哪些 buffer 同时活着哪些可以复用同一块地址。把生命周期复用做干净比把权重从 INT8 换成 INT4 带来的收益更明显。5. 如果偏要往 4× 逼近剩下那几步的成本清单5.1 把 Embedding/LM Head 也量化到 INT4技术上完全可行很多开源方案也这么做。做法通常是给 Embedding 换用更大的 group size比如 128和 per-channel scale尽量保住低频 token 的精度。但代价也很清楚Embedding 查表后必须当场反量化增加一条硬件路径LM Head 的 logits 误差会被 softmax 放大通常需要额外的校准集才能把分数找回来。而这块 Tile 里 Embedding 和 LM Head 合起来只占 0.09 MB就算压到 4 bit整体节省也就是 0.0675 MB 左右总倍率从 3.16 提到 3.25 这样。为了这个提升去增加硬件复杂度和精度风险不划算。5.2 KV Cache 再做低比特先看目标序列长度别盲从KV Cache 低比特研究已经卷到 2 bit 了但你要先看自己的目标负载。如果序列长度短、batch 小KV Cache 在 SRAM 里占比很低压到 4 bit 的收益很小。如果序列长度长优先方案是分页到外部存储而不是在片上死磕低比特。对于大多数开源模型KV Cache 做 per-group 8 bit 已经能让 PPL 保持在可接受范围。再往下走需要重新校准每一层 K/V 的 scale测试成本会翻几倍。在这块 Tile 上KV 从 0.12 压到 0.04 已经贡献了足够的空间继续往下压属于边际收益递减。5.3 真正的空间挖潜双缓冲、显式生命周期复用、分页回收我自己的体会是4× 的真正瓶颈已经不在权重位宽而在“冗余”。激活双缓冲可以显示地只在边界处保留两份MLP 的第一段 GEMM 和第二段 GEMM 可以共享同一块中间地址区KV 的旧页可以及时换出到外部存储。这块 Tile 能做到 3.16×很大程度不是来自“更小的权重”而是来自“更小的冗余”。权重从 4 bit 省下的空间是固定的但冗余是可以通过设计压缩的。如果以后我要再改造一块新 Tile第一步绝对不是换精度而是把每类 SRAM 住户的 live range 画出来把不必要的驻留清掉再回来跟权重位宽谈折扣。INT4 确实没骗人它只负责“权重户口本”上的四倍。整体仓库的租金得靠整个系统一起谈。

相关新闻

最新新闻

嵌入式启动流程与OTA升级实战:从MCU到SoC的故障定位与容错设计

嵌入式启动流程与OTA升级实战:从MCU到SoC的故障定位与容错设计

做嵌入式固件这行,最怕的不是需求改版,而是设备上电之后毫无反应、升级升到一半变砖、极端环境下一觉醒来发现远程设备集体失联。最近我把这几年做启动流程、故障定位和OTA升级的工程经验整理成付费专栏连载,本篇是衔接上篇思考题的一期&…

2026/9/8 14:55:13
FPGA 100G光口测试实战:从硬件架构到误码率验收

FPGA 100G光口测试实战:从硬件架构到误码率验收

100G光口测试这件事,放在几年前还是数通大厂验证团队的专属领域,但这两年随着数据中心和AI集群的爆发,FPGA工程师碰100G光模块的机会越来越多。我接手过的项目里,有做交换机线卡的、有做加速卡的、还有做专用测试仪表的,几乎都绕不开光口这一关。这期就把我在FPGA上做100G光口/…

2026/9/8 14:55:13
RK3588+RK1288双芯架构破解机器人智能巡检三大现场挑战

RK3588+RK1288双芯架构破解机器人智能巡检三大现场挑战

做机器人智能巡检方案,最难的不是算法跑不出效果,而是整套系统丢到工业现场之后还能不能稳定扛住。我上一个项目是变电站轮式巡检机器人,实验室里YOLOv8识别率刷到99%,一到现场就翻车:模型推理太慢导致机器人走走停停、…

2026/9/8 14:55:13
嵌入式开发必知:23个关键寄存器清单与调试实战指南

嵌入式开发必知:23个关键寄存器清单与调试实战指南

干了这么多年嵌入式,我有个特别深的体会:很多莫名其妙的问题,绕到最后都出在寄存器上。要么是某个位没置对,要么是配置顺序不对,要么是读的时候没注意影子寄存器。库函数用久了容易产生一种错觉,觉得底层都…

2026/9/8 14:55:13
嵌入式工程师多年经验总结:学习路线、面试与实战避坑指南

嵌入式工程师多年经验总结:学习路线、面试与实战避坑指南

后台收到过很多私信问我嵌入式怎么学、能不能入行、薪资到底怎么样。以前在职的时候,有些话不太方便讲,毕竟顶着公司工程师的身份,说重了怕被觉得在带节奏,说轻了又等于没说。现在手续已经办完,交接文档也交了&#xf…

2026/9/8 14:55:13
opencode 终端AI编程Agent实战:安装配置、插件生态与排错指南

opencode 终端AI编程Agent实战:安装配置、插件生态与排错指南

最近在技术圈里,"opencode"这个名字出现的频率越来越高。不管是推特时间线、Hacker News 首页,还是你加的开发者群里,都有人在讨论这个开源的 AI 编程终端 Agent,而且讨论的角度五花八门:有人问安装报错&…

2026/9/8 14:50:13