Skip to content

Build Not yet confirmed elsewhere1 publisher 3 min read Published

A Pallas kernel tiled for TPU VMEM splits one Gemma 4 port into two checkpoints

One hand-written JAX port runs on TPU v5e, v6e and a Turing T4G, but the quantized fast path stays on TPU and the compute dtype has to be read off the live device, because neither leak ever shows up as red in a log.

The Engineer · Build desk

Send a correction

Illustration accompanying A Pallas kernel tiled for TPU VMEM splits one Gemma 4 port into two checkpoints
Generated illustration

What happened

  • A hand-written pure-JAX Gemma 4 port runs the same code on Cloud TPU v5e, on TPU v6e, and on an NVIDIA T4G attached to an AWS Graviton2 host.
  • Gemma 4 makes any port carry two attention geometries, head_dim 256 on sliding layers and 512 on global ones, where most inference stacks assume a single head dimension per model.
  • On the vLLM path those mixed head dimensions rule out FA4 and force the Triton attention backend, a selection driven by the model config rather than by the operator.
  • Because the fused W4A16 Pallas kernel is tiled for TPU VMEM, the TPU rigs serve the -qat-w4a16-ct export while the GPU rigs serve the dense reference checkpoint at 16-bit.
  • Rather than trust a config file, the port reads compute capability off the live device and picks float16 on pre-Ampere hardware and bfloat16 everywhere else.

Why it matters

  • What it limits constraint Weight parity is what the one-port claim gives up, so any quality or latency comparison run across the TPU and GPU rigs is comparing two weight sets and not one port on two chips.
  • What it costs cost The silent bf16 emulation bills you in decode throughput with no log line to show for it, which means the cost lands on whoever is later asked why the rig feels slow.
  • Who has to decide decision For models with irregular head dimensions, picking a kernel-tiled serving stack over plain XLA becomes a hardware-eligibility decision, because one path carries a per-block memory ceiling and the other does not.
  • What it sets up precedent Any kernel advertised as portable while tied to VMEM sizing now has a reasonable expectation attached to it: compute the requirement at boot, or have the portability claim tested at the first token.

The fused W4A16 path is written in Pallas and tiled for TPU VMEM, which gives 16 MB per core, and at this model's shapes the tiles want 550 KiB to 1.1 MiB per block [17]. Lower that to a GPU and Pallas goes through Triton, where per-block scratch is shared memory, 64 KiB of it on Turing [18]. Against that ceiling the tile range is 8.6x to 17.6x over budget [25]. The two budgets are not even the same unit of accounting, since VMEM is counted per core and shared memory per block, and the nominal gap between the two figures is 256x [26]. Ada lifts the ceiling and still does not come near a megabyte [18]. `check_w4a16_fits_scoped_memory()` runs that comparison at startup and refuses with the arithmetic attached instead of dying at the first token [19]. The author's one-line version is that Pallas is portable as an API and not portable as a memory model [3]. Measured against the project's own bar, that the only thing changing between rigs is a config file, that is one leak [5].

The second leak does not announce itself. A wrong compute dtype does not raise; it emulates, and XLA routes bfloat16 on a pre-Ampere GPU through fp32 with nothing red in the logs [2]. On the SM 7.5 card the policy resolves to float16, Turing's only real 16-bit datapath, since the part has neither bf16 nor fp8 [14]. The first line the process emits carries the whole decision, including `pallas_interpret=False` [15], which the write-up notes is the difference between serving and silently running a simulator [16]. A simulator that answers your HTTP requests is the most courteous way to be wrong.

What kills the vLLM path on Turing is a shared-memory request of 98,304 bytes against a 65,536-byte hardware limit [11], which is 1.5x the budget [24]. That figure travels only if your rig makes the same backend selection at the same shapes on a card with 64 KiB of shared memory per block. Somewhere with a higher ceiling the same code may fit and tell you nothing about the T4G. In this port there is no vLLM in the path at all, and attention is ordinary XLA rather than a hand-tiled kernel, so the attention path has no per-block ceiling to exceed [4][12].

The geometry is still work for whoever ports next. The 8:1 MQA means the KV budget bears no relation to what the parameter count implies [7], and a KV-share map folds 35 layers onto 15 caches, an average of 2.3 layers per cache [8][27]. On top of that sit a 512-slot sliding ring and a 4.70 GB per-layer-embedding table that is quantized to 4 bits on load [9]. The write-up reports a week lost to a padding-eviction bug in that ring, under an invariant that a cache index is an absolute real position and that padding never occupies an index a real position uses [21]. The text breaks off mid-sentence there [22]. This is one engineer's set of rented rigs, published on dev.to [23], so read the memory ceilings as checkable arithmetic and the lost decode throughput as an unquantified characterisation.

What to watch

  • The write-up breaks off inside the KV ring padding-eviction section, and the invariant test that closes it is the part worth reading when it lands.
  • A W4A16 kernel retiled against shared-memory blocks rather than VMEM would put one checkpoint back on both rigs.
  • A vLLM attention backend that accepts heterogeneous head dimensions without falling back to Triton would remove the Turing failure entirely.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence54
Adoption14
Hype gap+8
Incentives32
Confidence45
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    The same hand-written pure-JAX Gemma 4 port code runs on Cloud TPU v5e and v6e and on an NVIDIA T4G attached to an AWS Graviton2 host.

  2. [2]

    A wrong compute dtype does not raise, it emulates: bfloat16 on a pre-Ampere GPU is routed through fp32 by XLA, most of decode is lost to conversion, and nothing in the logs is red.

  3. [3]

    The author's stated conclusion is that Pallas is portable as an API and not portable as a memory model, because a kernel written against VMEM does not describe a GPU.

Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 28, 2026

    Gemma 4 in Pure JAX: What Ports from TPU to GPU, and What Doesn't

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories