想象你是一家员工食堂的主厨。每天中午都有几百人打饭,很多人点的菜一模一样。如果每个人都从洗菜切菜开始,你就算有三头六臂也忙不过来。聪明的做法是:提前把热门菜做好,放进贴着标签、分好区的冰箱,客人一开口立刻端出来。离线推理缓存,就是给大模型这个“主厨”配一台这样的冰箱。模型处理批量请求时,很多输入是重复甚至相似的,与其每次都重新跑一遍完整推理,不如把已经算好的结果存下来,下次直接读取。这篇文章会用大白话带你搞清楚:缓存到底存什么、放在哪里、什么时候失效,以及怎样用 Embedding 做语义缓存,让看起来不一样的请求也能命中同一份答案。学完你就能在真实批量推理任务里把成本和延迟一起降下来。

岗位关联

对 AI 从业者而言,缓存优化是少数同时提升性能、降低成本、且能快速复用的工程手段;掌握它,你就能从“只会跑模型”升级为“会设计推理系统”。

目录
  1. 可视化演示
  2. 1. 缓存什么内容
  3. 2. 缓存存储设计
  4. 3. 缓存键设计
  5. 4. 缓存失效机制
  6. 5. 语义缓存Embedding
  7. 6. 批量推理优化
  8. 7. 实战注意事项
  9. 8. 监控与评估
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示精确缓存哈希匹配、语义缓存Embedding相似度搜索、双路缓存架构与成本分析曲线,支持模式切换查看各环节细节。

本节配有交互动效(进入视口后自动加载,约 3KB)。

精确缓存哈希匹配 Prompt+参数组合计算SHA-256哈希 哈希命中直接返回缓存结果 key = hash(prompt + model + params) O(1)查找但仅匹配完全相同请求

1. 缓存什么内容

想象你是食堂主厨。过去每来一桌客人,你都要从洗菜、切菜、起锅烧油开始。后来你发现,很多客人点的其实是同一道热门菜,于是你提前把几道菜做好放进冰箱,客人下单后直接盛盘上桌。离线推理也一样:模型每处理一个请求都要消耗算力,但如果请求是重复的,把上次结果存下来,下次直接取用,就能大幅省下时间和成本。

对应到工程上,缓存至少可以分四层。第一层是重复输入请求:一模一样的问题,直接返回同一份答案。第二层是完整推理输出:模型生成的文本、分类标签、检测框等,都是最值得缓存的对象。第三层是模型中间特征:比如同一个系统提示词或同一批 few-shot 示例对应的中间特征,可以复用前半段计算,不用从零开始 Prefill。第四层是序列化结果:把后处理之后的 JSON 或 Protobuf 也存下来,避免下游每次都要重新解析。

工程实践中,最简单的做法是在入口做精确匹配的完整输出缓存,命中率稳定后再考虑中间特征缓存。完整输出缓存实现成本低、收益直观;中间特征缓存能省掉更多计算,但需要和模型解码器深度配合,适合 prompt 前缀高度重复的场景。序列化结果缓存则适合下游需要多次读取同一份结果的批处理任务。

所以,缓存不是只能“存答案”。一个成熟的推理系统会像厨房分门别类存放半成品、成品和酱料一样,把各种粒度的计算结果都管理起来。先想清楚自己任务里哪一层重复最多,再决定从哪里开始建缓存。

2. 缓存存储设计

冰箱不是一个大铁盒,它分冷藏室、冷冻室、门边格子。缓存也一样,要分存储层来设计。如果所有东西都塞在一个地方,要么取用太慢,要么空间不够用。

内存 KV 存储就像冷藏室:Redis、Memcached 或进程内字典都能在毫秒级返回,适合高频命中的热数据,但容量有限、价格相对高。磁盘持久化像冷冻柜:RocksDB、SQLite 或本地文件都能存下大量数据,断电也不丢,但每次访问多一次 IO,适合量大、复用间隔长的温冷数据。TTL 过期就是保质期:每条记录写入时带上过期时间,到期自动下架。容量淘汰策略则是冰箱塞满后扔旧菜:常用 LRU 或 LFU 算法,优先留下最近使用过或最常被使用的记录。

实际落地时,通常做成两级结构:先查内存缓存,没命中再查磁盘缓存。内存里只放大 key 和小 value,磁盘里存完整结果;同时用 TTL 控制每条记录的寿命,用淘汰策略控制总量。这样,既保证高频请求足够快,又不会让内存被低频数据占满。

这套“分区冷藏”的设计思路,和食堂备菜一模一样:最热门的菜放在顺手就能拿到的冷藏层,不常点的预制菜放冷冻柜,过期了就清理,塞不下就优先丢掉最不常吃的菜。

3. 缓存键设计

冰箱里的菜如果标签贴错,客人吃到嘴里的可能是完全不同的味道。缓存键就是那枚标签:做得不严谨,轻则命中不了,重则把 A 请求的答案返回给 B 请求。

常见的坑是只把用户输入当作缓存键。但同一个输入,换了模型版本、改了 temperature、换了业务命名空间,输出都可能不一样。所以缓存键必须把这些信息全部包进去:命名空间、模型 ID、推理参数、输入内容。一个稳定做法是把它们按固定顺序拼接成一个字符串,再做 SHA-256 哈希,得到一个定长 key。

还有一种工程细节是冲突处理。虽然 SHA-256 碰撞概率极低,但如果担心极端情况,可以在 key 里保留原文指纹,命中后再做一次原文比对。这就像在冰箱盒子上同时贴了菜名和制作日期,双重确认,减少串味风险。

key=SHA256(namespace∣model_id∣params∣input)\text{key} = \text{SHA256}(\text{namespace} | \text{model\_id} | \text{params} | \text{input})
缓存键的本质是“这道菜的身份证明”,身份信息越完整,越不会拿错菜。
  1. 命名空间先拼上业务线或租户标识,保证 A 业务读不到 B 业务的缓存。
    namespace\text{namespace}
  2. 模型版本标识模型升级后 key 自动变化,旧缓存不会污染新结果。
    model_id\text{model\_id}
  3. 推理参数temperature、top_p 等参数变了,答案可能变,必须放进 key。
    params\text{params}
  4. 输入内容输入原文要先做稳定序列化,避免字典顺序不同导致 key 漂移。
    input\text{input}
  5. SHA-256 哈希把上面的长字符串压缩成固定长度 key,同时降低碰撞概率。
    SHA256(payload)\text{SHA256}(\text{payload})

4. 缓存失效机制

再好的冰箱也有保质期。菜放过期不能上桌,菜谱升级后旧菜也要全部撤下。缓存的失效机制,就是这套“保质期管理”规则。

首先是手动清理。当模型或业务规则有重大变更时,运维平台需要能一键清空某个命名空间下的缓存,像厨房大扫除一样重置状态。其次是自动过期。每条缓存记录写入时设置 TTL,读取时发现过期就惰性删除,后台也可以定期扫描清理。再次是版本升级失效:因为模型版本已经写进 key,升级后新 key 自然命不中旧缓存;同时可以批量删除旧版本 key,避免空间浪费。最后是持续监控命中率,及时发现问题。

失效机制不是越复杂越好,而是要和业务节奏匹配。离线推理任务通常是按天或按版本周期重算的,只要 TTL 能覆盖一个重算窗口,再加上模型版本入 key,绝大部分脏数据问题都能被挡住。

厨房的经验是:大扫除要定期做,但不是每做一道菜就刷一次锅。缓存失效也一样,手动清空是救火工具,自动过期才是日常保鲜的默认机制。

命中率=命中次数命中次数+未命中次数\text{命中率} = \frac{\text{命中次数}}{\text{命中次数} + \text{未命中次数}}
命中率表示所有请求里,能从缓存直接拿结果的比例。
  1. 分子命中次数指直接从缓存读到结果的请求数量。
    hit\text{hit}
  2. 分母总请求次数命中次数加未命中次数,也就是全部请求数。
    hit+miss\text{hit} + \text{miss}
  3. 比值含义命中率高表示缓存有效,但要结合业务变化观察趋势。
    rate\text{rate}

5. 语义缓存Embedding

最妙的一招来了:食堂不仅会给一模一样的热门菜备好成品,还会给每道菜做“口味指纹”。客人点菜用的词不一样,但只要口味足够接近,厨师直接端上同一道菜,客人也能接受。这就是语义缓存。

普通缓存要求输入字符串完全相等,而语义缓存先让 Embedding 模型把请求变成向量,再计算向量之间的相似度。相似度超过预设阈值,就认为这两条请求语义相近,可以直接复用之前的推理结果。这样,用户说“如何提升大模型推理速度”和“怎么加速大模型推理”,虽然字面不同,但因为语义相近,也能命中同一份缓存。

工程上,语义缓存通常依赖向量数据库或 ANN 近邻检索引擎。请求先查向量索引,如果找到相似度超过阈值的旧结果,直接复用;否则才调用模型,并把新结果和新向量写回索引。阈值的设定非常关键:太高会退化成精确匹配,太低会把不同答案的请求错误合并。

把“口味指纹”用到推理系统里,离线批处理的收益尤其明显。很多用户问题本质是同一意图的不同表述,语义缓存能把这些相似请求全部折叠成一次模型计算,省下的算力非常可观。

cos⁡(a,b)=a⋅b∥a∥∥b∥\cos(\mathbf{a},\mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \|\mathbf{b}\|}
余弦相似度衡量的是两条请求向量在方向上有多接近,越接近 1,语义越像。
  1. 向量 a 和 b两条请求经过 Embedding 后得到的向量表示。
    a,b\mathbf{a}, \mathbf{b}
  2. 点积衡量两个向量同向的程度,同向越大值越大。
    a⋅b\mathbf{a} \cdot \mathbf{b}
  3. 模长乘积把长度差异归一化,让结果只关注“方向”而非“长短”。
    ∥a∥∥b∥\|\mathbf{a}\| \|\mathbf{b}\|
  4. 余弦值取值在 -1 到 1 之间,越接近 1 表示语义越接近。
    cos⁡(a,b)\cos(\mathbf{a},\mathbf{b})

6. 批量推理优化

缓存做的是“少做重复菜”,批量推理做的是“把多道菜一锅出”。一个订单一个订单炒菜非常费火;如果几桌人都点宫保鸡丁,备料一起切,酱汁一次调一大盆,整体效率自然更高。

批量推理优化的第一步是请求聚合。离线任务本来就有成百上千条请求,与其一条条送进模型,不如积攒成一个 batch 同时推理。第二步是计算复用:尤其在使用 Transformer 结构时,同前缀的请求可以共享部分中间计算结果,避免每个请求都从零开始做 Prefill。第三步是显存管理:batch 太大会溢出,太小又喂不饱 GPU,需要动态估算显存占用,在吞吐和稳定性之间取平衡。

实际操作中,缓存和批量往往是配合使用的:缓存负责过滤掉重复和相似请求,批量负责把没命中的请求合并成高效 batch。这就像厨房里,预制菜直接解冻上桌,新鲜菜则集中到一个灶台批量炒制,两条线互不干扰。

吞吐量=NTtotal\text{吞吐量} = \frac{N}{T_{\text{total}}}
吞吐量就是单位时间内能处理的请求数,批量策略好不好,最终看这个数。
  1. N表示一段时间内处理的请求总数。
    NN
  2. T_total表示处理这些请求消耗的总时间。
    TtotalT_{\text{total}}
  3. 吞吐量数值越大,说明 GPU 用得更满,批量优化越有效。
    NTtotal\frac{N}{T_{\text{total}}}

7. 实战注意事项

冰箱不是万能的。刚开业的食堂冰箱是空的,突然停电会变质,不同租户的菜也不能混放在同一个盒子里。离线推理缓存落地时,同样有几件容易踩坑的事。

第一是冷启动。缓存刚上线时,命中率会从零开始缓慢爬升。如果业务方对首次运行时间有要求,可以用历史日志预热缓存,把高频请求提前算好放进去。第二是一致性保证。模型或业务规则升级后,旧缓存不能继续用,必须让 key 带上版本信息或直接批量清理。第三是安全隔离。多租户场景下,A 租户的结果绝不能返回给 B 租户,所以命名空间隔离和权限校验是底线。第四是成本效益。缓存本身要占用内存和磁盘,如果存了一堆永远不命中的 key,省下的算力钱还不够交存储费。

很多项目刚开始做缓存时信心满满,结果上线后发现命中率不高,反而拖慢了主流程。所以实战中要从小范围试起:先选一个重复度最高的任务,跑通缓存链路,量化收益后再推广。主厨的智慧是先把一两道招牌菜做好,而不是第一天就把整个冰箱塞满。

净收益=(命中次数×单次推理成本)−存储与维护成本\text{净收益} = (\text{命中次数} \times \text{单次推理成本}) - \text{存储与维护成本}
判断缓存是否值得做的标准很简单:省下的钱必须大于花出去的钱。
  1. 命中次数乘以单次推理成本表示因为命中而省下的 GPU 算力费用。
    hit×costper\text{hit} \times \text{cost}_{\text{per}}
  2. 存储与维护成本包括缓存服务器、磁盘、监控和运维投入。
    storagecost\text{storage}_{\text{cost}}
  3. 净收益只有结果大于 0,缓存才值得继续投入。
    net=saving−cost\text{net} = \text{saving} - \text{cost}

8. 监控与评估

一个精明的厨房会记账:今天哪些菜从冰箱出货,省了多少洗切时间,冰箱电费花了多少。推理缓存系统同样需要一套监控指标,否则你根本不知道缓存到底值不值。

最核心的指标是命中率,包括精确命中率和语义命中率;命中率不是越高越好,要结合业务分布看趋势。其次是延迟收益:加了缓存后,平均延迟和 P99 延迟有没有下降。再次是成本节省:GPU 使用时长、推理服务实例数、总账单有没有减少。最后是调优反馈:定期分析未命中请求,看看是否有新的高频模板值得加入预热,或者语义阈值是否需要调整。

监控不是一次性工作。离线任务的数据分布会随着业务变化而漂移,今天的热门请求明天可能就没人问了。所以建议建立每周复盘机制:对比命中率、成本、延迟三条曲线,持续调整 TTL、淘汰策略、语义阈值和批量大小。

冰箱里的菜要天天数、周周清。推理缓存的魅力也在于此:它不是一锤子买卖,而是一个能持续省钱的工程杠杆。只要把监控循环跑起来,这台“智能冰箱”就会越用越顺手。

AI 应用场景

电商批量商品描述生成

阶段:离线批处理 + 结果缓存

解决的问题:电商平台每天要离线生成数万条商品卖点,很多文案模板和产品词高度重复。传统方式每次都调用大模型重新推理,GPU 成本高、耗时长。用精确缓存加语义缓存,相似商品词可以直接复用旧结果,只有真正的新表述才触发模型计算。

具体案例:某商城每天晚上离线生成商品标题,同一批 SPU 重跑时 40% 请求命中缓存,单次任务成本下降约 30%,整体生成时间从 3 小时压缩到不到 2 小时。

智能客服问题分类

阶段:特征推理 + 语义缓存

解决的问题:用户问题措辞千差万别,但意图往往相同。传统规则匹配容易漏掉变体表达,全量跑模型又太贵。使用 Embedding 语义缓存后,只有语义相似度低于阈值的请求才进入模型推理。

具体案例:某智能客服系统把用户问题向量化,按 0.85 相似度阈值命中历史结果;上线后准确率基本不变,模型调用量下降一半,服务吞吐提升 2 倍。

广告合规批量审核

阶段:模型版本升级 + 缓存失效管理

解决的问题:广告平台每次升级审核模型,都要对全部历史素材重新推理,成本极高。通过把模型版本写进缓存 key,升级时只重新计算新版本涉及的样本,旧版本结果仍可留作审计备份。

具体案例:某广告平台在模型从 v1 升到 v2 时,依靠版本化缓存跳过约 30% 未变化的样本,重算 GPU 时长下降三分之一。

Python 演示

python106 行
import timeimport mathimport hashlib  def embed(text, dim=32):    vec = [0.0] * dim    s = " " + text.lower() + " "    for i in range(len(s) - 1):        g = s[i:i + 2]        idx = (ord(g[0]) * 31 + ord(g[1])) % dim        vec[idx] += 1.0    norm = math.sqrt(sum(v * v for v in vec))    if norm > 0:        for i in range(dim):            vec[i] /= norm    return vec  def cosine(a, b):    dot = 0.0    for x, y in zip(a, b):        dot += x * y    return dot  def sha_key(text):    return hashlib.sha256(text.encode("utf-8")).hexdigest()  class InferenceCache:    def __init__(self, threshold=0.3):        self.exact_store = {}        self.semantic_store = []        self.threshold = threshold     def _expired(self, expire_at):        return expire_at < time.time()     def get_exact(self, key):        item = self.exact_store.get(key)        if item is None:            return None        value, expire_at = item        if self._expired(expire_at):            self.exact_store.pop(key, None)            return None        return value     def set_exact(self, key, value, ttl):        self.exact_store[key] = (value, time.time() + ttl)     def get_semantic(self, vec):        now = time.time()        best_sim = 0.0        best_value = None        for v, value, expire_at in self.semantic_store:            if expire_at < now:                continue            sim = cosine(vec, v)            if sim > best_sim:                best_sim = sim                best_value = value        if best_value is not None and best_sim >= self.threshold:            return best_value        return None     def set_semantic(self, vec, value, ttl):        now = time.time()        self.semantic_store.append((vec, value, now + ttl))        self.semantic_store = [            item for item in self.semantic_store if item[2] >= now        ]     def _predict(self, text):        return "summary:" + text.strip()[:20] + "..."     def infer(self, text, ttl=3600):        key = sha_key(text)        cached = self.get_exact(key)        if cached is not None:            print("[exact hit]   ", text[:10])            return cached         vec = embed(text)        cached = self.get_semantic(vec)        if cached is not None:            print("[semantic hit]", text[:10])            return cached         result = self._predict(text)        self.set_exact(key, result, ttl)        self.set_semantic(vec, result, ttl)        print("[miss]         ", text[:10])        return result  if __name__ == "__main__":    cache = InferenceCache(threshold=0.3)    queries = [        "如何提升大模型推理速度",        "怎么加速大模型推理",        "训练好的模型如何部署上线",    ]    for q in queries:        print(cache.infer(q))
目的:演示离线推理中的精确缓存 + 语义缓存双路查询机制,模拟模型预测并自动回填缓存。
  1. def embed(text, dim=32):把一句话变成 32 维向量,用字符二元组模拟轻量 Embedding。
  2. idx = (ord(g[0]) * 31 + ord(g[1])) % dim把相邻两个字符映射到向量下标,能捕捉局部语义。
  3. self.exact_store[key] = (value, time.time() + ttl)精确缓存写入时带上过期时间,实现 TTL。
  4. sim = cosine(vec, v)计算当前请求与历史请求的余弦相似度。
  5. if best_value is not None and best_sim >= self.threshold:只有相似度超过阈值才算语义命中。
  6. result = self._predict(text)真正未命中时,才调用模型计算,并随后回填两类缓存。

常见误区

误区搞一个全局 dict 把输入和输出存下来就行,用户输入变了就多存几条,反正内存够大。

正解dict 只是缓存存储的雏形。真正工程化还要设计 key:命名空间、模型版本、推理参数、输入序列化一个都不能少,否则会串缓存或命中过期结果。同时要考虑 TTL、容量淘汰、并发安全和多租户隔离,这些都不是一个普通字典能解决的。

为何常见:简单 Demo 看多了,容易把缓存本质误解成 get/set,忽略了 key 和生命周期的复杂度。

误区离线任务不就是追求复用吗?把 TTL 设成一年,命中率越高越好。

正解TTL 过长会导致模型升级后仍返回旧结果,业务一致性受损。离线推理同样要尊重模型版本和数据新鲜度。TTL 应该等于“结果保持有效的时间”,需要跟业务方约定,而不是一拍脑袋设个很大的数。

为何常见:只盯着成本优化,忘了缓存根本目的是在“正确性前提下”加速。

误区相似度阈值设成 0.99,宁可不命中也不能给错答案。

正解阈值太高,语义缓存基本退化成精确匹配,失去意义;阈值太低,又会把答案不同的请求错误合并。正确做法是拿一批标注好的相似问题画曲线,挑业务可接受的阈值,并对高敏感场景加入实体一致性校验。

为何常见:语义相似不等于答案一定相同,很多人因为怕出错而不敢放开阈值。

误区离线任务反正挂在那里慢慢跑,缓存只能省在线延迟,对离线没价值。

正解离线批量任务同样有大量重复和相似请求,缓存能显著减少 GPU 机时和电费。比如历史数据重算、报表周期生成,上一轮结果只要 key 还在有效期内,就能直接复用。

为何常见:把缓存和“在线低延迟”绑定,忽略了离线场景下成本优化的本质。

面试题

Q1. 离线推理缓存最直接想省下的是哪一项?

单选题 · easy

  • A.模型训练时间
  • B.重复推理算力← 正确答案
  • C.人工标注成本
  • D.数据清洗流程

答案:B。缓存把已经算过的推理结果保存下来,下次直接读取,省掉的是重复推理的 GPU 算力。训练时间和数据清洗不在推理缓存的作用范围内。

Q2. 缓存 key 设计时,下面哪一项必须考虑?

单选题 · easy

  • A.服务器 IP 地址
  • B.模型版本标识← 正确答案
  • C.显卡品牌型号
  • D.日志文件路径

答案:B。模型升级后输出可能变化,所以模型版本必须写进缓存 key。服务器 IP、显卡型号和日志路径与推理输出无关,不需要参与 key 拼接。

Q3. TTL 在缓存里表示什么?

单选题 · easy

  • A.缓存存储容量上限
  • B.缓存记录有效时长← 正确答案
  • C.模型推理超时时间
  • D.请求排队最大时长

答案:B。TTL 是 Time To Live,即每条缓存记录能存活多久。到期后缓存自动失效,和容量上限、推理超时、排队时长都没有直接关系。

Q4. 语义缓存判断两条请求是否相似通常用什么?

单选题 · mid

  • A.字符串编辑距离
  • B.向量余弦相似度← 正确答案
  • C.关键词词频统计
  • D.请求 URL 长度

答案:B。语义缓存先把请求转成向量,再用余弦相似度衡量方向接近程度。编辑距离只适合字面差异小的场景,词频和 URL 长度都不能反映语义。

Q5. 下面哪个属于缓存存储层的常见分层?

单选题 · mid

  • A.内存 KV 与磁盘持久化← 正确答案
  • B.CPU 与 GPU 混合部署
  • C.训练集与验证集划分
  • D.灰度与正式环境切换

答案:A。缓存存储层通常把高频数据放内存 KV,低频数据放磁盘持久化。CPU/GPU 部署、数据集划分和环境切换都不属于缓存存储分层。

Q6. 哪些情况需要让缓存失效?

多选题 · mid

  • A.模型版本升级← 正确答案
  • B.业务规则变化← 正确答案
  • C.缓存命中率上升
  • D.租户数据隔离调整← 正确答案

答案:ABD。模型升级、业务规则变化、租户隔离调整都可能让旧结果不再正确,必须失效。命中率上升说明缓存效果好,不需要失效。

Q7. 缓存未命中时,系统通常应执行什么操作?

单选题 · mid

  • A.直接返回空结果
  • B.调模型计算并回填← 正确答案
  • C.删除最近一条缓存
  • D.增加请求重试次数

答案:B。未命中说明没有可复用结果,正确流程是调用模型推理,再把新结果写入缓存。返回空或删数据都会破坏服务逻辑。

Q8. 语义缓存在生产中需要注意哪些问题?

多选题 · hard

  • A.相似请求答案可能不同← 正确答案
  • B.向量检索会有性能开销← 正确答案
  • C.缓存永远不需要过期
  • D.多租户结果不能串用← 正确答案

答案:ABD。语义相似不等于答案一定相同,需要加校验;向量检索自身有计算成本,要考虑延迟;多租户必须隔离。缓存永远需要过期,否则会返回过期结果。

Q9. 下面哪个公式刻画了缓存净收益?

单选题 · hard

  • A.命中节省成本减存储开销← 正确答案
  • B.模型参数规模乘显存占用
  • C.请求总量除以总推理耗时
  • D.缓存容量除以 GPU 利用率

答案:A。净收益 = 命中次数 × 单次推理成本 - 存储与维护成本。只有节省的钱大于存储开销,缓存才值得做。其余选项分别是显存、吞吐和容量相关的指标。

Q10. 关于批量推理与缓存结合,下列说法正确的是?

多选题 · extreme

  • A.聚合请求可提高 GPU 吞吐← 正确答案
  • B.同前缀输入能复用中间特征← 正确答案
  • C.缓存命中后仍需重新 Prefill
  • D.显存上限决定最大 batch 大小← 正确答案

答案:ABD。批量聚合能提高吞吐,同前缀输入可以复用中间特征,显存上限决定 batch 不能无限增大。缓存命中后直接取结果,不需要重新 Prefill,所以 C 错误。