Gemma 4 2B 模型 Cloud TPU v5e 部署实战
AIAI Summary (BLUF)
本教程是 v6e-1 调试指南的续篇,演示如何将 Gemma 4 2B 模型部署到单颗 Cloud TPU v5e 芯片上。文章对比了 v5e 与 v6e 的规格和价格,剖析了部署中的常见坑(如加速器类型命名、配额不等于可用性、网络配置),并通过实测数据指出:对于带宽受限的 2B decode 场景,v5e 的性价比要优于 v6e。
核心洞察
先说结论:跑 2B 模型的解码任务,v5e 的性价比确实比 v6e 合适。4.7 倍的算力差距在表格里看着吓人,但这类任务先撞到的是带宽瓶颈,用一半价格换接近 2 倍的带宽,这笔账算得过来。配额不等于可用性这个坑,作者自己就栽了不少时间,开 TPU 之前先把坑 2 读完。
核心结论
对 Gemma 4 2B 这类解码密集型任务,TPU v5eCloud TPU v5e,Google Cloud 第五代 TPU 芯片,单芯片 16GB HBM、800 GiB/s 带宽、峰值 BF16 197 TFLOPs,以较低成本提供推理/训练能力。-1 的性价比优于 v6e-1:v6e 价格是 v5e 的 2.25 倍,BF16 算力高 4.66 倍,但 HBM 带宽只高约 1.9 倍;解码任务先撞带宽瓶颈,因此 v5e 用一半价格换到接近 2 倍的每美元带宽,这笔账更划算。
gcloud 中不存在“v5e-1”这个名称,必须写成
v5litepod-1;配套 flex-startTPU 的一种预付费使用模式,价格约为按需的一半,但可能会有容量限制。 运行时版本是v2-alpha-tpuv5-lite,GCE 机器类型是ct5lp-hightpu-1t。写错会导致创建失败,或把 v6e 的ct6e-standard-1t误当 v5e 使用,内存相关配置会差一倍。配额非零不等于可用性:扫描显示 44 个区域有 TPU v5e 配额,但 v5litepodTPU v5e 的加速器类型名称,在 gcloud 中必须使用该名称。-1 的 flex-start 在 europe-west4-a/b 均被拒绝,只有 us-west4-a 实测可用;这类供应模式支持范围没有 API 能提前查询,只能逐个创建尝试。
16 GB HBM 下,Gemma 4 E2B 常驻权重实测约 8.97 GiB(只按 2B 参数估算会得到约 4.5 GiB,差额来自多模态塔),留给 KV 缓存约 5.0 GiB,约对应 29 万 token;在 16,384 上下文下大约 17 路并发触顶。
实测性能峰值:64 并发、128 上下文时解码吞吐最高 1,877.7 output tok/s;16 并发、8K 上下文时总吞吐最高 28,260 tok/s;单流解码稳定在 90–127 tok/s,上下文从 128 涨到 15,000 时 TPOT 仅从 7.76 ms 增至 8.42 ms。
这个项目想干什么
这篇文章是 v6e-1 调试指南的续篇。同一套 MCPModel Context Protocol - a protocol that enables AI models to access external tools, data sources, and services to enhance their capabilities and context awareness. 工具,同样是 Antigravity CLI终端驱动的AI辅助编程工具,作为MCP客户端管理模型部署。 来驱动,芯片换成更小更便宜的 v5e,故障模式也换了一批。这次的模型是 google/gemma-4-E2B-it,跑在单颗 Cloud TPU v5e 上,有实测吞吐、实测延迟,成本拆解也是基于实测数据算出来的。
任务和上一次一样:一个 DevOps/SRE 助手,大脑是自托管的 Gemma 4 模型。MCP 服务器负责申请 TPU、部署 vLLM一个高性能的LLM推理和服务库,为DeepSeek-OCR提供优化的推理能力,支持流式输出和批量处理。 容器、找到端点,然后用这个端点分析 Cloud Logging 的输出。31 个工具,一个 server.py,走 stdio 传输。
这次变的是目标:TPU v6eCloud TPU v6e (Trillium),第六代 TPU 芯片,单芯片 32GB HBM、1638 GB/s 带宽、峰值 BF16 918 TFLOPs,性能更强但价格更高。-1 换成 TPU v5e-1,模型从 Gemma 4 4B 换成 Gemma 4 2B。
这篇文章要回答的问题是:v5e 每芯片小时的价格大约是 v6e 的一半。它是台缩水一半的机器,还是更差?
纸面上的芯片参数
| 规格(单芯片) | TPU v5e(v5litepod) | TPU v6e(Trillium) | 比值 |
|---|---|---|---|
| HBM 容量 | 16 GB | 32 GB | 2.0× |
| HBM 带宽 | 800 GiBps | 1,638 GBps | 约 2× |
| 峰值 BF16 | 197 TFLOPs | 918 TFLOPs | 4.66× |
| 峰值 INT8 | 393 TOPs | 1,836 TOPs | 4.67× |
| 单芯片机器类型 | ct5lp-hightpu-1t | ct6e-standard-1t | — |
| 按需价格 | 约 $1.20 / 芯片小时 | 约 $2.70 / 芯片小时 | 2.25× |
| Flex-start(见坑 2) | 约 $0.60 / 芯片小时 | 约 $1.35 / 芯片小时 | 2.25× |
表格里的数字来自 Google 官方的 v5e 和 v6e 文档。有个细节:v5e 的带宽单位是 GiBps,v6e 是 GBps,两者不是一个单位。换算之后实际比值约 1.9 倍,不是干净的 2 倍。这种差别在咱们讨论的层面可以忽略,但别把“2 倍带宽”当成精确数字到处引用。
这张表基本就是整个故事的缩影。v6e 贵 2.25 倍,换来 2 倍内存、约 2 倍带宽和 4.7 倍的裸算力。对 2B 模型的解码任务来说,瓶颈在带宽,v5e 的定价几乎正好踩在点上。如果是预填充密集或者长上下文的任务,矩阵单元确实在大量消耗算力,v6e 那 4.7 倍的优势就开始显现了。后面实测跑出来的数据,跟这个判断对得上。
Antigravity CLI
Antigravity CLI 是 Gemini CLI 的继任者,一个终端里的 AI 辅助编程工具。安装步骤见 Getting Started with Antigravity CLI。
启动之后,对着一个 Google Cloud 项目做认证:
agy
一个配置文件,四个读取方
相比 v6e 那套环境,这次最大的结构调整:部署参数只放在一个地方,tpu.env,所有环节都从那里读。
# tpu.env — the single source of truth for this rig.
GOOGLE_CLOUD_PROJECT=aisprint-491218
GOOGLE_CLOUD_REGION=us-west4
GOOGLE_CLOUD_ZONE=us-west4-a
MODEL_NAME=google/gemma-4-E2B-it
ACCELERATOR_TYPE=v5litepod-1
TENSOR_PARALLEL_SIZE=1
读它的地方有四个:server.py(通过 load_dotenv)、mcp-run.sh(MCP 配置实际启动的就是它)、Makefile(用 -include 引入)和 set_env.sh。四处遵循同一个规则:真实的环境变量优先于文件里的值。load_dotenv 不会覆盖已有的,wrapper 只导出没设置过的,Makefile 用 ?=。所以想临时换个区域跑,直接 GOOGLE_CLOUD_ZONE=europe-west4-a make status 就能覆盖。
要换区域,改一处就行,不用在五个地方改。听起来是小事,直到你花一个下午去调试一个部署,最后发现是 mcp_config.json 里残留的旧区域在捣鬼。
MCP 的配置跟着变得很平淡,这正是目的:
{
"mcpServers": {
"tpu-2B-v5e1-devops-agent": {
"command": "/home/xbill/gemma4-queens/tpu-2B-v5e1-devops-agent/mcp-run.sh",
"args": [],
"env": {}
}
}
}
对比 v6e-1 的版本,那个 JSON 里内联了七个环境变量。每一个都是配置可能漂移的地方。
(那个绝对路径,如果你打算照抄,值得多看一眼:它指向的仓库目录和这篇文章写作时的目录已经不是同一个了。MCP 配置里的绝对路径就是那种东西,目录改名之后它照样存在,启动的时候悄悄失败。mcp-run.sh 的存在,是为了让其他杂七杂八的东西都别进这个配置文件。但它自己的路径,仍然是一串写死的字符串。)
坑 1:v5e 在 gcloud 里叫 v5litepod
先把话说在前面。gcloud 根本不知道“v5e-1”是什么。
- 加速器类型:v5litepod-1,不是 v5e-1
- Flex-start 运行时版本:v2-alpha-tpuv5-lite
- 机器类型(走 GCE 路线的话):ct5lp-hightpu-1t,不是 ct6e-standard-1t
“v5e-1”写进文章没问题,放进 gcloud 参数就永远不认。最后一行比看起来重要得多:ct6e-standard-1t 是 v6e 的机器类型,如果它出现在你的笔记里被标成 v5e,后面所有跟内存有关的数字都会差一倍。我写这篇文章的时候,就在这个仓库自己的 demo 页面里发现了这个标注错误。
坑 2:配额不等于可用性,可用性不等于供应模式
这是最耗时的一个坑。
MCP agent 里有个 get_zones_with_available_quota 工具,扫描所有区域的 TPUV5sLitepodPerProjectPerZoneForTPUAPI:
> get_zones_with_available_quota
返回了 44 个配额非零的区域。每一个都是。
这个数字毫无用处。配额只意味着允许创建,说明不了容量是否存在,更说明不了你申请的供应模式在那个位置受不受支持。后面这一点才是真正费时间的地方。
Flex-start 的 v5litepod-1,实际可用范围比配额表暗示的窄得多:
> create a v5litepod-1 queued resource in europe-west4-a
ERROR: FLEX_START provisioning model is not supported for accelerator type
"v5litepod-1" in location "europe-west4-a"
europe-west4-b 同样被拒。us-west4-a 接受。这是逐个尝试创建试出来的,没有任何 API 能提前告诉你。
所以这个项目默认的区域(europe-west4-a,从 v6e 那套继承来的)永远不可能创建这套环境,重试多少次都一样。顺带提一句:参考文档里写了 europe-west4-b 支持 v5e 的 flex-start,但示例用的是 v5litepod-4。单芯片规格的实际可用范围,比表格给人的印象要窄。
坑 3:满屏都是创建失败,先查参数,别怀疑区域
find_tpu 这个 agent 会把可变状态写进 tpu_zones_status.md。每次创建失败记一笔,之后跳过这些已知有问题的可用区。
某次状态表长这样:
| Zone | Quota Available | TPU v5e-1 Started Successfully | Details |
| europe-west4-a | Yes | No | creation failed |
| europe-west4-b | Yes | No | creation failed |
| europe-west1-b | Yes | No | creation failed |
| ... 40 more rows ... |
四十多个区同时报 out of capacity,先去查参数。每次创建请求都带了 --network=vpc-glitnir,但这个项目里根本没有这个 VPC。aisprint-491218 只有自动模式的 default 网络。
修法:TPU_NETWORK 和 TPU_SUBNETWORK 默认留空,让 gcloud 用项目默认网络。然后重置状态表,重新做一次实时配额扫描,把表填回来。
通用教训:一个资源在所有区都失败,问题基本不在区。
另外,这个文件是可变状态,别当文档。手工编辑没意义,find_tpu 跑一次就把你的改动覆盖了。
部署
可用区的事解决之后,部署就只剩一个 MCP 调用。生成的 gcloud 命令:
gcloud alpha compute tpus queued-resources create vllm-gemma4-qr \
--node-id=vllm-gemma4-qr-node \
--project=aisprint-491218 \
--zone=us-west4-a \
--accelerator-type=v5litepod-1 \
--runtime-version=v2-alpha-tpuv5-lite \
--provisioning-model=flex-start \
--max-run-duration=4h \
--valid-until-duration=4h \
--labels=purpose=flex-start \
--metadata-from-file=startup-script=startup_script.sh
这两个 duration 参数是本地策略。平台本身允许 flex-start 最多跑 7 天,maxRunDurationSeconds 默认就是 7 天。这里故意设 4 小时,因为这台机器是演示用的,自动到期的 TPU 比一台需要人记着去关的 TPU 便宜。--valid-until-duration=4h 管另一头:请求在 WAITING_FOR_RESOURCES 里最多等多久,超过就放弃,不会一直排着。
启动脚本拉 vllm/vllm-tpu:nightly,然后起服务:
vllm serve google/gemma-4-E2B-it \
--max-model-len 16384 \
--tensor-parallel-size 1 \
--disable_chunked_mm_input \
--max_num_batched_tokens 4096 \
--limit-mm-per-prompt '{"image":4,"audio":1}' \
--enable-auto-tool-choice \
--tool-call-parser gemma4 \
--reasoning-parser gemma4
--tensor-parallel-size 是 1。v5e-1 是单芯片。任何 v5e-1 配置里出现 4,基本是从大拓扑复制过来的,引擎初始化会失败。
有两个结构细节值得抄走。
启动脚本自己去取 HF token。渲染完的脚本作为实例元数据上传,token 如果直接烤在脚本里,任何有 compute.instances.get 权限的人都能读到。启动时通过元数据服务从 Secret Manager 读 hf-token,重试 30 分钟。这样即使实例创建之后才授予 IAM 权限,也能赶上。整个 token 段没开 set -x。
服务参数只放在一个函数里。_vllm_serve_flags() 从 MAX_MODEL_LEN、MAX_NUM_BATCHED_TOKENS、LIMIT_MM_PER_PROMPT、TENSOR_PARALLEL_SIZE 拼出 vLLM 的参数列表。启动脚本模板里的占位值也是这几个变量。部署的两条路径和生成的一次性命令,不会互相打架。
对了,模板是用 str.format() 渲染的。bash 文件里出现任何字面 { 或 },都会在 format() 阶段直接炸掉。shell 的花括号展开、${VAR}、JSON 字面量,全都要转义成 {{ 或 }}。上面 --limit-mm-per-prompt 里的 JSON 就是这个陷阱。
坑 4:内存账比你想的更紧
16 GB 在这里开始吃紧,而且从参数量上不容易看出来。
Gemma 4 E2B 标称 2B,但 vLLM 里的常驻权重占用大约 8.97 GiB。按 2B 参数和 bfloat16 直接算,会觉得才 4.5 GiB 左右。差的这部分主要来自多模态塔。
在 v5e 芯片上这笔账:
物理 HBM 16 GB (~15.5 GiB)
vLLM 利用率上限 (0.9) ~13.9 GiB
常驻模型权重 ~8.97 GiB
─────────────────────────────────────────
留给 KV 缓存 ~5.0 GiB
同样的模型放 v6e 芯片,留给 KV 的约 19.8 GiB。KV 池大了差不多 4 倍,价格只多 2.25 倍。
按实测每个 token 约 18 KiB 的 KV 占用(15 层会实际生成缓存,bfloat16),~5.0 GiB 大概对应 29 万 token。在配置的 16,384 上下文下,大约 17 路并发。
说清楚:~5.0 GiB 和 ~29 万这两个数是拿实测权重占用和芯片公开容量算出来的,v5e 引擎日志里读不到。8.97 GiB 和 18 KiB/token 是实测值。KV 部分当靠谱估算看,按这个数去规划机群之前,拿自己的 vLLM 初始化日志再对一遍。
17 路并发这个上限有实测支撑,这个扫描正好在这个位置碰顶。
实测:并发 × 上下文扫描
用容器里的 vllm bench serve 在 TPU VM 上跑,每个请求输出 128 个 token,20 个网格点。整个网格零失败。这点值得单独说,因为同样的模型在 v6e-1 的网格上,156 个点里有 27 个失败或被跳过。
机器终于开起来了。接下来是一轮完整的压测,这张表是结果。
| 并发 | 上下文 | req/s | 输出 tok/s | 总 tok/s | 平均 TTFT | p99 TTFT | TPOT |
|---|---|---|---|---|---|---|---|
| 1 | 128 | 0.99 | 126.9 | 254 | 22.5 ms | 75 ms | 7.76 ms |
| 1 | 1,024 | 0.96 | 123.4 | 1,110 | 48.4 ms | 105 ms | 7.79 ms |
| 1 | 4,096 | 0.87 | 110.8 | 3,655 | 149.5 ms | 208 ms | 7.92 ms |
| 1 | 8,192 | 0.83 | 105.8 | 6,875 | 174.5 ms | 180 ms | 8.15 ms |
| 1 | 15,000 | 0.70 | 90.1 | 10,643 | 351.2 ms | 358 ms | 8.42 ms |
| 4 | 128 | 3.38 | 432.5 | 865 | 55.7 ms | 94 ms | 8.84 ms |
| 4 | 1,024 | 3.40 | 435.1 | 3,916 | 32.4 ms | 42 ms | 8.97 ms |
| 4 | 4,096 | 3.12 | 399.7 | 13,190 | 61.5 ms | 100 ms | 9.47 ms |
| 4 | 8,192 | 2.78 | 355.8 | 23,126 | 92.9 ms | 169 ms | 10.41 ms |
| 4 | 15,000 | 1.45 | 185.3 | 21,904 | 615.0 ms | 2,616 ms | 13.23 ms |
| 16 | 128 | 8.41 | 1,076.9 | 2,154 | 169.5 ms | 289 ms | 13.59 ms |
| 16 | 1,024 | 7.33 | 938.8 | 8,449 | 198.5 ms | 352 ms | 15.53 ms |
| 16 | 4,096 | 4.79 | 612.8 | 20,221 | 365.8 ms | 974 ms | 23.10 ms |
| 16 | 8,192 | 3.40 | 434.8 | 28,260 | 983.1 ms | 2,207 ms | 29.20 ms |
| 16 | 15,000 | 1.32 | 169.3 | 20,003 | 3,319 ms | 8,220 ms | 68.73 ms |
| 64 | 128 | 14.67 | 1,877.7 | 3,755 | 341.7 ms | 398 ms | 31.52 ms |
| 64 | 1,024 | 11.52 | 1,474.9 | 13,274 | 686.5 ms | 1,347 ms | 37.68 ms |
| 64 | 4,096 | 6.43 | 822.9 | 27,154 | 2,505 ms | 5,277 ms | 55.99 ms |
| 64 | 8,192 | 2.66 | 340.3 | 22,119 | 3,077 ms | 6,870 ms | 69.24 ms |
| 64 | 15,000 | 1.59 | 203.1 | 24,003 | 7,755 ms | 16,516 ms | 72.96 ms |
先看两组峰值。解码峰值在 64 并发、短上下文,1,877.7 output tok/s。总吞吐峰值在 16 并发、8K 上下文,28,260 total tok/s,这是 prefill 吞吐。RAG 形态的业务,对外报数字用后者。
看这张表
单流解码稳定在 90 到 127 tok/s,几乎是条直线。上下文从 128 涨到 15,000,TPOT 从 7.76 ms 挪到 8.42 ms。8% 的劣化,换来的是 117 倍上下文长度。2B 模型的解码瓶颈在带宽,15K token 的 attention 开销跟权重流式读取相比,根本排不上号。交互式 agent 看这一行就够:单用户不管塞多少上下文,都能拿到稳定 120 tok/s 左右。
批处理在 4K 上下文以内扩得很好,过了 4K 就停。相对单流的加速比:
| 上下文 | 4 并发 | 16 并发 | 64 并发 |
|---|---|---|---|
| 128 | 3.41× | 8.49× | 14.80× |
| 1,024 | 3.53× | 7.61× | 11.96× |
| 4,096 | 3.61× | 5.53× | 7.43× |
| 8,192 | 3.36× | 4.11× | 3.22× |
| 15,000 | 2.06× | 1.88× | 2.26× |
看 8,192 那一行。并发从 16 加到 64,吞吐不升反降,434.8 掉到 340.3 tok/s。这块卡的 KV 池大概 5 GiB,能装约 29 万 token。64 个请求乘 8,192 token,KV 需求 52 万,池子装不下。vLLM 开始抢占、重算,同一个 prefill 要付两遍钱。
15,000 token 这列是墙,不是坡。并发 1 很轻松,90 tok/s,TTFT 351 ms。并发 16,p99 TTFT 到了 8.2 秒。并发 64,16.5 秒。好消息是没挂,vLLM 选择排队而不是 OOM 崩掉。但 p99 16 秒,体验直接崩了。
这台机器实际能扛的负载范围:
| 负载形态 | 建议并发 | 实测效果 |
|---|---|---|
| 聊天 / 短提示词(≤1K) | 64 | 约 14.7 req/s,TTFT 低于 700 ms |
| 工具调用 agent(≤4K) | 16 | 约 4.8 req/s,TTFT 约 366 ms,p99 小于 1 秒 |
| RAG / 长文档(8K) | 16 | 总吞吐 28,260 tok/s,TTFT 约 1 秒 |
| 极限上下文(15K) | 1 到 4 | 超过 4 并发 p99 就没法用 |
(图:总吞吐随上下文与并发变化)
(图:TTFT 和 p99 随并发变化)
成本
压测跑完,算账。
先交代口径。flex-start 这列我第一版就算错了。Google 不公布 flex-start 的每芯片小时价格,它走 Dynamic Workload Scheduler 计费,官方只说“折扣最高 53%”。所以下面所有 flex 数字都是按 on-demand 列表价打五折算的,是“最高 53%”里的保守端。每 token 成本也按这个上界算。
on-demand 是列表价,us-west4:
| 配置 | Flex-start(估算) | On-demand(列表价) |
|---|---|---|
| TPU v5e-1(16 GB HBM) | 约 $0.60 / 小时 | $1.20 / 小时 |
| TPU v6e-1(32 GB HBM) | 约 $1.35 / 小时 | $2.70 / 小时 |
| TPU v6e-4(128 GB HBM) | 约 $5.40 / 小时 | 约 $10.80 / 小时 |
| GCE 1× NVIDIA L4(24 GB) | 约 $0.30 / 小时(spot) | 约 $1.00 / 小时 |
| GCE 8× NVIDIA L4(192 GB) | 约 $2.40 / 小时(spot) | 约 $8.00 / 小时 |
| Cloud Run 1× L4(serverless) | — | 约 $1.20 到 $1.50 / 小时 |
| AWS EC2 g6.xlarge(1× L4) | 约 $0.35 / 小时(spot) | 约 $0.97 / 小时 |
| Azure NVadsA10v5(1× A10G) | 约 $0.35 / 小时(spot) | 约 $1.05 / 小时 |
参考一下。v5e spot 价约 $0.35/芯片小时,v6e spot 在 $0.60 到 $1.30 之间。比 flex-start 便宜,但 spot 会被回收,提前 30 秒通知,跟 flex-start 有界运行时长是两种风险。
价格随区域浮动,下单前自己查。这个代码库里还有个坑:server.py:880 的 estimate_deployment_cost,费率表写的是 "v5e": 0.12,跟 $1.20 的列表价差了一个数量级。硬编码的费率表别信,包括这张。
每百万输出 token 成本,直接用压测数据算:
| 并发 | 上下文 | 输出 tok/s | Flex($0.60/小时) | On-demand($1.20/小时) |
|---|---|---|---|---|
| 1 | 128 | 126.9 | $1.314 / M | $2.627 / M |
| 4 | 128 | 432.5 | $0.385 / M | $0.771 / M |
| 16 | 128 | 1,076.9 | $0.155 / M | $0.310 / M |
| 64 | 128 | 1,877.7 | $0.089 / M | $0.178 / M |
| 16 | 1,024 | 938.8 | $0.178 / M | $0.355 / M |
| 64 | 1,024 | 1,474.9 | $0.113 / M | $0.226 / M |
| 16 | 4,096 | 612.8 | $0.272 / M | $0.544 / M |
| 64 | 4,096 | 822.9 | $0.203 / M | $0.405 / M |
头条数字:flex-start 最优批处理下,每百万输出 token 只要 $0.089。
但更有用的头条是价差。同一块芯片、同一个模型、同一个计费小时,并发 1 跑和并发 64 跑,每 token 成本差 14.8 倍。闲置的 TPU 就是成本的全部。一个常年并发 1 的 agent 服务,正在花 $1.31/M 买本来 $0.09/M 就能拿到的 token。
devops agent 的成本问题很现实:你的负载大概就是并发 1。SRE 助手回答一个运维的问题,就是一条单流。再怎么调,也没法把一个运维变成 64 个并发。
这种场景下,成本控制靠运行时长,不靠批处理。所以这套环境设了 --max-run-duration=4h,没有接受 flex-start 默认的七天。一个会自动过期的 agent 盒子,成本有上限。一个被遗忘的盒子,$0.60/小时一直烧,服务零个用户。按会话时长开,让它过期。别把一台并发 1 的闲置 TPU 拿去跟托管 API 比 $/M,那个对比会输得很惨,而且活该。
反过来,上限也别卡太死。重新开机器不是瞬时的。flex-start 请求会停在 WAITING_FOR_RESOURCES 排队等容量,官方没有 SLA。这个项目里等过两三个小时。演示机设 4 小时合理,真在用的东西只给 1 小时就不合适了。
对比 2B 模型服务:v5e-1 vs v6e-1
先说清楚,这张表里的 req/s 不能直接比,这个坑我不想糊弄过去。v5e-1 的数据来自 vllm bench serve,每个请求固定输出 128 token。v6e-1 的 Gemma 4 2B 网格跑的是另一套压测工具,输出窗口短得多。输出越短,req/s 虚高得越厉害,基本是线性关系。v6e 网格那个约 140 req/s 的峰值,主要量的是 prefill 加停止,不是持续生成。
能诚实对比的维度都列在下面,测量方法不影响结论:
| 维度 | v5e-1(实测) | v6e-1 | 结论 |
|---|---|---|---|
| 常驻权重(E2B) | ~8.97 GiB | ~8.97 GiB | 模型属性,两边一致 |
| 留给 KV 的 HBM | ~5.0 GiB(推算) | ~19.8 GiB(实测) | v6e 约 4 倍池子 |
| KV 容量 | ~290K tok(推算) | 1,151,744 tok(实测) | v6e 约 4 倍 |
| 单流持续 decode | 90–127 tok/s | ~214 tok/s | v6e 约 1.7 到 1.8 倍 |
| 短上下文单流 TTFT | 22.5 ms(宿主机上) | ~210 ms(走网络) | 不可比,观测位置不同 |
| 网格失败数 | 0 / 20 | 27 / 156 | v5e 完整跑完网格 |
| 并发 16 下最大可用上下文 | ~8K | 65K 配置 | v6e 明显胜出 |
| flex 价格 | ~$0.60/hr | ~$1.35/hr | v5e 便宜 2.25 倍 |
单流 decode,v6e 快约 1.7 到 1.8 倍,约 214 对 120 tok/s,价格贵 2.25 倍。按单个交互用户的每美元产出算,v5e 反而领先。这跟硬件对得上:decode 是带宽瓶颈,v6e 带宽正好翻倍,算下来实际转化率约 85%。
v6e 的溢价花在内存上,算力那边 2B 模型根本吃不满。4 倍 KV 池子是两个模型的分水岭,一个能在 16 并发下扛住 8K 上下文,另一个已经开始抢占。短上下文聊天场景,你付的钱买的是永远用不上的余量。跑长文档 RAG 还有真实并发的时候,v5e 那个约 5 GiB 的池子就是硬约束,调什么都救不回来。
FLOPS 差距几乎看不出来。v6e 的 BF16 吞吐是 4.7 倍,decode token 只多 1.8 倍。这个落差是最明确的信号:2B 模型推理卡在内存带宽上,矩阵乘法根本不是瓶颈。反过来看,v6e-4 那种 4 芯片 128 GB 的拓扑对 2B 模型严重过剩,之前的 v6e-4 扫描里 2B 模型已经撞上宿主 CPU 分发开销的天花板,TPU 处理 token 比 host 调度还快。
2B 模型该选哪块芯片?
| 场景 | 选择 | 理由 |
|---|---|---|
| 单 agent SRE 助手,一个操作员 | v5e-1 flex | 并发 1 下每美元 token 最多,$0.60/hr |
| 聊天服务,短上下文,高并发 | v5e-1 flex | 14.7 req/s,输出 token 单价 $0.089/M |
| 工具调用 agent,上下文 ≤4K,并发 ≤16 | v5e-1 flex | KV 余量完全够用,p99 低于 1 秒 |
| 8K 以上文档的 RAG,并发 >16 | v6e-1 | v5e 约 5 GiB 的 KV 池会抢占,吞吐直接反转 |
| 长上下文(15K+),任何真实并发 | v6e-1 | v5e 的 p99 TTFT 会拖到 8 到 16 秒 |
| 任何 2B 模型跑到 4 芯片规模 | 都别用 | 卡在 host 上,不如把钱花在更大的模型上 |
一句话:Gemma 4 2B 的默认选择是 v5e-1。v6e-1 值钱在内存余量,速度上没有实质提升。
验证部署
资源变成 ACTIVE 之后,端点发现是动态的。discover_vllm_url() 列出当前 zone 的 queued resource,取第一个 ACTIVE 的,解析节点和外部 IP,拼出 http://{ip}:8000。别硬编码,每次资源重建 IP 都会变。
> verify_model_health健康检查通过!
• 状态: 通过
• 响应: "你好!是的,模型正常工作。我……"
• 延迟: 0.31 秒
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24"><title>退出全屏模式</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
> query_queued_gemma4_with_stats 什么是 TPU?性能统计
• 首 token 延迟 (TTFT): 0.022s
• 吞吐: 126.9 tokens/s
• 平均 TPOT: 7.76 ms
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24"><title>退出全屏模式</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
最后一个坑:裸 /v1/completions 在 -it 模型上会返回空的 completion。make query 和 benchmarking_suite.py 都在用这个端点,所以看到空结果别以为是部署坏了,这是预期行为。server.py 全程走 /v1/chat/completions。新代码都放 chat 端点,raw completions 只有 prefill-only 基准才用得上。
工具集
> make tools
31 个工具已注册。
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24"><title>退出全屏模式</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
GemmaTools.md 和 get_help 工具都从 mcp.list_tools() 拉工具列表,不会跟 @mcp.tool() 装饰器漂移。加一个工具,跑 make tools,文档自动重新生成。不管哪条路,源头都是 grep -n "^@mcp.tool" server.py。
要 fork 这套代码的话,有个分工值得保留:create_tpu_queued_resource 是非破坏性的,只碰它拿到的那个 id,这是 find_tpu 能安全做 zone 扫描的前提。manage_queued_resource 是破坏性的,会删掉 zone 里除指定主资源外的所有 queued resource。两个动词,两个爆炸半径,互不重叠。
还有,别随手销毁 queued resource,除非你真想清楚了。Teardown 不是日常调试的一部分,flex-start 等容量也没有成文的 SLA,这个项目上最长等过两三个小时。
总结
单颗 TPU v5e 芯片服务 Gemma 4 2B,单流 90 到 127 tok/s,批处理 1,878 tok/s,flex-start 每小时 $0.60。按峰值批处理折算,每百万输出 token 约 $0.089。大多数 agent 实际跑在并发 1,这时候每百万 token 是 $1.31。
对比 2.25 倍价钱的 v6e-1,v5e 让出约 1.8 倍 decode 速度和 4 倍 KV 池。decode 差距是带宽问题,按每美元算 v5e 赢。KV 差距真实存在,修不了,选 v6e 的理由跟速度无关,单纯是 16 GB 不够用。
最耗时间的三件事全是配置问题,容量一个都没占。flex-start 的 v5litepod-1 只有 us-west4-a 接受。create flags 里一个不存在的 VPC 看起来跟全局容量短缺完全一样。还有一张标注 v5e 的笔记里混进了 ct6e-standard-1t,后面所有内存数字都悄悄翻了一倍。配额不等于可用容量,可用容量不等于随开随用,一个在所有 zone 都失败的资源,问题从来不在 zone。
常见问题(FAQ)
v5e跑2B模型为什么性价比比v6e高?
因为2B解码是带宽瓶颈,v5e带宽约为v6e的1.9倍,但价格只有一半,带宽性价比更高;v6e算力虽强但解码用不上。
gcloud中TPU v5e的加速器类型怎么填?
用v5litepod-1,而不是v5e-1;机器类型是ct5lp-hightpu-1t,Flex-start运行时用v2-alpha-tpuv5-lite。
TPU配额显示可用但创建失败是怎么回事?
配额只是允许创建,不代表有容量或供应模式支持;flex-start的v5litepod-1实际区域支持范围比配额表窄,需逐个尝试创建,比如us-west4-a支持而europe-west4-a不支持。
版权与免责声明:本文仅用于信息分享与交流,不构成任何形式的法律、投资、医疗或其他专业建议,也不构成对任何结果的承诺或保证。
文中提及的商标、品牌、Logo、产品名称及相关图片/素材,其权利归各自合法权利人所有。本站内容可能基于公开资料整理,亦可能使用 AI 辅助生成或润色;我们尽力确保准确与合规,但不保证完整性、时效性与适用性,请读者自行甄别并以官方信息为准。
若本文内容或素材涉嫌侵权、隐私不当或存在错误,请相关权利人/当事人联系本站,我们将及时核实并采取删除、修正或下架等处理措施。也请勿在评论或联系信息中提交身份证号、手机号、住址等个人敏感信息。



