生成式推理内存墙:3D-DRAM近存加速器评估方法

发布时间:2026/9/18 1:40:24
生成式推理内存墙:3D-DRAM近存加速器评估方法 上个月帮朋友压一个 70B 模型的在线服务双卡 80G 显存batch 拉到 32单卡吞吐就卡在 400 token/s 附近上不去了。nvidia-smi里 GPU 利用率显示 90% 以上看着挺满但用ncu一测FP16 计算管线的占用只有 6%剩下的时间全花在等显存把数据搬过来。这个现象几乎每个做过生成式推理部署的人都遇到过它不是算力问题是搬运问题。围绕这个痛点近两年出现了一类新的硬件思路——把 DRAM 在垂直方向堆叠起来同时把一部分计算挪到存储的近端也就是业内讨论越来越多的 3D-DRAM 加速器。这篇文章不打算复述某篇论文的摘要而是把我自己在做生成式推理性能建模和硬件方案评估时踩过的坑、算过的账、以及一套可以拿来复现的分析方法完整写出来。如果你正在做推理侧的性能优化、在评估下一代存储方案或者只是想搞清楚为什么堆 HBM 堆到头还是会撞墙下面的内容应该对你有用。1. 生成式推理为什么会被内存带宽掐住脖子1.1 预填充和解码是两种完全不同的负载很多人第一次做 LLM 推理优化时会犯一个错把整条链路当成一个大矩阵乘法来优化。实际上生成式推理在时间轴上被切成两段性质完全相反的东西混在一起谈优化方向一定跑偏。预填充prefill阶段一次性吃掉整段 prompt输入是一个[seq_len, hidden]的大矩阵跟权重做的是真正的 GEMM。这个阶段算术强度高属于典型的算力受限负载优化手段是算子融合、FlashAttention、张量并行切分目标是榨干 Tensor Core 的 FLOPS。解码decode阶段完全是另一回事。每生成一个 token你都要拿一个[1, hidden]的向量去乘一堆[hidden, out]的权重矩阵。这就是 GEMV矩阵-向量乘。随批量增大它可以退化回 GEMM但在 batch 很小、延迟敏感的交互式场景里它就是货真价实的 GEMV——每个权重元素只被用一次读完就扔。关键差别在这里GEMM 里一块权重能被复用几十上百次搬运成本被摊薄GEMV 里权重只服务一次乘加搬运成本是全额支付的。这就是为什么解码阶段几乎必然撞上内存墙而不是算力墙。我习惯用一个很土但很好用的判据每读一个字节能换来多少次浮点运算也就是算术强度。这个数低于机器的脊点ridge point你就没救只能靠加带宽或者减少数据搬运量。1.2 拿 Roofline 把账算到具体数字空谈没意义直接算。以一颗主流数据中心 GPU 为例FP16 稠密算力约 989 TFLOPS显存带宽 3.35 TB/s那么脊点就是 989 ÷ 3.35 ≈295 FLOP/Byte。也就是说每搬一个字节你得做将近 300 次浮点运算才配得上这块芯片的算力。现在看解码。假设一个 70B 参数的模型权重量化到 INT8每个 token 需要读一遍全部权重那就是 70 GB。计算量是每个权重两次浮点操作一次乘、一次加约 140 GFLOP。算术强度 140 ÷ 70 ≈2 FLOP/Byte。和 295 差了将近 150 倍。剩下的算力全是摆设。用一段 Python 把这个估算固定下来方便你换成自己的模型参数复算def decode_roofline(peak_tflops, peak_bw_tb_s, params_b, bytes_per_param, kv_bytes_per_token0.0, ctx8192, batch1): 粗粒度 roofline 估算判断解码阶段卡在带宽还是算力。 kv_bytes_per_token: 单序列、单 token 的 KV Cache 字节数含 K 和 V weight_bytes params_b * 1e9 * bytes_per_param kv_bytes kv_bytes_per_token * ctx * batch flops 2 * params_b * 1e9 * batch total_bytes weight_bytes kv_bytes ai flops / total_bytes # FLOP/Byte ridge peak_tflops * 1e12 / (peak_bw_tb_s * 1e12) time_s total_bytes / (peak_bw_tb_s * 1e12) attain_tf min(ai, ridge) * peak_bw_tb_s # 可达算力TFLOPS print(f算术强度 : {ai:.2f} FLOP/Byte) print(f机器脊点 : {ridge:.1f} FLOP/Byte) print(f单步理论下限 : {time_s*1000:.2f} ms - {1/time_s:.1f} token/s (batch{batch})) print(f可达算力 : {attain_tf:.2f} TFLOPS / {peak_tflops:.0f} TFLOPS) decode_roofline(989, 3.35, 70, 1.0)跑出来大概是单步 20.9 ms也就是单卡约 48 token/s 的硬上限可达算力不到峰值的 1%。你所有关于 kernel 调优、CUDA Graph、算子融合的努力都在这个 48 的天花板下面折腾——能优化的是多接近这个数而不是突破这个数。要突破只有三条路减少搬运的字节数量化、稀疏、权重复用、降低每字节的搬运能耗这决定功耗墙、提高有效带宽。3D-DRAM 加速器想同时吃下后两条。1.3 KV Cache 才是那个被低估的变量大家都在盯权重的 70 GB但真正会让你的容量规划崩盘的往往是 KV Cache。它的体量随上下文长度线性增长$$ \text{KV bytes} 2 \times L \times H_{kv} \times D \times S \times B \times \text{precision} $$其中 2 代表 K 和 V 两份L 是层数H_kv 是 KV 头数D 是 head dimS 是序列长度B 是批量。拿两个真实配置对一下模型配置每 token KV 字节FP168K 上下文单序列128K 上下文单序列MHA80 层64 头D1282.62 MB21.5 GB344 GBGQA80 层8 个 KV 头D128328 KB2.68 GB43 GB结论很反直觉MHA 架构的模型KV Cache 在 26K 上下文左右就和权重一样重了再往上 KV 反而成了主要搬运对象。而 GQA/MQA 把 KV 压到八分之一后权重依然是主导但到了 128K 长上下文KV 又涨回 43 GB重新变成不可忽视的一块。这件事对加速器设计的直接影响是权重和 KV 的访问模式完全不同。权重是每个 token 顺序扫一遍、跨 batch 可复用KV 是每层每头随机跳转、按需索引、跨 batch 不可复用。把它们塞进同一套存储结构、同一条数据通路必然有一方被拖累。这也是我看这类加速器时第一个去找的点——它有没有在物理层面把这两类流量拆开。2. 3D-DRAM 改变的到底是哪一层2.1 先说清楚3D-DRAM这个词的歧义这里必须先泼一盆冷水HBM 本身就是 3D 堆叠的 DRAM8 到 12 层 die 用 TSV 串起来所以3D-DRAM这个词在不同语境下可能指三件完全不同的事混着聊会得出完全错误的结论。第一种是封装级堆叠也就是 HBM 那条路线DRAM die 竖着摞起来底下加一层 base die整摞放在硅中介层上围着主芯片摆一圈。这条路已经成熟问题是它撞到了两个物理极限——中介层面积有限一个封装里塞不下超过 6 到 8 摞HBM PHY 的每比特能耗在 3 pJ 上下这个数字几乎不随工艺进步而下降。第二种是存储单元本身的三维化把平面 DRAM 的 6F² 单元做成垂直沟道结构像 3D NAND 那样往上长。这解决的是密度问题不是带宽问题短期内也不会出现在推理加速器里。第三种也是针对生成式推理的 3D-DRAM 加速器这个命题真正指向的方向是存储-逻辑的垂直异质集成把逻辑 die 放在下面DRAM 阵列晶圆直接混合键合hybrid bonding压在逻辑上面中间不打 TSV 走 PHY而是靠铜-铜键合点直接对接。对比一下这三条路线在关键指标上的差别维度HBM封装堆叠3D 单元垂直沟道存储-逻辑键合主要解决的问题容量密度、封装内带宽单元面积、制造成本每比特能耗、可达带宽互连方式TSV 微凸点 中介层不涉及互连铜-铜混合键合互连间距25–55 µm微凸点—1–10 µm研究前沿亚微米每比特能耗约 2.5–3.5 pJ—目标 0.3–1 pJ逻辑工艺自由度独立可先进工艺无逻辑层受热预算限制需降频混合键合的价值在于互连密度提高了两个数量级。微凸点做 40 µm 间距每平方毫米撑死几百个连接混合键合做 3 µm 间距每平方毫米上万个。当连接点足够多你就可以用极宽但很慢的总线替代很窄但很快的 PHY——总线频率降到几百 MHz 甚至更低宽度拉到几万位总带宽反而更高而动态功耗随频率平方下降。这是 3D-DRAM 在能耗上能赢的根本原因不是材料变好了是拓扑换了。2.2 逻辑层里到底该放什么一旦接受逻辑和存储贴在一起下一个问题就很具体了哪些计算值得搬进 DRAM 那侧我的判断标准是三条访存比极高、不需要跨 token 依赖、精度容忍度大。按这三条筛解码阶段能搬的东西其实不多但搬对了收益很大。第一条候选是权重的反量化。INT4/INT8 权重从 DRAM 读出来如果原样送到主芯片再解包你等于把压缩收益全浪费在总线上——总线传的还是压缩后的字节但解压后的数据又要在主芯片内部走一遍。把 scale/zero-point 的解包逻辑放在存储近端读出即所得主芯片拿到的直接是能进 MAC 的定点数。第二条是注意力分数的部分归约。解码时 Q·K^T 是向量-向量点积每个 head 的分数可以完全独立算完。把 K 按 head 分片放在不同的 DRAM 分区每片本地算部分和再用一个很窄的归约网络汇总能把 KV 的读取和计算重叠起来。第三条是激活值的累加树。GEMV 的结果是各分行部分和的叠加这部分加法完全可以下沉到存储侧主芯片只收最终结果。注意这里说的是加法不是乘法阵列。关于存内计算我要多说一句。真正把乘加阵列做进 DRAM 单元阵列里analog CiM、charge-domain MAC的方案看起来很诱人但精度是硬伤——DRAM 单元的电荷量随温度、刷新时序、工艺偏差漂移做 4 bit 以上的稳定模拟计算非常吃力。我个人的看法是近存计算near-memory比存内计算in-memory在生成式推理这个场景里更现实因为推理对精度的要求虽然不苛刻但也不能容忍不可预测的漂移而近存方案保留了数字逻辑的确定性。2.3 DRAM 工艺做逻辑的代价必须算进去有个坑很多人第一次都会踩DRAM 工艺节点的逻辑速度比同代逻辑工艺慢 3 到 5 倍。你在 DRAM 那侧放的不是一颗高性能 ALU而是一堆能跑几百 MHz 的简单加减器和移位器。这意味着逻辑层的设计原则跟 GPU 完全相反不用想频率只想并行度不做复杂控制流只做规整的流式运算。想在存储侧跑 softmax 的完整实现要 exp、要 max 归约、要除法基本不现实但算个部分和、做个比较、搬个数据完全够用。所以一个合理的分工是存储侧做搬运 初筛 部分归约主芯片做非线性 跨头归约 采样。任何带超越函数或者需要全局同步的操作都留在主芯片。这条边界画错了整个架构的收益会被跨层通信吃干净。3. 加速器内部的数据通路怎么排3.1 KV Cache 的 bank 映射决定了真实性能KV Cache 的读取模式和权重完全不同它按(layer, head, token_position)三元组索引访问是跳跃的、碎片化的。而 DRAM 最怕的就是碎片化访问——行缓冲row buffer命中率一掉tRC 和 tFAW 这些时序参数就会把你按在地上摩擦。以常见的 paged attention 为例KV 被切成固定大小的 block比如 16 个 token 一块。如果 block 的物理地址直接按分配顺序落到 bank 上同一个 head 在连续 decode step 里访问的 block 会落在同一批 bank形成热点其他 bank 闲着。解决办法是按 bank 数做轮转交织让 head 索引和 bank 索引正交// KV 物理地址映射示意让 bank 索引与 head 索引解耦 // 目标同一时刻活跃的多个 head 分布在不同 bank避免 bank 冲突 uint64_t kv_addr(uint32_t layer, uint32_t head, uint32_t blk, uint32_t off) { uint32_t banks NUM_BANKS; // 例如 32 uint32_t b (head * 7 blk) % banks; // 7 与 banks 互质保证轮转 uint32_t row ((head / banks) * KV_BLKS blk) / KV_BLKS_PER_ROW; return base[layer] ((uint64_t)b BANK_SHIFT) ((uint64_t)row ROW_SHIFT) off; }这里用 7 这个质数做乘子不是玄学是为了保证当 head 数以 8 或 16 递增时bank 索引不会被 2 的幂整除而周期性落在同一个 bank 上。我在做 DDR 控制器调优时吃过这个亏地址映射里任何一个跟 bank 数成整数倍的步长都会在长序列推理里变成性能悬崖。另一个容易被忽略的点是权重的行连续性问题。GEMV 读权重时最理想的情况是一次 ACT 拉出一整行然后连续 burst 读走。如果量化分组group-wise quantization把每 128 个权重配一组 scale而 scale 和权重数据分开放那每读 128 个权重就要多一次跳跃访问。把 scale 内联到权重块头部虽然损失一点点解码便利性但能保住行局部性。这个改动我在实测里通常能拿回 8% 到 15% 的有效带宽。3.2 权重驻留与量化策略的耦合容量和带宽在 3D 堆叠里是一对矛盾体。往上多堆一层 die容量涨了但热阻也涨了而且良率是乘法关系——每层 95% 的键合良率12 层下来只剩 54%。所以堆得越高越好是想当然。务实一点的做法是按访问频率分层放置数据类别访问频率建议放置量化精度容忍权重每 token 全扫极高贴近逻辑层的 DRAM 层INT4 / INT8KV Cache随上下文高独立 bank group与权重物理隔离FP8 / INT8Attention 中间量极高但体量小逻辑层片上 SRAMFP16Embedding / LM Head每 token 一次普通层即可INT8我特别想强调权重的复用次数等于 batch size这件事。batch1 时权重没有任何复用每次乘加都要为一次完整的 DRAM 读取付出代价。batch32 时同一块权重被 32 个 token 共享摊到每个 token 上的权重搬运成本降到 1/32。所以对 3D-DRAM 加速器来说能不能把 batch 做大比带宽绝对值高不高更关键。这也是为什么这类方案在线服务场景天然有并发比端侧单用户场景更容易体现出价值。推理的算术强度随 batch 变化的表达式大致是这样$$ AI(B) \approx \frac{2PB}{W KV \cdot B} $$其中 P 是参数量W 是权重字节数KV 是单序列单 token 的 KV 字节数。B 很小时分母被 W 主导AI 随 B 线性涨B 大到 KV·B 超过 W 之后AI 趋于饱和回到2P/KV这个上限。这个拐点位置决定了你的加速器在哪个 batch 区间最有价值——拐点之前加带宽收益最大拐点之后加带宽是浪费。3.3 激活功耗被忽视的真正瓶颈带宽数字好看不代表能跑得动因为 DRAM 的功耗大头是 ACT行激活不是数据传输。给一个订单量级但方向正确的估算。假设行缓冲 1 KB一次 ACT 的能量按 1.5 nJ 计具体数值跟工艺、电压、bank 数强相关这里只做量级判断。70 GB 权重按 1 KB 一行算是 7×10⁷ 次激活单 token 的激活能量约0.105 J。而数据传输这边5600 亿比特乘以 3.5 pJ/bit单 token 是1.96 J。也就是说数据搬运能量是激活能量的 18 倍。这正是 3D 键合方案最想解决的那 1.96 J。如果能把每比特能耗压到 0.5 pJ单 token 从 1.96 J 掉到 0.28 J功耗降了七倍。在 48 token/s 的吞吐下这条差异就是 94 W 和 13 W 的区别——一个需要暴力风冷一个能被动散热。但这里有个陷阱降低单位能耗的同时如果你把带宽提高了 10 倍总功耗模型会怎样答案是如果带宽提升靠的是更宽的总线 更低的频率动态功耗大致正比于 C·V²·f总线宽度翻倍、频率不变功耗翻倍但能效pJ/bit改善两个数量级总功耗反而可能持平甚至下降。如果靠的是同样的总线拉高频率那就是纯粹的功耗灾难。评估任何 3D-DRAM 方案时第一件事是看它的带宽是怎么来的是靠宽度还是靠频率这一个问题就能把方案分成两类。3.4 和连续批处理、投机解码的协同实际部署里解码从来不是孤立的 batch1 负载。连续批处理continuous batching让新请求随时插入batch 大小在 8 到 64 之间波动投机解码speculative decoding用一个小的 draft 模型一次猜 4 到 8 个 token然后并行验证。这两套机制都会改变带宽画像。连续批处理把权重访问摊薄了但同时让 KV 访问变得极其不规则——不同序列的上下文长度不同KV 读取呈长尾分布。加速器如果只优化了规整的 KV 布局遇到变长序列就会退化。我的建议是让加速器暴露一个KV block 粒度的 gather/scatter原语而不是假设 KV 是连续数组。投机解码则相反它把验证阶段变成了一个小 batch 的 GEMMk 个候选 token 同时验证算术强度比纯解码高得多。对带宽受限的加速器来说投机解码是性价比最高的软件侧优化——用可接受的额外算力换掉一部分带宽需求。如果你的硬件带宽是稀缺资源先把投机解码上了再考虑换硬件。4. 落地之前必须算清的四笔账4.1 带宽需求目标吞吐反推先给自己定一个目标吞吐然后反推带宽。这是所有评估的起点比看任何规格表都重要。目标吞吐单 token 搬运量需要的峰值带宽按 65% 有效带宽折算50 token/s71 GB3.6 TB/s5.5 TB/s200 token/s71 GB14.2 TB/s21.8 TB/s1000 token/s71 GB71 TB/s109 TB/s这里的 71 GB 是 70B INT8 权重加 8K 上下文 GQA 的 FP8 KV。看到最后一行了吗千 token 级吞吐需要百 TB/s 量级的有效带宽这已经远超任何单封装 HBM 方案能提供的水平。这就是为什么这个方向必须走 3D 键合——不是想不想的问题是 HBM 的引脚数和中介层面积算不过这个账。反过来说如果你的目标只是 50-100 token/s现有 HBM 加量化和投机解码就能做到没必要上 3D-DRAM。判断一个新技术方案值不值得第一步永远是问我的目标点在哪。4.2 容量KV Cache 会把你的规划打乱容量规划有个常见的错误做法拿模型权重大小直接当需求。真实需求是权重 峰值 KV 激活 预留。以 70B INT4 模型为例权重 35 GB。8K 上下文的 GQA 模型单序列 KVFP8约 1.34 GB32 路并发就是 43 GB。加上运行时激活和碎片实际需要80 GB 以上。如果上下文推到 32K单序列 KV 涨到 5.4 GB32 路就是 172 GB直接翻倍。这意味着 3D-DRAM 加速器在容量上会面临一个尴尬要么堆更多层热和良率双杀要么把 KV 压缩到更激进的精度要么在命中率上做文章。第三种思路是分层的——把热 KV最近访问的几十个 token放在逻辑层的 SRAM冷 KV 放在 DRAM 深处。但要小心DRAM 的访问粒度是 32 到 64 字节如果热 KV 的复用窗口太短分层带来的额外管理开销会抵消收益。我见过一些方案在这里翻车SRAM 缓存的命中率只有 40% 多但每次 miss 都要多走一次跨层协议净收益是负的。4.3 功耗与热3D 堆叠的结构性难题3D 堆叠有个绕不过去的物理事实热阻随堆叠层数上升而且发热最凶的逻辑层往往被压在 DRAM 下面散热路径要穿过好几层硅。DRAM 的温度敏感性体现在刷新率上。JEDEC 规范里85°C 以下 tREFI 是 64 ms超过 85°C 就减半到 32 ms到 95°C 再减半到 16 ms。刷新占用的带宽从 2% 涨到 8%同时刷新功耗也同步上涨。温度升高会自己吃掉带宽这个正反馈回路在堆叠结构里特别容易失控。我的经验做法是在架构阶段就把热预算当硬约束来排布把功耗密度最高的单元MAC 阵列、时钟树放在堆叠的边缘靠近散热路径不要在中间层夹心。给每个 bank group 留独立的时钟门控粒度让空闲分区真正断电而不是靠全局降频。在调度器里显式建模温度当某个分区的温度逼近阈值时主动降低对它的访问频率把流量挪到凉的分区。这本质上是把热管理做进地址映射里比事后降频优雅得多。4.4 精度量化不是免费的午餐最后这笔账最容易被忽略。为了省带宽把权重压到 INT4精度会掉把 KV 压到 INT8 甚至 INT4长上下文下的退化更明显。而且不同类型的量化对精度的影响完全不一样权重量化相对安全per-channel 或 group-wisegroup128的 INT4 在大多数模型上掉点可控。KV 量化要小心尤其是 K 的量化误差会通过 softmax 放大。实践中 K 用更高的精度、V 用更低的精度往往比两者同精度更划算。激活量化在解码阶段最麻烦因为激活是动态范围的per-token 动态 scale 会引入额外开销。一个务实的策略是混合精度 精度感知调度敏感层通常是前几层和最后一层保留 FP8中间层压到 INT4。这会带来一个问题——不同层的访问模式不一致加速器的数据通路如果只支持单一精度就得在软件层做拆解反而增加复杂度。评估加速器时问清楚它的混合精度支持粒度比看它的峰值带宽数字更有价值。5. 从仿真到流片我踩过的那些坑5.1 仿真链路怎么搭才不浪费时间评估一个新硬件方案最容易犯的错是一上来就搭 cycle-level 的完整仿真花三个月跑出来一个数字然后发现方向错了。我现在的做法是两段式先粗后细。第一段用解析模型就是前面那段 Python 的扩展版把容量、带宽、能耗、温度四个维度扫一遍参数空间。这一步通常只要一两天目的是找到值得深挖的参数区域而不是得到精确数字。参数空间里 90% 的区域是可以被粗模型直接排除的。第二段才上 cycle-level。存储侧我一般用 DRAMsim3 或者 Ramulator 2.0计算侧用 Timeloop 或者自己写的 tile-level 模型。关键是把两边的接口对齐——存储模型输出的是请求级的延迟和能耗计算模型输出的是请求序列中间的映射关系必须严格一致否则仿出来的数字全是假的。一个典型的存储配置片段长这样# 存储子系统配置示意字段名按你用的仿真器对齐 frontend: impl: Generator # 用 trace 驱动先关掉 CPU 前端 clock_ratio: 8 # 逻辑侧时钟与存储时钟的比例 memory_system: impl: HBMPIM # 或自定义的 3D 键合模型 clock_ratio: 4 channel: 16 rank: 1 bank: 32 # bank 数直接决定行冲突概率 row_size: 1024 # 1KB 行缓冲前面能量估算用到的就是它 tRC: 45 tRAS: 32 tFAW: 20 tREFI: 3900 # 约 64ms 计算时钟 subarray: 8row_size和bank这两个参数一定要按你的真实布局改默认值出来的结果毫无意义。我见过有人拿默认的 4 bank 配置去评估 32 bank 的方案然后得出bank 冲突不严重的结论——因为根本就没多少 bank 可以冲突。5.2 理想带宽和有效带宽之间的鸿沟规格表上的带宽是理想值。真实跑起来你会发现有效带宽通常在 60% 到 75% 之间访存模式排得不好会掉到 30% 以下。差距来自这几个地方损耗来源典型影响缓解手段行缓冲未命中15%–35%地址交织、长度对齐的 burst刷新占用2%–8%温度补偿刷新、bank 级错峰读写转向3%–10%按方向分组调度减少 turnaroundECC 开销6%–12%内联 ECC避免额外 burstbank 冲突5%–25%质数步长的地址映射跨层协议开销视实现可能 10%大粒度搬运减少握手次数最后一行是 3D 堆叠方案特有的。逻辑层和存储层之间的协议如果做成了细粒度的请求-应答握手开销会吃掉大量带宽。正确的做法是把接口做成大块 DMA 批量完成通知而不是逐字读写的 memory-mapped 语义。这个设计决策的影响比后面所有的调度优化加起来都大。5.3 软件栈适配编译器不会自动帮你硬件再强编译器不配合也是白搭。3D-DRAM 加速器对软件栈有三个硬要求。第一算子融合要在图层做不能指望 kernel 层。因为存储侧的计算原语是受限的编译器必须知道哪些算子能下沉、哪些必须留在主芯片。这需要图层有一个明确的可下沉性标注而不是靠后端猜。第二KV 的页表要和 DRAM 的 bank 映射对齐。这两层现在通常属于不同团队维护一个在推理框架里一个在驱动里中间隔着好几层抽象。要打通就得暴露一个物理地址提示接口让框架能把它的访问模式告知分配器。听起来很脏但这是目前唯一能拿到行局部性的办法。第三量化 scale 的处理要融进 DMA 路径。如果每次搬运都要经过一次独立的解量化 kernel那省下的带宽全被 kernel 启动和中间写回吃掉了。理想情况是 DMA 引擎在搬运过程中就把 scale 应用上去落地的数据直接可计算。一个典型的 bank 内 GEMV 分片逻辑大概是这样示意不是真实 ISA// 存储近端的 GEMV 分片每片只负责一段 K 维的部分和 void gemv_block(const int8_t *w_row, const int8_t *x_tile, int32_t *acc_local, int k_len) { int32_t scale w_row[-1]; // scale 内联在块头省一次跳转 for (int i 0; i k_len; i 16) { #pragma unroll for (int j 0; j 16; j) { // 不需要乘法器阵列用移位加实现定点乘加 acc_local[j] (int32_t)w_row[i j] * (int32_t)x_tile[i j]; } } // 本片只把部分和推给归约网络不做全局累加 push_partial(acc_local, scale); }注意最后那行分片只推部分和不做全局归约。全局归约是跨 bank 的必须由主芯片或者一个独立的归约网络统一做放在分片里会引入大量跨层通信。5.4 什么情况下这套方案不划算我给自己定了几条红线命中任何一条就不考虑上这类加速器。第一模型切换频繁的场景。如果每几小时就要换一个模型权重加载会成为主要开销。3D 堆叠的容量是固定的换模型就要重新灌数据而灌数据的通道带宽通常远低于推理时的带宽。第二batch 上不去的场景。单用户端侧推理、低 QPS 的边缘部署batch 常年是 1权重零复用。这时候算术强度只有 2 FLOP/Byte加速器的带宽优势没法转化成实际收益反而不如用大容量统一内存加激进的量化。第三需要训练或微调的场景。这是推理加速器不是训练卡。反向传播需要保存激活、需要 FP16/BF16 原精度、需要高带宽的双向通信3D-DRAM 的近存设计在这些负载上没有优势。第四生态迁移成本无法承受的场景。现有的推理栈、算子库、调优工具链都是围绕 GPU 建的。换一套新硬件意味着重新走一遍所有的适配和调优这个隐形成本经常被低估。我的经验是至少预留六到九个月的软件适配期不要指望硬件到位就能跑出规格书上的数字。5.5 我自己总结的几个实操要点做了几轮这样的评估之后有几个东西我每次都会检查写在这里供参考。看方案的带宽来源。是加宽总线还是拉高频率加宽是可持续的拉高频率在堆叠结构里会被热预算限制死。看它怎么处理 KV 和权重的流量隔离。如果这两类数据共享同一条数据通路长上下文场景下必然互相阻塞。看它的归约网络设计。部分和的归约路径带宽如果不够存储侧算得再快也要等。很多方案在这里留了瓶颈。看混合精度的支持粒度。是整层一个精度还是可以按 channel 甚至按 block 切换粒度越细软件侧的空间越大。看它的有效带宽实测值。任何只给理论峰值、不给有效带宽的方案可信度都要打问号。前面那张损耗表里的六项加起来能吃掉 40% 以上的带宽这个数字不会因为堆叠方式变了就消失。最后一条也是最重要的先把自己的负载画像清楚——batch 分布、上下文长度分布、目标吞吐、精度容忍度——再去看硬件方案。我见过太多人先选型再找理由最后花了半年时间证明一个从一开始就不该选的方案。我个人在实际评估这类加速器时的体会是3D-DRAM 方向真正的价值不在那个第一个的名头上而在于它把两个原本分开的问题——搬多少和搬多远——重新放到了一起设计。传统的做法是先定存储、再定计算、最后调总线每一层都只能在自己那一层做局部最优。而垂直整合之后你可以在架构阶段就问这个计算该放在哪一侧这个自由度本身就是收益。至于具体某个方案值不值得投回到最朴素的三个数字就够了目标吞吐反推的带宽需求、峰值并发下的容量缺口、以及每 token 的能耗预算。这三个数字对不上后面所有的架构讨论都是空转。