XLA (Accelerated Linear Algebra) is a domain-specific compiler that optimizes TensorFlow and JAX computations — performing whole-program optimization including operation fusion, memory planning, and hardware-specific code generation to achieve significant performance improvements over eager execution.
What Is XLA?
- Definition: Compiler for linear algebra workloads.
- Origin: Google, part of TensorFlow/JAX.
- Function: Optimizes and compiles ML computations.
- Targets: CPU, GPU (CUDA/ROCm), TPU.
Why XLA Matters
- Fusion: Combines operations to reduce memory traffic.
- Memory: Optimizes buffer allocation and reuse.
- Hardware: Generates optimized target-specific code.
- Performance: 2-10× speedups common for fused operations.
- TPU: Required compiler for TPU execution.
How XLA Works
Compilation Pipeline:
<svg viewBox="0 0 760 470" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,Segoe UI,Roboto,sans-serif">
<rect x="0" y="0" width="760" height="470" fill="#0d1117"/>
<text x="380" y="28" fill="#e6edf3" font-size="21" font-weight="700" text-anchor="middle">XLA — Accelerated Linear Algebra Compiler</text>
<text x="380" y="48" fill="#8b98a5" font-size="12" text-anchor="middle">JIT-compiles computation graphs into fused, hardware-optimized kernels</text>
<!-- Compilation pipeline -->
<rect x="30" y="65" width="700" height="175" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
<text x="380" y="84" fill="#e6edf3" font-size="11" font-weight="600" text-anchor="middle">XLA Compilation Pipeline</text>
<!-- Input: HLO graph -->
<rect x="50" y="100" width="130" height="115" rx="6" fill="#0b1220" stroke="#60a5fa" stroke-width="1.2"/>
<text x="115" y="118" fill="#93c5fd" font-size="9" font-weight="600" text-anchor="middle">HLO Graph</text>
<text x="115" y="135" fill="#8b98a5" font-size="7.5" text-anchor="middle">(High-Level Ops)</text>
<rect x="65" y="145" width="100" height="14" rx="2" fill="#0d1117" stroke="#334155" stroke-width="0.4"/>
<text x="115" y="155" fill="#6b7684" font-size="7" text-anchor="middle">dot(A, B)</text>
<rect x="65" y="163" width="100" height="14" rx="2" fill="#0d1117" stroke="#334155" stroke-width="0.4"/>
<text x="115" y="173" fill="#6b7684" font-size="7" text-anchor="middle">add(bias)</text>
<rect x="65" y="181" width="100" height="14" rx="2" fill="#0d1117" stroke="#334155" stroke-width="0.4"/>
<text x="115" y="191" fill="#6b7684" font-size="7" text-anchor="middle">relu()</text>
<text x="115" y="210" fill="#6b7684" font-size="7" text-anchor="middle">from JAX / TF / PyTorch</text>
<path d="M182,157 L210,157" fill="none" stroke="#3a4453" stroke-width="1.2"/>
<polygon points="208,154 214,157 208,160" fill="#3a4453"/>
<!-- Optimization passes -->
<rect x="218" y="100" width="170" height="115" rx="6" fill="#0b1220" stroke="#f59e0b" stroke-width="1.2"/>
<text x="303" y="118" fill="#fbbf24" font-size="9" font-weight="600" text-anchor="middle">Optimization Passes</text>
<text x="235" y="138" fill="#8b98a5" font-size="8">• Op fusion (dot+add+relu → 1 kernel)</text>
<text x="235" y="154" fill="#8b98a5" font-size="8">• Layout assignment (NHWC/NCHW)</text>
<text x="235" y="170" fill="#8b98a5" font-size="8">• Constant folding</text>
<text x="235" y="186" fill="#8b98a5" font-size="8">• Memory scheduling</text>
<text x="235" y="202" fill="#8b98a5" font-size="8">• algebraic simplification</text>
<path d="M390,157 L418,157" fill="none" stroke="#3a4453" stroke-width="1.2"/>
<polygon points="416,154 422,157 416,160" fill="#3a4453"/>
<!-- Backend codegen -->
<rect x="425" y="100" width="140" height="115" rx="6" fill="#0b1220" stroke="#a78bfa" stroke-width="1.2"/>
<text x="495" y="118" fill="#c4b5fd" font-size="9" font-weight="600" text-anchor="middle">Backend Codegen</text>
<text x="495" y="138" fill="#8b98a5" font-size="7.5" text-anchor="middle">target-specific lowering</text>
<rect x="442" y="148" width="55" height="20" rx="3" fill="#14261f" stroke="#34d399" stroke-width="0.6"/>
<text x="469" y="162" fill="#34d399" font-size="7" text-anchor="middle">GPU PTX</text>
<rect x="502" y="148" width="55" height="20" rx="3" fill="#2a1a0a" stroke="#f59e0b" stroke-width="0.6"/>
<text x="529" y="162" fill="#f59e0b" font-size="7" text-anchor="middle">TPU HLO</text>
<rect x="442" y="175" width="55" height="20" rx="3" fill="#1e3a5f" stroke="#60a5fa" stroke-width="0.6"/>
<text x="469" y="189" fill="#60a5fa" font-size="7" text-anchor="middle">CPU AVX</text>
<rect x="502" y="175" width="55" height="20" rx="3" fill="#1c1633" stroke="#a78bfa" stroke-width="0.6"/>
<text x="529" y="189" fill="#a78bfa" font-size="7" text-anchor="middle">custom</text>
<path d="M567,157 L595,157" fill="none" stroke="#3a4453" stroke-width="1.2"/>
<polygon points="593,154 599,157 593,160" fill="#3a4453"/>
<!-- Output: optimized binary -->
<rect x="602" y="100" width="115" height="115" rx="6" fill="#0b1220" stroke="#34d399" stroke-width="1.2"/>
<text x="659" y="118" fill="#6ee7b7" font-size="9" font-weight="600" text-anchor="middle">Fused Kernel</text>
<text x="659" y="138" fill="#8b98a5" font-size="7.5" text-anchor="middle">1 launch instead of 3</text>
<text x="659" y="156" fill="#8b98a5" font-size="7.5" text-anchor="middle">no intermediate allocs</text>
<text x="659" y="174" fill="#8b98a5" font-size="7.5" text-anchor="middle">optimal tile sizes</text>
<text x="659" y="192" fill="#34d399" font-size="8" text-anchor="middle">2–5× faster</text>
<text x="659" y="208" fill="#6b7684" font-size="7" text-anchor="middle">hardware-native</text>
<!-- Benefits panel -->
<rect x="30" y="252" width="345" height="130" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
<text x="202" y="272" fill="#e6edf3" font-size="10" font-weight="600" text-anchor="middle">Key Optimizations</text>
<text x="50" y="295" fill="#34d399" font-size="9" font-weight="600">Op Fusion:</text>
<text x="130" y="295" fill="#8b98a5" font-size="8.5">matmul+bias+gelu → single kernel (no DRAM round-trip)</text>
<text x="50" y="315" fill="#60a5fa" font-size="9" font-weight="600">Buffer reuse:</text>
<text x="135" y="315" fill="#8b98a5" font-size="8.5">in-place ops, eliminate copies, reduce peak memory</text>
<text x="50" y="335" fill="#f59e0b" font-size="9" font-weight="600">Tiling:</text>
<text x="100" y="335" fill="#8b98a5" font-size="8.5">partition large GEMMs to fit shared memory / L2</text>
<text x="50" y="355" fill="#a78bfa" font-size="9" font-weight="600">Sharding:</text>
<text x="115" y="355" fill="#8b98a5" font-size="8.5">GSPMD auto-partitions across TPU/GPU pods</text>
<text x="50" y="375" fill="#6b7684" font-size="7.5">without XLA: N kernel launches, N memory allocations per op</text>
<!-- Right: ecosystem -->
<rect x="390" y="252" width="340" height="130" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
<text x="560" y="272" fill="#e6edf3" font-size="10" font-weight="600" text-anchor="middle">XLA in the Ecosystem</text>
<text x="410" y="295" fill="#60a5fa" font-size="9" font-weight="600">JAX:</text>
<text x="448" y="295" fill="#8b98a5" font-size="8.5">XLA-native (jax.jit → XLA compile always)</text>
<text x="410" y="315" fill="#f59e0b" font-size="9" font-weight="600">TensorFlow:</text>
<text x="492" y="315" fill="#8b98a5" font-size="8.5">tf.function + XLA (tf.config experimental)</text>
<text x="410" y="335" fill="#a78bfa" font-size="9" font-weight="600">PyTorch:</text>
<text x="470" y="335" fill="#8b98a5" font-size="8.5">torch_xla (TPU), torch.compile backend</text>
<text x="410" y="355" fill="#34d399" font-size="9" font-weight="600">OpenXLA:</text>
<text x="478" y="355" fill="#8b98a5" font-size="8.5">StableHLO → XLA (cross-framework)</text>
<text x="560" y="375" fill="#6b7684" font-size="7.5" text-anchor="middle">XLA is the standard backend for TPU and increasingly for GPU</text>
<!-- Footer -->
<rect x="30" y="395" width="700" height="35" rx="6" fill="#080d14" stroke="#233043" stroke-width="1"/>
<text x="380" y="416" fill="#8b98a5" font-size="8.5" text-anchor="middle">XLA + MLIR convergence: XLA is being rebuilt on MLIR (StableHLO dialect) → unified compiler for all accelerators</text>
<text x="380" y="452" fill="#6b7684" font-size="11" text-anchor="middle">XLA turns eager Python into datacenter-optimized machine code — the reason TPUs achieve near-peak FLOPS utilization.</text>
</svg>
Key Optimizations:
Optimization | Effect
---------------------|----------------------------------
Operation fusion | Reduce memory reads/writes
Buffer allocation | Minimize memory footprint
Layout optimization | Match hardware preferences
Constant folding | Pre-compute constants
Dead code elimination| Remove unused computations
Common subexpression | Avoid redundant computation
Using XLA
TensorFlow:
import tensorflow as tf
# Enable XLA globally
tf.config.optimizer.set_jit(True)
# Or per-function
@tf.function(jit_compile=True)
def train_step(x, y):
with tf.GradientTape() as tape:
predictions = model(x)
loss = loss_fn(y, predictions)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
return loss
JAX (XLA by default):
import jax
import jax.numpy as jnp
@jax.jit # Compiles with XLA
def forward(params, x):
return jnp.dot(x, params["w"]) + params["b"]
# First call compiles, subsequent calls use cached
result = forward(params, input_data)
PyTorch (via TorchXLA):
import torch
import torch_xla.core.xla_model as xm
# Get XLA device (TPU or GPU with XLA)
device = xm.xla_device()
# Move model and data
model = model.to(device)
data = data.to(device)
# Training loop
output = model(data)
loss = criterion(output, target)
loss.backward()
xm.optimizer_step(optimizer)
Operation Fusion
Example:
Without fusion:
temp1 = add(a, b) # Read a,b; write temp1
temp2 = multiply(temp1, c) # Read temp1,c; write temp2
result = relu(temp2) # Read temp2; write result
Memory: 6 reads + 3 writes
With fusion (XLA):
result = fused_add_mul_relu(a, b, c) # One kernel
Memory: 3 reads + 1 write
Fusion Types:
Type | Example
------------------|----------------------------------
Element-wise | add + multiply + relu
Broadcast | scalar + matrix
Transpose | transpose + matmul
Reduction | softmax + cross_entropy
HLO (High-Level Optimizer) IR
Example HLO:
HloModule example
ENTRY main {
%p0 = f32[4,8] parameter(0)
%p1 = f32[8,16] parameter(1)
%dot = f32[4,16] dot(%p0, %p1)
%p2 = f32[4,16] parameter(2)
%add = f32[4,16] add(%dot, %p2)
ROOT %relu = f32[4,16] maximum(%add, %zero)
}
Debugging XLA:
# Dump HLO
XLA_FLAGS="--xla_dump_to=/tmp/xla_dumps" python train.py
# Visualize
# /tmp/xla_dumps contains .txt and .dot files
Performance Considerations
When XLA Helps Most:
✅ Compute-intensive operations
✅ Many small operations (fusion benefit)
✅ Repeated computations (compilation amortized)
✅ TPU workloads (required)
❌ Dynamic shapes (recompilation)
❌ Heavy Python control flow
❌ Small, infrequent computations
❌ Debug/development iteration
Compilation Overhead:
First call: Compilation (seconds to minutes)
Subsequent: Cached execution (fast)
Mitigation:
- Consistent input shapes
- Warm-up before timing
- AOT compilation for production
XLA is the optimization engine behind high-performance ML — by compiling entire computational graphs rather than executing operations independently, it enables the efficiency gains that make large-scale training and inference economically viable.
Explore 500+ Semiconductor & AI Topics
From EUV lithography to CUDA optimization — search the full knowledge base or chat with our AI assistant.