Inference on Google Cloud: Precision, Memory and Capacity on TPU, from v5e to Ironwood A developer benchmarked Gemma 4 inference across Google Cloud TPU generations v5e through Ironwood, serving the model via vLLM's tpu-inference JAX backend and comparing against NVIDIA L4, Blackwell, AMD MI300X and AWS Inferentia. On the same v5e chip, Gemma 4 E2B served 144 tokens per second in bf16 versus 243 in int8 with 4-bit embedding tables, and a v6e-1 delivered 3.3 times the L4's throughput on the same 26B checkpoint. The writeup attributes most results to native number format, memory bandwidth, slice shape and TPU availability rules, noting a zone reporting quota for 1,536 chips can still return a stockout. This article is the written companion to the talk "Inference on Google Cloud". It covers serving Gemma 4 on Cloud TPU v5e and v6e through vLLM, measured on the gemma4-dev Cloud TPU test setups between July and October 2026, and compares them with the NVIDIA L4 on Google Cloud, Blackwell list prices, an AMD MI300X and AWS Inferentia. Four ideas explain most of the results: the number format the chip computes natively, the memory bandwidth that feeds it, the slice shape, and the rules that decide whether a TPU is there to rent at all. https://github.com/xbill9/gemma4-dev https://github.com/xbill9/gemma4-dev The hands-on steps run from a second repository, which starts one TPU and serves Gemma 4 on it with a single command: https://github.com/xbill9/gcp-inference https://github.com/xbill9/gcp-inference The slides: https://docs.google.com/presentation/d/1hT8UXOEBueZfoFdIH0ZyhYhQ3WZJ 0xbCsvNDDs8H4A/edit https://docs.google.com/presentation/d/1hT8UXOEBueZfoFdIH0ZyhYhQ3WZJ 0xbCsvNDDs8H4A/edit This article compares four TPU generations, the L4 and Blackwell GPUs beside them, and every way Google Cloud prices a TPU chip-hour. The same Gemma 4 E2B serves at 144 tokens a second on a v5e in bf16 and at 243 in int8 with 4-bit embedding tables, on the same chip. A v6e-1 serves 3.3 times the L4's tokens on the same 26B checkpoint. And a zone that reports quota for 1,536 chips can still return a stockout. | Chip | HBM | Peak bf16 | Native low precision | Max pod | On-demand | |---|---|---|---|---|---| | v5e | 16 GB | 197 TFLOPs | int8, 393 TOPs | 256 | $1.20/hr | | v6e Trillium | 32 GB | 918 TFLOPs | int8, 1,836 TOPs | 256 | $2.70/hr | | v5p | 95 GiB | 459 TFLOPs | int8; fp8 at bf16 rate | 8,960 | $4.20/hr | | TPU7x Ironwood | 192 GiB | 2,307 TFLOPs | fp8, 4,614 TFLOPs | 9,216 | $12.00/hr | Google's first TPU, in its datacenters since 2015, was an 8-bit inference chip with a 92 TOPS peak, and Google announced Ironwood on 2025-04-09 as "the first Google TPU for the age of inference". The gemma4-dev test setups measured v5e and v6e. v5p and TPU7x rows come from Google's documentation only. vLLM reaches the TPU through tpu-inference, a JAX backend. XLA compiles one program per input shape, which makes the compile the largest part of a cold start. gcloud CLI with its alpha and beta components, signed in with gcloud auth login and gcloud auth application-default login python3 with pip install 'google-adk extensions ' google-genai mcp xbill9/gcp-inference demo.sh asks for a TPU v5e-1 as a flex-start request in us-west4-a, waits for the node, opens an SSH tunnel to vLLM on the TPU's port 8000, waits for the model to load and compile, then runs a Google ADK agent against it. Flex-start stops the node by itself after 4 hours. On a TPU that is already up, ./demo.sh again skips the first two steps: ./demo.sh again == 0m01s 3/5 Connecting to it over SSH the demo reaches vLLM on the TPU's port 8000 through a tunnel 0m05s SSH works 0m11s tunnel open: localhost:8005 - TPU port 8000 == 0m11s 4/5 Starting Gemma on vLLM first start downloads and compiles: several minutes 0m30s vLLM is answering 20:21:25Z PASS tpu chat 0.198s; tool run filters= 'id = 10' quoted=8 3.71s READY == 0m37s 5/5 The demo: Gemma on the TPU counts ids with the count ids tool from the MCP server Question: How many of the ids are 10 or more? Answer with the number. Ids: 0, 2, 3, 20, 21, 22, 23, 10, 11, 12, 13 True answer: 8 tools: mcp tpu run 1/3 ok quoted=8 filters= 'id = 10' 3.87s tpu run 2/3 ok quoted=8 filters= 'id = 10' 0.51s tpu run 3/3 ok quoted=8 filters= 'id = 10' 0.49s backend runs correct right-filter errors median s filters sent tpu 3 3/3 3/3 0 0.51 id = 10 The agent is an ADK LlmAgent whose model is LiteLlm pointed at vLLM's OpenAI-compatible API, and an ADK McpToolset starts mcp server.py over stdio and offers it one tool. The model never counts. It calls count ids on that MCP server, which returns the exact count, and each run is graded on the filter it sent as well as the number it quoted. Gemma 4 E2B on the v5e sent id = 10 in all three runs. The node serves google/gemma-4-E2B-it in bf16 with vllm/vllm-tpu:v0.30.0 , one chip, no sharding. vLLM logs the chip's memory as it starts. In order: Init worker | ... | hbm= 0.0, 15.75 GiB Init model | hbm= 8.97, 15.75 GiB Memory statistics | total hbm limit gb=15.75GiB | total hbm limit cap gb=14.49GiB | total hbm used gb=8.97GiB | total hbm avail gb=5.52GiB TPU KV cache size: 321,280 tokens, Maximum concurrency for 8,192 tokens per request: 39.22x Init kv-cache | num total layers=15 | num blocks= 1255, ... | regular attn shape= num blocks, 256, 1, 2, 256 | regular attn dtype=bfloat16 | hbm= 14.51, 15.75 Gb | HBM on one v5e chip | GiB | |---|---| | Weights, bf16 | 8.97 | | vLLM's ceiling 92% of 15.75 | 14.49 | | Left for the KV cache | 5.52 | | In use after the KV cache is allocated | 14.51 | | Never allocated | 1.24 | Each of the 15 cached layers holds 1,255 blocks of 256 tokens, one KV head of dimension 256, in bf16. That is 1,024 bytes per token per layer, 15,360 bytes per token across all 15, and 321,280 token slots in all. The model's config lists 35 layers with num kv shared layers: 20 : 20 layers reuse another layer's keys and values, so the cache holds 15. Gemma 4 E2B's config allows 131,072 positions. vLLM sizes its KV cache from the memory left after the weights, so raising --max-model-len from 8,192 to 131,072 changes the concurrency and leaves the cache the same size. To change a flag on a running TPU, replace the container: gcloud compute tpus tpu-vm ssh gcp-inference-tpu-node --project