By the end you can:
| Chip | Year | Answer to |
|---|---|---|
| v1 | 2015 | inference cost: one 256×256 int8 array, DDR3-bound |
| v2, v3 | 2017–18 | training: HBM, bf16, ICI 2D torus, 1,024 chips |
| v4 | 2020 | scale and failures: OCS, 3D torus, SparseCore |
| v5e / v5p | 2023 | cost vs scale: 256-chip 2D vs 8,960-chip 3D |
| v6e | 2024 | 256×256 MXU, 4.7× v5e |
| 7x Ironwood | 2025 | inference at scale: FP8, 192 GiB, 9,216 chips |
| 8t / 8i | 2026 | split: training torus vs inference SRAM + Boardfly |
Arithmetic over peak FLOP/s; bytes over the bandwidth of the link that carries them. Overlapped: the max. Not overlapped: the sum, at most 2× worse.
| Chip | BF16 | FP8 | HBM BW | BF16 intensity |
|---|---|---|---|---|
| TPU v5e | 197 TFLOP/s | — | 819 GB/s | 241 |
| TPU v5p | 459 TFLOP/s | 459 TFLOP/s | 2.77 TB/s | 166 |
| TPU v6e | 918 TFLOP/s | 920 TFLOP/s | 1.64 TB/s | 560 |
| TPU7x Ironwood | 2.31 PFLOP/s | 4.61 PFLOP/s | 7.38 TB/s | 313 |
| TPU 8t | 996 TFLOP/sp | 5.98 PFLOP/sp | 6.53 TB/s | 153p |
| TPU 8i | 1.1 PFLOP/sp | 8.81 PFLOP/sp | 8.6 TB/s | 128p |
| H100 SXM | 989 TFLOP/s | 1.98 PFLOP/s | 3.35 TB/s | 295 |
| B200 (HGX) | 2.25 PFLOP/s | 4.5 PFLOP/s | 8 TB/s | 281 |
p provisional: from the JAX Pallas hardware table, not in Google's published spec.
Weight bytes dominate when B is small, and each weight byte is used once per token.
Split the contracting dimension over 2 chips only if each half's math outlasts shipping the partial sums.
By the end you can:
Ironwood figures, per TensorCore: two TensorCores per chip, each on its own chiplet. HBM and VMEM sizes from the JAX hardware table; VREG and VPU shapes from the Scaling Book. Earlier chips differ in counts (v5p: 4 MXUs of 128×128 per core), not in structure.
Utilization of one weight tile: M rows streamed, K × N of an S × S array filled.
| 256×256, full tile | M = 8 | M = 256 | M = 2,048 |
|---|---|---|---|
| Utilization | 1.5% | 33% | 80% |
| Chip | VMEM per chip |
|---|---|
| v5e, v5p, v6e, 7x, 8t | 128 MiB |
| 8i | 384 MiB |
| H100 | 30 MB SMEM + 50 MB L2 |
This is the lever TPU 8i pulls with 384 MB of SRAM.
T(8,128)(2,1) and int8/fp8 are T(8,128)(4,1): 2 or 4 values per 32-bit sublane.| Chip | TensorCores | JAX devices |
|---|---|---|
| v4, v5p | 2 | 1 (megacore) |
| v5e, v6e | 1 | 1 |
| Ironwood | 2 chiplets | 2 |
| 8t / 8i | 1 / 2 | not documented |
Ironwood's die-to-die link is 6× one ICI link: keep a chip's two chiplets on the busiest mesh axis.
By the end you can:
| Chip | Docs, per chip both ways | Per link, one way | Wici per axis |
|---|---|---|---|
| v5e | 400 GB/s / 4 links | 50 GB/s (book 45) | 100 GB/s |
| v5p, Ironwood | 1,200 GB/s / 6 links | 100 GB/s (book 90) | 200 GB/s |
| 8i | 2,400 GB/s | not published | — |
Divide by links, then by two. Wici counts the two links of one axis.
| Slice | Wraparound | Diameter (hops) |
|---|---|---|
| v5e 16×16 | both axes | 16 |
| v5e 4×4 | none | 6 |
| Ironwood 4×4×4 | all axes | 6 |
| Ironwood 2×2×2 | none | 3 |
| Ironwood 8×16×16 | all axes | 20 |
| Link | GB/s | HBM ÷ link |
|---|---|---|
| HBM | 7,380 | 1 |
| ICI, all 6 links | 600 | 12 |
| ICI, one link | 100 | 74 |
| PCIe (v6e figure) | 32 | 231 |
| DCN | 12.5 | 590 |
TP and FSDP on ICI. Data parallelism across DCN. MaxText: dcn_tensor_parallelism "never recommended".
| Scale-up domain | Hops | In-network reduction | |
|---|---|---|---|
| NVL72 | 72 GPUs, 1.8 TB/s each | 1 | yes (SHARP) |
| Ironwood | 9,216 chips, 1.2 TB/s each | 20 across an 8×16×16 slice | no |
| 8i | 1,152 chips, 2.4 TB/s each | up to 7 | CAE on chip |
| Trn3 | 64 or 144 chips | switched | — |
GPU and Trainium converge on switched domains of 72 to 144. TPU keeps a torus for training and adds Boardfly for inference.
| Pod | Chips | Hosts | BF16 FLOP/s | HBM |
|---|---|---|---|---|
| v5e | 256 | 32 | 5.0e16 | 4 TiB |
| v5p | 8,960 | 2,240 | 4.1e18 | 840 TiB |
| Ironwood | 9,216 | 2,304 | 2.1e19 | 1,728 TiB |
Ironwood FP8: 42.5 EFLOP/s per pod. Google's "1.77 PB" is 9,216 × 192 GB in decimal units; the docs say 192 GiB.
By the end you can:
| Component | Parameters per layer | Training FLOPs per layer |
|---|---|---|
| Gated MLP | 3DF | 18BTDF |
| Attention projections | 2DH(N+K) | 12BTDH(N+K) |
| Dot-product attention | none | 12BTSNH |
| Vocabulary | 2DV total | 6BTDV |
LLaMA-3-70B: MLP 56.4B, attention 12.1B, vocab 2.1B. 6.3e24 FLOPs on 15T tokens: 45 days on a v5p pod, 9 on an Ironwood pod at 40% MFU.
| Quantity | Value |
|---|---|
| Parameters | 70.5B (MLP 80%) |
| Training FLOPs, 15T tokens | 6.3e24 |
| Forward FLOPs per token | about 141 GFLOP |
| KV per token, bf16 | 320 KB |
| Weights, bf16 | 141 GB: fits on one Ironwood chip (206 GB), 65 GB left for cache |
Attention dominates only beyond 65,536 tokens for D = 8,192.
Prefill: grows with T. Decode: about G ≈ 8, at any batch.
Decode attention is memory-bound at every batch size: each sequence reads its own cache.
Each expert sees only its share of tokens, so the batch must be E/k times larger.
| E/k | Compute-bound batch | |
|---|---|---|
| DeepSeek-V3 shape, v5e, int8 weights | 32 | 3,840 |
| Same, Ironwood, fp8 weights, bf16 math | 32 | about 5,000 |
| Same, Ironwood, fp8 math too | 32 | about 10,000 |
By the end you can:
| Notation | PartitionSpec | Meaning |
|---|---|---|
| A[IX,JY] | P('X', 'Y') | I over X, J over Y |
| A[IXY,J] | P(('X','Y'), None) | I over X and Y together |
| A[I,J]{UX} | unreduced={'X'} | partial sums still to add over X |
| Case | Sharding | Collective |
|---|---|---|
| 1 | J not sharded on either | none |
| 2 | J sharded on one operand | AllGather it |
| 3 | J sharded on both, same axis | AllReduce, or ReduceScatter at half cost |
| 4 | I and K sharded on the same axis | AllGather one first |
Tensor parallelism's second matmul is case 3. FSDP's weights are case 2.
V per device; independent of ring size. Divide by the number of axes used. Floor: N/2 µs of hop latency.
| 1 GB per device, Ironwood | AG | AR | A2A | AG over 3 axes |
|---|---|---|---|---|
| Time | 5 ms | 10 ms | 1.25 ms | 1.7 ms |
xla_jf_spmd_threshold_for_windowed_einsum_mib.| Chip | C/Wici per axis |
|---|---|
| v5p (book) | 2,550 |
| Ironwood (docs) | 11,535 |
By the end you can:
FLOPs a chip must do per byte moved on one torus axis.
| Chip | α |
|---|---|
| v5p | 2,550 (book), 2,295 (docs) |
| Ironwood | 11,535 |
| TPU 8t | about 2,490 (provisional) |
| H100, across nodes | 2,475 |
| Scheme | Condition | v5p | Ironwood |
|---|---|---|---|
| DP / FSDP | B/X>α/MX | 850 (3 axes) | 3,845 |
| TP | Y<MYF/α | 11-way | 2.5-way |
| FSDP + TP | B/N>α2/(MXMYF) | 113 | 2,320 |
| DP over DCN | B/slice>C/Wdcn | 73,440 | 185,000 |
For LLaMA-3-70B (F = 28,672), tokens per chip or per slice.
v5p pod, 8,960 chips
Ironwood pod, 9,216 chips
ici_fsdp_parallelism: -1: FSDP fills the slice.dcn_data_parallelism: -1: data parallelism across slices.dcn_tensor_parallelism: "never recommended".logical_axis_rules: named dimensions (embed, mlp, heads) to mesh axes.| Quantity | Value |
|---|---|
| Parameters / FLOPs | 406B / 3.8e25 (15.6T tokens) |
| Time at 40% MFU | about 33 days |
| Per pod, 16M batch | 2.1M tokens (DCN needs 73,440) |
| Per chip | 234 tokens (FSDP+TP needs 61) |
| Mesh | about 1,000 FSDP × 8 TP per pod, 8 DP across pods |
By the end you can:
Prefill
Decode
Attention always reads every cache; the MLP is weights or FLOPs, whichever is longer.
Bcrit≈WhbmCmath⋅2bytes per weight| bf16 | fp8 weights, bf16 math | fp8 throughout | |
|---|---|---|---|
| Ironwood | 313 | 156 | 313 |
| 8i (provisional) | 128 | 64 | 512 |
| Weights, KV | Batch | Step | KV / weights / FLOPs | tok/s/chip |
|---|---|---|---|---|
| bf16 | 64 | 14.0 ms | 11.6 / 2.4 / 0.5 | 570 |
| bf16 | 128 | 25.7 ms | 23.3 / 2.4 / 1.0 | 623 |
| fp8 | 64 | 7.0 ms | 5.8 / 1.2 / 0.5 | 1,140 |
KV-bound at every batch. Fewer bytes doubles throughput; more FLOPs does nothing.
By the end you can:
| When | Stack | Status |
|---|---|---|
| 2024–25 | JetStream | archived 2026-02-01 |
| to 2025 | vLLM on PyTorch/XLA | deprecated; repo archived 2026-11-15 |
| Oct 2025 | tpu-inference: JAX models + PyTorch via torchax | 3.6× / 2.1× over PyTorch/XLA |
| Oct 2026 | vLLM JAX for JAX models; vllm-torchtpu for PyTorch | current |
New PyTorch port: vllm-torchtpu. Existing torchax ports keep working.
As of 2026-10-23. tpu-inference is being renamed vLLM JAX and focuses on JAX models; its torchax path still exists. New PyTorch-native TPU serving is in vllm-torchtpu. SGLang has the same split (sglang-jax, sglang-torchtpu).
torchax.Tensor is a torch.Tensor holding a jax.Array.__torch_dispatch__ maps each ATen op to jnp / lax; jax.jit traces it to StableHLO.model_fn(weights, kv_caches, inputs) -> (kv_caches, ...).K and V interleaved, packed 2 (bf16) or 4 (8-bit) per 32-bit sublane: the (8,128)(2,1) and (4,1) tilings.
Mesh: ('data', 'model') by default; heads, KV heads, MLP and experts on model. Also expert, attn_dp, dcp, pcp.
| Slice | Weight-read floor | Limit |
|---|---|---|
| 36 v5e | 13.8 ms | about 17 sequences |
| 64 v5e | 7.7 ms | ICI caps batch near 100: about 12 ms, 130 tok/s/chip |
| 256 v5e | 1.9 ms | per-chip throughput levels off at about 149 |
| 8 Ironwood, fp8 | 6.9 ms | — |
Answer: replicas of 64 chips.
By the end you can:
sdy.sharding annotations.Pallas enters at stage 3 as a custom call, compiled by Mosaic.
ENTRY %main.0_spmd (param: bf16[4,512], param.1: bf16[512,1024], param.2: bf16[1024,512]) -> bf16[4,512] {
%param = bf16[4,512]{1,0} parameter(0), sharding={devices=[2,1,2]<=[4] last_tile_dim_replicate}, metadata={op_name="x"}
%param.1 = bf16[512,1024]{1,0} parameter(1), sharding={devices=[1,2,2]<=[2,2]T(1,0) last_tile_dim_replicate}, metadata={op_name="w1"}
%param.2 = bf16[1024,512]{1,0} parameter(2), sharding={devices=[2,1,2]<=[2,2]T(1,0) last_tile_dim_replicate}, metadata={op_name="w2"}
%dot = f32[4,1024]{1,0} dot(%wrapped_convert, %wrapped_convert.1), lhs_contracting_dims={1}, rhs_contracting_dims={0}, metadata={op_name="jit(mlp)/dot_general" stack_frame_id=2}
%dot.1 = f32[4,512]{1,0} dot(%convert_convert_fusion, %wrapped_convert.2), lhs_contracting_dims={1}, rhs_contracting_dims={0}, metadata={op_name="jit(mlp)/dot_general" stack_frame_id=3}
%all-reduce.1 = f32[4,512]{1,0} all-reduce(%dot.1), channel_id=1, replica_groups=[2,2]<=[4], use_global_device_ids=true, to_apply=%add.clone_promoted, metadata={op_name="jit(mlp)/dot_general" stack_frame_id=3}
ROOT %wrapped_convert.3 = bf16[4,512]{1,0} fusion(%all-reduce.1), kind=kLoop, calls=%wrapped_convert_computation.3, metadata={op_name="jit(mlp)/dot_general" stack_frame_id=3}
You control
with_sharding_constraintAUTO)LIBTPU_INIT_ARGSYou do not
| Mode | View | Collectives |
|---|---|---|
| Auto | global | compiler inserts; steer with constraints ("tickling", about 60% of the work) |
| Explicit | global; float32[8@X,512@Y] | compiler inserts; ambiguous ops must give out_sharding |
Manual, jax.shard_map | per device | you write psum, all_gather, ppermute |
Shardy has been the default propagator since JAX 0.7.0.
PrivateUse1 backend, torch.device("tpu"), plain tensors.torch.compile (FX graph to XLA, no Inductor).tpu_dist: DDP, FSDP2, DTensor; collectives are StableHLO, no NCCL.torch.tpu.pallas.jax_op. Qwen3-MoE-235B on 8,192 v7 chips at 27.1% MFU.| Tensor | Capture | To XLA | |
|---|---|---|---|
| PyTorch/XLA (deprecated) | lazy xla | until mark_step | HLO |
| torchax | subclass over jax.Array | jax.jit | StableHLO via jaxpr |
| TorchTPU eager | plain, PrivateUse1 | per op or region | StableHLO |
TorchTPU torch.compile | plain | Dynamo FX | StableHLO per graph |
AWS did the same: TorchNeuron (PrivateUse1), compiled by neuronx-cc instead of XLA.
By the end you can:
pl.pallas_call(
matmul_kernel,
grid=(m // bm, n // bn, k // bk), # sequential, reduction axis last
in_specs=[pl.BlockSpec((bm, bk), lambda i, j, kk: (i, kk)),
pl.BlockSpec((bk, bn), lambda i, j, kk: (kk, j))],
out_specs=pl.BlockSpec((bm, bn), lambda i, j, kk: (i, j)),
scratch_shapes=[pltpu.VMEM((bm, bn), jnp.float32)], # fp32 accumulator
compiler_params=pltpu.CompilerParams(
dimension_semantics=("parallel", "parallel", "arbitrary")))
| Tile | FLOP/B | VMEM | Ironwood (ridge 313) |
|---|---|---|---|
| 256² | 128 | 1.5 MiB | memory-bound |
| 512² | 256 | 4 MiB | memory-bound |
| 1024² | 512 | 12 MiB | compute-bound |
| 2048² | 1024 | 40 MiB | compute-bound |
Write Pallas for access patterns XLA cannot express: paged attention (scalar prefetch), grouped MoE, quantized matmuls, collective matmul (remote DMA).
with jax.profiler.trace(dir): ... then xprof --logdir dir.PHASED_PROFILING_DIR, USE_JAX_PROFILER_SERVER.all-gather-start … compute … all-gather-done.%fusion.3 = bf16[32,32,4096]{2,1,0:T(8,128)(2,1)S(1)} fusion(...), kind=kCustom, calls=%all-reduce-scatter.3
{2,1,0}: minor-to-major. T(8,128)(2,1): tiles, bf16 pairs packed.S(1): VMEM. No S: HBM.calls=%all-reduce-scatter: a ReduceScatter. One matmul per fusion at most.replica_groups={{0,16,...,112},...}: 8 per group, 8-way model parallel.| Tool | Answers |
|---|---|
| Op Profile | which ops cost the time |
| Roofline Model | which resource each op is bound by (HBM, VMEM lines) |
| Memory Viewer | peak HBM and what is live |
| Graph Viewer | HLO graph and shardings |
| Megascale Viewer | DCN traffic across slices |
Book's check: 95.6 ms predicted, 96 ms measured.
| Replica | Batch | Step | tok/s/chip |
|---|---|---|---|
| 4 chips, bf16 | 63 | 27.7 ms | 569 |
| 4 chips, fp8 W + KV | 140 | 27.9 ms | 1,257 |
| 8 chips, fp8 W + KV | 294 | 27.9 ms | 1,316 |
KV-read-bound. Choose 4-chip fp8 replicas; prefill on separate 4-chip servers (337 ms TTFT), about 3 per decode replica.
By the end you can:
| Cores per chip | Latency hiding | On-chip memory | Compilation | |
|---|---|---|---|---|
| GPU (H100) | 132 SMs | hardware warps | 30 MB SMEM + 50 MB L2 cache | eager kernels, graphs |
| TPU (Ironwood) | 2 TensorCores | compiler schedule | 128 MiB VMEM, no cache | whole-program XLA |
| Trainium2 | 8 NeuronCores | concurrent engines | 224 MiB SBUF + PSUM | neuronx-cc |
| Chip | BF16 | FP8 | HBM | HBM BW | BF16 intensity |
|---|---|---|---|---|---|
| TPU7x Ironwood | 2.31 PFLOP/s | 4.61 PFLOP/s | 192 GiB | 7.38 TB/s | 313 |
| TPU 8t | 996 TFLOP/sp | 5.98 PFLOP/sp | 216 GiB | 6.53 TB/s | 153p |
| TPU 8i | 1.1 PFLOP/sp | 8.81 PFLOP/sp | 288 GiB | 8.6 TB/s | 128p |
| H100 SXM | 989 TFLOP/s | 1.98 PFLOP/s | 75 GiB | 3.35 TB/s | 295 |
| GB200 (NVL72) | 2.5 PFLOP/s | 5 PFLOP/s | 173 GiB | 8 TB/s | 313 |
| Rubin (VR NVL72) | 4 PFLOP/s | 17.5 PFLOP/sp | 268 GiB | 19.2 TB/s | 208 |
| Trainium3 | 671 TFLOP/s | 2.52 PFLOP/s | 144 GiB | 4.9 TB/s | 137 |
p provisional: from the JAX Pallas hardware table, not in Google's published spec.
Flagship ridge points cluster near 300; inference designs push it down.
Switch (NVLink, NeuronSwitch)
Torus (ICI)
| 8 | 64 | 512 chips | |
|---|---|---|---|
| AllToAll, Ironwood | 1.25 ms | 0.078 ms | 0.020 ms |
| AllToAll, H100 | 0.28 ms | 0.31 ms | 0.039 ms |
| AllToAll, GB200 NVL72 | 0.14 ms | 0.017 ms | 0.039 ms |
| AllGather, Ironwood | 5.0 ms | 1.7 ms | 1.7 ms |
| AllGather, GB200 NVL72 | 1.0 ms | 1.1 ms | 1.1 ms |
The switch wins inside its domain; the torus wins past it. Practical GPU reach is lower than these figures.
| Workload | Advantage |
|---|---|
| Large dense pretraining | TPU pod (v5p-class α); GPU close |
| MoE training | NVL72 inside 72; torus at multi-rack scale |
| Low-latency decode | NVL72 one hop; TPU 8i at most 7 hops |
| Long-context decode | most HBM bandwidth and capacity per dollar |
| Recommendation (embeddings) | TPU SparseCore |
| Changing ops, dynamic shapes | GPU |