用粗略估算进行大规模推理成本分析(13分钟阅读)

TLDR AI 新闻

摘要

一篇技术指南,展示如何使用简单的粗略估算来估计大规模服务AI模型的成本,涵盖GPU带宽、矩阵乘法、令牌定价和用户容量。

你需要以下信息来计算每位用户的美元价格:GPU硬件规格、上下文长度、模型的活跃参数数量以及产品特定因素。模型架构的具体细节影响出奇地小,除非是完全不同的东西,比如扩散模型。本文展示了如何用纸笔进行数学计算。这个练习应该能揭示推理引擎中的各种优化如何帮助SaaS产品保持盈利。
查看原文
查看缓存全文

缓存时间: 2026/06/16 00:52

# 用粗略估算理解大规模推理成本 来源:https://injuly.in/blog/napkin-inference-cost/index.html 首页 (https://injuly.in/) 关于 (https://injuly.in/about/) 博客 (https://injuly.in/blog/) 作品集 (https://injuly.in/work/) 现在 (https://injuly.in/now/) 如果你将 AI 模型作为产品栈的一部分(在 2026 年很难不这样做),那么你一定计算过一块 A100/H100/H200/B200 等 GPU 能带来多少收益。这直接影响基于订阅的产品的定价。我想证明,即使模型、硬件和推理引擎在不断演进,每个用户的美元成本仍然可以通过纸上计算轻松得出。这个练习还能揭示推理引擎中的各种优化如何帮助 SaaS 产品保持盈利。 如果你真的要在纸上计算,只需要以下信息: 1. **GPU 硬件规格**:内存带宽和峰值吞吐量(下文解释)。 2. **上下文长度**:假设为 20 万个 token。 3. **模型的活跃参数数量**:为简单起见,假设为 32B,以便在单个 GPU 上运行。 4. **对产品的了解**:是由用户提示驱动还是程序循环驱动,用户画像的占空比(文末解释)等。 模型架构的具体细节影响不大,除非是完全不同的架构(例如扩散模型)。 如果你对 LLM 的架构熟悉/了解,可以使用以下图例跳到感兴趣的部分: - 单 GPU 的资源 (https://injuly.in/blog/napkin-inference-cost/index.html#resources-on-a-single-gpu) - 矩阵乘法的成本 (https://injuly.in/blog/napkin-inference-cost/index.html#cost-of-a-matrix-multiplication) - 语言模型概述 (https://injuly.in/blog/napkin-inference-cost/index.html#an-overview-of-language-models) - 注意力机制的详细说明 (https://injuly.in/blog/napkin-inference-cost/index.html#attention-in-greater-detail) - 使用 KV 缓存减少计算量 (https://injuly.in/blog/napkin-inference-cost/index.html#reducing-compute-with-kv-cache) - 一个 token 的成本是多少? (https://injuly.in/blog/napkin-inference-cost/index.html#how-much-does-a-token-cost) - 实际能服务多少用户? (https://injuly.in/blog/napkin-inference-cost/index.html#how-many-users-can-you-serve-realistically) - 在单 GPU 上优化服务数百用户 (https://injuly.in/blog/napkin-inference-cost/index.html#optimizing-for-hundreds-of-users-on-a-gpu) - 每秒 Token 数 (https://injuly.in/blog/napkin-inference-cost/index.html#tokens-per-second) - 每个用户的美元成本 (https://injuly.in/blog/napkin-inference-cost/index.html#dollar-cost-per-user) ## 单 GPU 的资源 对于市面上任何 GPU,你都可以在其规格表上找到两个关键指标: 1. **峰值吞吐量**:每秒执行的浮点运算次数。通常以 TeraFLOP/s 为单位(1 TFLOP/s = \(10^9\) 次运算/秒)。 2. **内存带宽**:从全局内存(VRAM)传送到寄存器(SRAM)的数据量。通常以 TB/秒为单位。 我们将假设 FP-8 量化来计算吞吐量,当然也很容易调整为 FP-16 的计算。 ## 矩阵乘法的成本 如果你点击了这篇文章,你就知道 AI 模型在**巨大的**矩阵上执行**大量的**矩阵乘法。那么,我们首先计算矩阵乘法的成本也就不足为奇了。 假设有两个矩阵:\(A_{N \times d}\) 和 \(B_{d \times M}\)。它们的乘积为矩阵 \(O_{N \times M}\)。根据中学代数,\(O\) 的每个元素可以计算为: $$ O^{i,k} = \sum_{j=1}^{d} A^{i,j} * \{B\}^{j,k} $$ 由此,我们得到了对矩阵乘法“成本”的第一个认识。对于每个 \(O^{i,k}\),我们需要从一个初始值 0 开始,然后: 1. 从内存中加载 \(A^{i,j}\)。 2. 从内存中加载 \(B^{j,k}\)。 3. 将它们相乘。 4. 将步骤 3 的结果加到累积和上。 每个元素要重复执行 \(d\) 次。因此,一个 \( (N,d) * (d,M) \) 矩阵乘积的成本是 \(2NMd\) 次内存访问和 \(2NMd\) 次浮点运算。通过一种称为“分块”(tiling)的优化,内存访问次数可以降低到约 \(d(N+M)\)。继续往下看并不需要了解细节,但好奇的读者可以参考 Alvin 的博客文章 (https://alvinwan.com/how-to-tile-matrix-multiplication/)。 ## 语言模型概述 LLM 的核心很简单——它们接收一个由 N 个词组成的序列,然后生成第 N+1 个词。每个词被表示为一个具有 d 个分量的向量。通过反复应用一个叫做“注意力”(attention)的函数(稍后解释),它们预测下一个词。一次前向传播大致如下: ``` y = input() # y = 大小为 N x d 的矩阵 for each layer in the network: y = attention(y) # 将最后一层的输出转换为词概率。 # W_vocab = 大小为 d x vocab_len 的矩阵, # vocab_len 是模型词汇表中所有词的数量。 token_probs = softmax(y * W_vocab) next_tok = token_probs(argmax(token_probs)) # next_tok 是一个 (1 x d) 向量 ``` 这也是 LLM 被称为自回归(auto-regressive)的原因。它们可以对其输出反复进行多次前向传播,直到生成一个 `<STOP>` token。这是一个简化概述,跳过了 RoPE、中间的 MLP 层、最后的 token 采样等。如前所述,你可以将这些内容加入,并仍能通过费米估算 (https://en.wikipedia.org/wiki/Fermi_problem) 验证我们的数学结果大致正确。 ## 注意力机制的详细说明 让我们用放大镜来仔细看看 `attention` 函数。如前所述,输入是一个矩阵 \(X \in \mathbb{R}^{N \times d}\),其中 \(X_i\) 是一个单独的 \(d\) 维向量。对于网络中的每一“层”,模型存储矩阵 \(W_Q, W_K, W_V \in \mathbb{R}^{d \times d}\),并按如下方式计算“注意力”: \(Q = X \cdot W_Q\), \(K = X \cdot W_K\) 以及 \(V = X \cdot W_v\) \(Attention(Q,K,V) = softmax(Q \cdot K^T / \sqrt{d}) \cdot V\) 或者用 Python 表示: ``` def attention(X, W_q, W_k, W_v): Q, K, V = X @ W_q, X @ W_k, X @ W_v Q_KT = Q @ K.transpose(2, 1) return softmax(Q_KT / sqrt(d_model)) @ V ``` 其中 `@` 是两个矩阵的点积。实际上,多个 LLM 对话是并行处理的。因此推理是批处理(batched)的——我们同时处理 B 个对话。这意味着我们的输入序列 \(X \in \mathbb{R}_{B \times N \times d}\)。在纸上验证一下数学是否成立。在我们的 Python 代码中,只需要改变转置参数: ``` - Q_KT = Q @ K.transpose(2, 1) + Q_KT = Q @ K.transpose(0, 2, 1) ``` 不过,我们的注意力实现有一个问题:它**从内存中读取了过多的数据**。让我们来看一个简单的矩阵乘法,比如 \(K = X \cdot W_k\)。提供模型的公司允许你与模型对话最多约 20 万个 token。对于一个单一的 `X @ W_k` 矩阵乘法,它看起来像这样: ``` X = tensor(B, N, d) # "B" 个对话,每个最多 "N" 个 token W_k = tensor(d, d) # 权重没有批次维度 O = tensor(B, N, d) # X @ W_k 的结果 ``` 注意输出是另一个 \(\mathbb{R}_{B \times N \times d}\) 张量。根据矩阵乘法成本部分所述,要计算每个 \(O^b \in \mathbb{R}_{N \times d}\),我们需要 \(d(N+d)\) 次内存读取和 \(2Nd^2\) 次计算操作。对于批大小 \(B\)(并发对话数),我们得到: 1. **浮点运算次数**:\(2BNd^2\)。 2. **内存访问次数**:\(Bd(N+d)\)。 假设 N 约为 20 万,d 为 8192(前沿实验室之外最常见的值)。这意味着为单个用户生成一个 token 需要 **26 万亿次浮点运算** 和 **17 亿次内存访问**。这还是用了分块矩阵乘法的情况。这**远远**多于内存读取次数。实际上,计算操作比内存访问多出四个数量级。下一批输入必须等待数万个周期,让 GPU 完成当前批次。 在纸上画出上述矩阵乘法后,你会注意到一个关键细节——我们浪费了太多资源来重新计算**在前一次迭代中已经处理过的** token 的矩阵乘法乘积。回想一下,LLM 是自回归的。它们: 1. 接收一个 token 列表 \(X\),进行一系列矩阵乘法。 2. 在 L 层(L 层)上反复执行 `attention(X, weights)`,并生成一个新 token \(x\)。 3. 将 \(x\) 附加到 \(X\)(即到目前为止的对话)。 4. 将步骤 3 的输出放回步骤 1,直到生成一个 "STOP" token。 为了避免为每个新词**再次**重新处理整个对话历史,推理引擎会缓存 \(K, V\) 对以备重用。 ## 使用 KV 缓存减少计算量 每个对话的中间输出,即 \(K\) 和 \(V\),会在每一层被缓存,存储在 VRAM 中一个称为 **KV 缓存** 的区域。像 vLLM 这样的推理引擎允许程序员决定 VRAM 的多少百分比应该预分配给这个缓存。当然,这并不像听起来那么简单。为了充分利用分配给 vLLM 的内存,有很多巧妙的技巧,详情可以参见原作者的这个演讲 (https://youtu.be/5ZlavKF_98U)。 对于我们的粗略估算,KV 缓存的存在允许一个简化:**对于每次前向传播,我们只需处理最近生成的一个词,而不是整个历史**。即,我们不再处理 \(X \in R_{N \times d}\),而是处理 \(X \in R_{1 \times d}\)(最新的 token)。`X @ W_k` 的数学现在变为: ``` X = tensor(B, 1, d) W_k = tensor(d, d) O = tensor(B, 1, d) ``` 对于批大小 \(B\)(并发对话数),我们得到: 1. 约 2620 万次内存访问 2. 约 5240 万次运算 这意味着每次内存访问,我们只需要执行**两次**运算,而不是一万次。对于整个批次,我们每访问一次内存就执行 **2*B 次运算**。这太棒了! 现在,让我们拿出市面上最快 GPU 的规格表,计算出每秒可以生成多少 token(以及为多少用户)。 ## 一个 token 的成本是多少? 接下来,我们以 NVIDIA B200 作为主要示例。通过网络搜索,你会发现它的规格如下: 1. 内存带宽:8 TB/s(即每秒访问 \(8 \times 10^9\) 字节)。 2. 计算强度:4500 TFLOP/s(即每秒处理 \(4500 \times 10^9\) 次运算)。 看到了吗?一块 Blackwell 级别的 GPU 每秒处理数据的速度是加载数据的 **562 倍**。换句话说,要充分利用这样的芯片,我们应该**每加载一个字节就进行 562 次计算**。如果计算次数更多(例如没有 KV 缓存),内存带宽就会闲置。如果计算次数更少,计算核心就会闲置。 我们目前是 **2*B** 次运算。那么,为了充分利用 B200 的计算和带宽预算,我们应该服务多少用户? \(2B = 562 \implies B = 331\) 使用单块 NVIDIA B200 GPU,我们应该**同时服务 331 个用户**,以最大化投资回报。当然,这是理论上限。实际上,VRAM 是有限的。我们必须在其中塞入模型权重以及巨大的 KV 缓存。 ## 实际能服务多少用户? 我们假设一个 32B 的稠密模型——这差不多是一块 192GB 芯片能轻松支持的上限。这可以是 Gemma、Qwen、DeepSeek 等任何模型。不同的技术和参数大小最终结果大致相同,误差可能在 5-10% 以内。 回到我们的问题:我们有一个 32B 模型。它占用 VRAM 中的 320 亿字节,即 32 GB。假设上下文窗口 \(N\) = 20 万个 token。每层的输入是 \(N \times d\) 维。对于每一层,我们需要为一对 K 和 V 矩阵存储 \(2Nd\) 字节。我们这个规模的模型通常有 \(d=8192\) 和 \(L=64\)。由此得到: ``` KV cache size = 2 * N * L * d = 2 * 200_000 * 64 * 8196 = 210 GB (!!) ``` 这比我们的 GPU 拥有的 VRAM 还要多!在这里,我引入另一个此类模型使用的优化:分组查询注意力 (Grouped-Query-Attention, GQA) (https://arxiv.org/pdf/2305.13245)。如果你对注意力机制不熟悉,可以先保存这个链接,以后阅读,只需相信我的结论:它大约将 KV 缓存大小降低了 8 倍。但如果你熟悉多头注意力 (Multi-Head-Attention),那么 GQA 很简单:它在多个查询头之间共享相同的 KV 头。因此,对于 64 个查询头,我们总共只使用 8 个 KV 头;即查询头 0-7 共享第一个 KV 头,查询头 8-15 共享下一个,依此类推。 使用 GQA 后,我们的 KV 缓存现在约为 **26 GB** *每对话序列(或每用户)*。权重已经占用了 **32GB**,那么剩余 160GB 中可以在 KV 缓存中存储多少个并发对话上下文?160 / 26 = 6。因此大约可以同时进行 6 个对话。这看起来……有点少。 ## 在单 GPU 上优化服务数百用户 即使你向客户宣传的上下文窗口是 20 万 token,大多数上下文实际上永远不会达到这个限制。根据你的产品,LLM 对话的中位数长度可能在 4-4 万 token 之间。我们可以将 KV 缓存分成小块,然后根据用户 token 使用量的增长将这些块分配给不同的用户。已经废弃/冷却的对话线程可以从缓存中清除。这就是 vLLM 通过分页注意力 (PagedAttention) (https://hamzaelshafie.bearblog.dev/paged-attention-from-first-principles-a-view-inside-vllm/) 所做的。根据中位数用户活动,每块 Blackwell 芯片可以服务 **40-60 个用户**,具体取决于你假设的对话长度。 请记住,你的产品性质也很重要。在大多数类似 ChatGPT 的应用中,用户阅读的时间多于提示。对于一个中位数的聊天会话,用户可能有 80% 的空闲时间。此时,GPU 的占空比为 20% (!)。因此实际上,根据应用的类型,一块芯片可以舒适地服务约 300-800 个用户。当然,如果你使用某种机制以循环方式向智能体发送数据,这种情况就不成立。 ## 每秒 Token 数 前面我们提到,在 100% 占空比下我们可以舒适地支持 6 个用户。但用户观察到的速度是多少?同样,这直接取决于我们的内存与计算比例。 对于一次前向传播,我们会将模型权重 + KV 缓存从 VRAM 一次性传送到寄存器。然后,每加载一个字节,我们就执行 2*B 次运算。因此总耗费时间为: ``` 数据传输时间(秒) = 内存 (GB) / 带宽 (GBps) = 190GB / (8*10**3) GBps = 0.02375 秒 = 23.75 毫秒 计算时间 = 190 * 2 * 6 / 4500 TFLOPs = 0.5 毫秒 ``` 由于两者并行进行,计算核心 98% 的时间处于空闲状态。每 24 毫秒,我们生成 B=6 个 token。对于 1 秒(=1000 毫秒),我们为 6 个用户生成大约 250 个 token,即每个用户每秒约 40 个 token。假设 LLM 的输出是供阅读的(而不是像在后台构建 SQL 查询那样),40 token/秒已经超过大多数人的阅读速度。 ## 每个用户的美元成本 这很大程度上取决于你是拥有硬件还是租用硬件。以每块 B200 40,000 美元计算,每个用户的终身成本为 `40_000 / 用户数`。在 100% 占空比的情况下(成本最差情况),每个用户成本为 6,000 美元。实际上,如果每块 GPU 服务 500 个用户,每个用户的终身成本约为 133 美元,再加上数据中心/维护费用。 如果租用 GPU,成本更直接。以 B200 每小时 3 美元计算,每个用户每小时的成本为 `3 / 用户数`。对于 `用户数=500`,每个用户每小时成本约为 0.006 美元,即每月 **4.32 美元**。因此,只要你向他们收取超过 4.32 美元的费用,你的运营成本就覆盖了。 作为一家人工智能公司,你很可能拥有不止一块 GPU(我希望如此)。因此,你将跨集群对用户进行负载均衡。

相似文章

每个AI提示都需花费成本——这改变了一切

Reddit r/AI_Agents

文章认为,AI的真正挑战不仅在于构建更智能的模型,更在于以规模化的方式降低成本效率,强调了减少token使用、提升速度以及优化基础设施的重要性。

AI团队悄然烧掉推理预算的五种方式

Reddit r/ArtificialInteligence

本文重点介绍了AI团队浪费推理预算的五种常见方式,并提供了提高效率的工程杠杆,针对的是正在扩展AI模型的初创公司。