Running AlphaFold3 (single seq mode) 3 times faster on TPUs
Background
Recently I’ve been training some protein design flow matching models on TPUs. To evaluate them protein folding models play an important role. So to keep things simpler (GCP buckets etc), I wanted to use AF3 on TPUs as well. However, the current AF3 codebase didn’t seem to support inference on TPUs.
Correct, we don’t support TPU at the moment.
— GitHub issue #550 [1], October 2025
Running AF3 with XLA
Unsupported doesn’t mean we cannot run it on TPUs.
AF3 is written in JAX and it compiles through XLA, which runs natively on TPUs. So what’s the issue? For TPUs it does not have an optimized kernel for pair-biased attention.33 AF3 actually has four pair-biased attentions: triangle attention, the single attention in the pairformer, the diffusion transformer and the atom transformers. Only triangle attention goes through tokamax. Because of this, when you run AF3 on a v6e chip it runs slow even compared to a 5090 44 This is mostly not the attention’s fault: one unfused triangle-attention call takes 0.71 ms against 0.32 ms fused, which over the 1,056 calls in a fold adds about 0.4 s, while the TPU VM’s slower CPU costs about 1.1 s..
To dig a little deeper: AF3 calls tokamax, an OpenXLA library of fused JAX kernels, and --flash_attention_implementation picks which tokamax backend runs.
However, on a TPU we hit two dead ends:
- tokamax’s fused GPU kernels (
triton, the default, andcudnn) don’t run on TPUs. - tokamax’s TPU kernel (
mosaic) doesn’t accept an attention bias at all 55 The check is inpallas_mosaic_tpu_common.py[3]: passing any bias raises this error – still the case in tokamax 0.0.17, and AF3′s triangle attention needs one: the pair bias.
If we don’t care about performance, we can just run the standard AF3 inference code with this flag 66 Defined in run_alphafold.py [4], whose help text calls xla “no flash attention” and “portable across GPU devices”; TPUs aren’t mentioned..
--flash_attention_implementation=xla
With it, AF3 computes attention with ordinary JAX operations instead of a hand-written GPU kernel. XLA compiles those for the TPU like the rest of AF3′s JAX code. But since this automatic compilation doesn’t result in a fused kernel, it writes the full attention-logit tensor to memory and reads it back 77 Flash attention (Dao et al., 2022 [5]) computes softmax(QKᵀ + bias)·V in tiles held in fast on-chip memory, keeping a running maximum and sum for the softmax, so the N × N matrix of logits is never written to the slower main memory (HBM). That removes the largest memory traffic in attention. The TPU kernel later in this post uses the same idea in a simpler form: at these sizes a whole row of logits fits on-chip, so it never has to tile across keys.. How much does this hurt us?
Optimizations in a nutshell
If we look at the timing for a full end to end run on a v6e chip, it runs in about 5.8 seconds. For context I measured this on a 5090 as well.
XLA attention
pipelining + hoisting
+ Pallas kernels
+ tighter padding
Triton (default)
XLA attention
pipelining + hoisting
+ tighter padding
- End-to-end time for one fold of stock AF3, in seconds.
- On a TPU v6e, a fold takes 5.81 s. The hatched 1.85 s is featurization, which runs on one core of the host CPU.
- On an RTX 5090 with AF3′s default fused Triton attention, it takes 5.33 s. The workstation’s desktop CPU featurizes in 0.77 s; per core, the TPU VM’s server CPU is about 2.4× slower.
- Leave featurization aside and the v6e still finishes first: 3.96 s against 4.56 s. Not because its chip computes faster: the 5090′s kernels run for only 3.2 s of that, and it spends the rest waiting on its CPU.
- Give the 5090 the same XLA attention the TPU uses and it slows to 6.10 s, slower than the v6e’s 5.81 s.
- Pipelining and hoisting alone bring the v6e to 3.56 s, with outputs bit for bit identical to stock AF3. Featurization now runs while the TPU works, so it no longer adds to the fold.
- The three Pallas kernels take it to 2.77 s (same inputs and random draws), and tighter padding to 1.81 s.
- Given the same driver changes (the Pallas kernels are TPU-only), the 5090 gets to 4.32 s, and 3.11 s with tighter padding. Even before its kernels, the v6e is ahead (3.56 s against 4.32 s); with them it is 1.6–1.7× faster.
How much do precision errors propagate?
Here are all the optimizations in one place, with what each one does to AF3′s predictions. Two of them just reorganize when and where the work happens, so the outputs stay the same as the original AF3 inference code’s.
The other four change the floating-point arithmetic. The kernels round the same way as AF3′s own code on more than 99% of values, but through 200 denoising steps even that can change the final structure.
| Change | What it does | Time per fold | Outputs vs stock AF3 |
|---|---|---|---|
| Pipelined driver | featurizes the next inputs on the CPU while the TPU runs the current one | 5.81 → 3.80 s | identical, bit for bit |
| Hoisting | computes the diffusion head’s position-independent work once per fold, not at each of the 200 steps | → 3.56 s | identical, bit for bit |
| Pallas triangle attention | fused attention that keeps the logits on-chip | → 3.21 s | rounding: 99.996% of values identical* |
| Pallas transition | LayerNorm, SwiGLU and output projection in one kernel | → 3.08 s | rounding: 99.97% identical |
| Pallas triangle multiplication | fused triangle multiplication | → 2.77 s | rounding: 99.2–99.6% identical |
| Tighter padding | pads to the next multiple of 16 tokens (192 here) instead of 256 | → 1.81 s | rounding, plus new starting noise for 4 of the 5 diffusion samples |
Do the predictions change?
Pipelining and hoisting cannot change anything: the outputs are bit for bit identical. The kernels can, through rounding, so I checked them in two ways.
First, against real structures. I folded 40 proteins from the PDB from their deposited sequences (with their ligands, no MSA), once with stock AF3 and once with the kernels, with the same padding and seed, and scored each prediction against the deposited structure: lDDT over all the protein’s heavy atoms, where 1 is a perfect match. If the kernels changed accuracy, the points below would move off the diagonal. For scale, the other two panels make changes that have nothing to do with the kernels: a new random seed, and running stock AF3 on an RTX 5090 instead of the TPU.
TPU v6e, same seed
typical change 0.027
stock AF3, RTX 5090
typical change 0.015
stock AF3, same seed
typical change 0.025
The kernels don’t shift accuracy: the mean change is −0.002 lDDT. Individual proteins do move, by 0.027 on average, about as much as running stock AF3 on an RTX 5090 instead of the TPU (0.025) and a bit more than a new random seed (0.015). Single-sequence AF3 is unsure about most natural proteins (mean pLDDT around 50, and most points sit low on both axes), and for an unsure prediction any small numerical difference, ours, a new seed or another chip, sends the diffusion somewhere slightly different.
Second, on the inputs I actually fold: 40 designed proteins with ligands, where AF3 is mostly confident. There is no deposited structure, so I compared top-ranked structures directly, over three seeds. With the kernels they move a median of 0.52 Å from stock AF3′s, against 1.29 Å for changing the seed, and the confident ones (pLDDT of 90 or more) move at most 0.84 Å.
Related implementations
After I worked on this with claude + codex on optimizing AF3 for TPUs, I realized that I didn’t do much of a existing literature search. So we revisited Anthropic’s FlashPairformer code [6] that include three close comparisons, all targeting GPUs through Pallas’s Triton backend:
- Triangle attention: tiled flash attention with the pair bias shared across triangle rows.
- Transition: fused LayerNorm, SwiGLU and output projection.
- Triangle multiplication: fused input processing, a contraction, and fused output processing. Its source explicitly notes that fusion changes BF16 rounding and is not bitwise identical to stock execution.
However, Anthropic’s repo mainly targets NVIDIA GPUs through the Pallas kernels. With only their compiler settings switched to TPU ones, the triangle-attention and triangle-multiplication kernels do not compile on a TPU: their memory layouts break the TPU’s block-shape rules.
Apart from Anthropic’s repo, JAX’s own Pallas FlashAttention for TPUs [2] does accept an additive attention bias, but only as a separate copy for every row. On AF3′s triangle attention it is 1.6–4.4× slower than the kernel in this post (at 256 tokens 0.54 ms for the kernel alone and 0.84 ms with the bias copy and transposes it needs, against 0.32 ms), and no faster than plain XLA up to 384 tokens. Most of the gap is its handling of AF3′s 32-wide heads; building the per-row bias costs the rest. Separately, JAXBench: Benchmarking Autonomous TPU Kernel Optimization [7] includes an AlphaFold2-style triangle-multiplication workload in its TPU v6e benchmark; its reference workload [8] differs from the exact AF3 module.
References
- AlphaFold 3 contributors (2025). “AlphaFold 3 TPU support discussion: GitHub issue 550.”
- The JAX Authors (n.d.). “JAX Pallas TPU FlashAttention implementation.”
- OpenXLA contributors (n.d.). “Tokamax: Pallas Mosaic TPU attention input-support checks.”
- Google DeepMind (n.d.). “AlphaFold 3 inference runner: flash-attention implementation flag.”
- Dao et al. (2022). “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness.”
- Anthropic (n.d.). “FlashPairformer: Pallas kernels for biomolecular modeling on GPUs.”
- Tschand et al. (2026). “JAXBench: Benchmarking Autonomous TPU Kernel Optimization.”
- JAXBench contributors (n.d.). “JAXBench: AlphaFold2-style triangle-multiplication reference workload.”
MoveThanks!

Comments
Join the conversation with your GitHub account.