GEOZ

Gemma 4 2B 模型 Cloud TPU v5e 部署实战

2026/8/6
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 读完。

核心结论

  1. 对 Gemma 4 2B 这类解码密集型任务,TPU v5e-1 的性价比优于 v6e-1:v6e 价格是 v5e 的 2.25 倍,BF16 算力高 4.66 倍,但 HBM 带宽只高约 1.9 倍;解码任务先撞带宽瓶颈,因此 v5e 用一半价格换到接近 2 倍的每美元带宽,这笔账更划算。

  2. gcloud 中不存在“v5e-1”这个名称,必须写成 v5litepod-1;配套 flex-start 运行时版本是 v2-alpha-tpuv5-lite,GCE 机器类型是 ct5lp-hightpu-1t。写错会导致创建失败,或把 v6e 的 ct6e-standard-1t 误当 v5e 使用,内存相关配置会差一倍。

  3. 配额非零不等于可用性:扫描显示 44 个区域有 TPU v5e 配额,但 v5litepod-1 的 flex-start 在 europe-west4-a/b 均被拒绝,只有 us-west4-a 实测可用;这类供应模式支持范围没有 API 能提前查询,只能逐个创建尝试。

  4. 16 GB HBM 下,Gemma 4 E2B 常驻权重实测约 8.97 GiB(只按 2B 参数估算会得到约 4.5 GiB,差额来自多模态塔),留给 KV 缓存约 5.0 GiB,约对应 29 万 token;在 16,384 上下文下大约 17 路并发触顶。

  5. 实测性能峰值: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 调试指南的续篇。同一套 MCP 工具,同样是 Antigravity CLI 来驱动,芯片换成更小更便宜的 v5e,故障模式也换了一批。这次的模型是 google/gemma-4-E2B-it,跑在单颗 Cloud TPU v5e 上,有实测吞吐、实测延迟,成本拆解也是基于实测数据算出来的。

任务和上一次一样:一个 DevOps/SRE 助手,大脑是自托管的 Gemma 4 模型。MCP 服务器负责申请 TPU、部署 vLLM 容器、找到端点,然后用这个端点分析 Cloud Logging 的输出。31 个工具,一个 server.py,走 stdio 传输。

这次变的是目标:TPU v6e-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_NETWORKTPU_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_LENMAX_NUM_BATCHED_TOKENSLIMIT_MM_PER_PROMPTTENSOR_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:880estimate_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 querybenchmarking_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.mdget_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不支持。

晓婷深圳
本文由 晓婷 审核,最后更新于 2026年8月7日
联系编辑 →
← 返回文章列表
分享到:微博

版权与免责声明:本文仅用于信息分享与交流,不构成任何形式的法律、投资、医疗或其他专业建议,也不构成对任何结果的承诺或保证。

文中提及的商标、品牌、Logo、产品名称及相关图片/素材,其权利归各自合法权利人所有。本站内容可能基于公开资料整理,亦可能使用 AI 辅助生成或润色;我们尽力确保准确与合规,但不保证完整性、时效性与适用性,请读者自行甄别并以官方信息为准。

若本文内容或素材涉嫌侵权、隐私不当或存在错误,请相关权利人/当事人联系本站,我们将及时核实并采取删除、修正或下架等处理措施。也请勿在评论或联系信息中提交身份证号、手机号、住址等个人敏感信息。