Home · ASPLOS 2023 · Machine Learning Systems

Overlap Communication with Dependent Computation via Decomposition in Large Deep Learning Models

Shibo Wang, Jinliang Wei, Amit Sabne, Andy Davis, Berkin Ilbeyi, Blake Hechtman and 8 others

Conference
ASPLOS 2023
Published
2022
Pages
93-106
Citations
63

Model-parallel layers must gather weights before multiplying and scatter results after — Google's compiler pass breaks each collective and its dependent matrix multiply into matched slices that flow around a ring, so TPUs compute on one shard while the next is in flight, pushing 500-billion-parameter training to 72% of peak FLOPS.

The argument in one glance

  1. Problem

    Sharded layers spend their lives waiting for tensors

    Models from 10 billion to 1 trillion parameters cannot fit on one accelerator, so each layer is partitioned across many chips (intra-layer model parallelism). Before a partitioned matrix multiply can run, an AllGather must assemble its operand from every chip; after it runs, a ReduceScatter must distribute the result. This communication is a large share of step time — and it grows with scale.

  2. What people did

    Overlap communication with computation — where they are independent

    Hiding transfers under compute is the classic distributed-systems remedy: frameworks overlap one layer’s gradient synchronization with another layer’s backward pass, and loop transformations extract independent work to run alongside messages.

  3. Why it fell short

    Here the communication and the computation depend on each other

    The multiply consumes exactly what the AllGather produces; the ReduceScatter consumes exactly what the multiply produces. A dependent pair cannot simply run in parallel — the consumer would read data that has not arrived. Deep models are chains of such pairs, so the classic trick finds nothing to overlap.

  4. This paper's idea

    Decompose both sides until the dependence dissolves

    Split the collective into a sequence of point-to-point shard transfers around a ring, and split the matrix multiply into a matching sequence of partial multiplies — such that step i of the computation never needs the data that step i of the communication is still carrying. Chips compute on one shard while the next is in flight. Implemented as automatic XLA compiler passes, this yields 1.14–1.38× training throughput on TPU v4 pods and up to 72% of peak FLOPS on a 500-billion-parameter model across 1,024 chips.

Why this problem is worth solving

Scaling laws made big models a strategy: more parameters, better sample efficiency. But a trillion-parameter model does not fit in any accelerator’s memory, so production systems use

partitioning each layer’s tensors and operators across many devices, which then execute the same program on different shards (the SPMD — single program, multiple data — style)
. The paper’s evaluation models span 10 billion to 1.03 trillion parameters on 128 to 2,048 TPU v4 chips.

Partitioning manufactures communication. In the standard sharded transformer layer, weights live split across chips; before each

Einstein summation — the generalized matrix multiply notation that XLA uses for dot products, matmuls, and contractions
the missing operand pieces must be assembled with an AllGather (every chip receives every shard and concatenates), and partial results must be combined and redistributed with a ReduceScatter (element-wise sum, then each chip keeps one shard). These collectives sit in the dependence chain: the multiply cannot start before the gather finishes, and the scatter cannot start before the multiply finishes. So compute units idle during transfers and the interconnect idles during compute — alternating waste, at pod scale, on hardware billed by the second. The paper reports the technique removes 2–3× of communication cost, which says how much was sitting there.

The interesting constraint: this must work automatically. Google’s stack compiles JAX and TensorFlow models through XLA for TPUs; an optimization that requires hand-rewriting each model would never reach production workloads. (Contrast CoCoNet — covered elsewhere on this site — which attacks the same barrier on GPUs with a user-facing DSL and autotuner; this paper’s answer is an invisible compiler pass.)

What people did before, and where it broke

Overlap independent work. Data-parallel training overlaps layer N’s gradient AllReduce with layer N−1’s backward computation — legal because they touch different tensors. Compiler literature extracts independent loops via fission, tiling, and pipelining. None of this applies when the communication’s output is the computation’s input.

Distributed matmul algorithms. Cannon-style 2D algorithms interleave shard rotation with partial products — the germ of the right idea, but specialized: they cover operands partitioned along contracting dimensions, not the zoo of partitioning strategies real models use (the paper treats Cannon’s algorithm as a special case of its scheme).

Offload engines. Proposals like ACE add hardware near the network to run collectives. That helps independent communication but cannot manufacture overlap where dependence forbids it.

The baseline that mattered. Google’s own production stack (the GSPMD partitioner in XLA) generated well-tuned synchronous collectives: gather, then multiply, then scatter — each individually optimal, jointly serialized.

From sharded graph to overlapped loop — the compiler pipeline

  1. SPMD-partitioned dataflow graph

    The XLA partitioner has already sharded the model per the chosen strategy; the graph contains AllGather→Einsum and Einsum→ReduceScatter pairs — the targets.

  2. Decomposition pass new

    Rewrites each profitable pair into a Looped CollectiveEinsum: N partial Einsums (N = partition count) interleaved with N point-to-point CollectivePermutes around a logical ring, plus a DynamicUpdateSlice or Addition to combine partial results. Semantically exact — same numbers.

  3. Async CollectivePermute + scheduling pass new

    Each transfer becomes a non-blocking CollectivePermuteStart/Done pair. A bottom-up scheduler (working backward from the graph roots with ready/pending queues) places Starts early and Dones late, keeping in-flight transfers under the hardware’s synchronization-flag budget without wrecking memory liveness.

  4. Optimizations new

    Loop unrolling (bigger partial multiplies, fewer iterations), bidirectional rings that use both directions of the TPU’s inter-chip interconnect links (halving trips, at the price of a small prologue/epilogue), and fusion rewrites so XLA’s fuser neither breaks the overlap nor leaves pointwise work unfused.

  5. Cost-model gate new

    Decomposition is enabled per operation only when estimated to win: original comp time + comm time must exceed max(comp, ring-comm) + prologue/epilogue overhead. Ring transfers use less of the torus bandwidth than a native collective, so narrow layers keep the original code.

Everything is automatic inside XLA, serving JAX and TensorFlow frontends unchanged — no model rewrites, no annotations, production TPU v4 pods.

The idea

The core observation: a collective and its dependent Einsum are only monolithically dependent — piecewise, they are not. The AllGather’s output is a concatenation of N shards; the Einsum over that concatenation is algebraically a combination of N partial Einsums, one per shard. Pick an order for both sequences such that partial computation i uses only shards that have already arrived, while transfer i ships the shard needed later. Same-index pairs share no data — so they run in parallel.

AllGather → Einsum becomes: each chip starts multiplying with the shard it already owns while asynchronously forwarding that shard to its ring neighbor. Next iteration, it computes with the shard that just arrived while forwarding it onward. After N iterations every chip has used all N shards; a DynamicUpdateSlice has been stitching each partial result into place. The full gathered tensor never needed to exist before compute began.

Einsum → ReduceScatter inverts the flow: what circulates is not an operand but the accumulating result shard. Each chip computes a partial product, adds it into the accumulator it just received from its neighbor, and passes the growing sum along. After N hops, each shard has visited every chip, collected every contribution, and landed on its home partition — the scatter happened during the multiplies.

Getting this to production quality is the paper’s second half:

  • Asynchrony with a budget. Transfers become CollectivePermuteStart/Done pairs; a bottom-up scheduler pushes Starts early and Dones late while respecting the hardware’s limited synchronization flags. (It beat a top-down alternative by ~5%.)
  • Bidirectional rings. TPU inter-chip links carry full bandwidth both ways; splitting each shard stream into clockwise and counterclockwise halves cuts transfer trips — worth the most on heavily-partitioned dimensions.
  • Fusion diplomacy. XLA’s fusion heuristics, left alone, could fuse an addition into the wrong Einsum and create a false dependence on a pending transfer, serializing the loop. The pass rewrites concatenations into fusable Pad/Max forms and steers the fuser to keep independent work independent.
  • Knowing when to decline. The decomposed ring uses only part of the torus’s bandwidth (one mesh dimension), so if compute is too small to hide the longer transfer, overlap loses. A simple peak-rate cost model gates the rewrite per operation, and picks which of two candidate collectives to overlap when an Einsum offers both.

Four chips run an AllGather-Einsum as an overlapped loop

4-way partitioning (N=4). A0–A3 = shards of operand A, one per chip; B = each chip's resident operand. CP = CollectivePermute, a point-to-point neighbor transfer on the ring, issued as async Start/Done. DUS = DynamicUpdateSlice, writing a partial result into its slice of the output. Latencies illustrative; mechanics follow the paper's Figures 4 and 6.

Step 1 of 6

Baseline: assemble everything, then compute

The AllGather circulates shards until every chip holds the full A = [A0, A1, A2, A3]; only then does each chip run its Einsum with B. During the gather the multipliers are idle; during the multiply the interconnect is idle. This is the serialized pattern the pass hunts for.

timeline: [AllGather —— idle compute] → [Einsum —— idle links]

Why it works

  • Concatenation and summation are linear → an Einsum over gathered shards equals combined partial Einsums → decomposition is exact, not approximate, so it can ship as a default-on compiler pass.
  • Same-index slices share no data → the dependence that forbade overlap exists only at the monolithic granularity → at shard granularity, compute and transfer are free to run concurrently.
  • Transfers become Start/Done pairs scheduled far apart → the interconnect works during multiplies → when compute time covers transfer time, communication costs approach zero observed time.
  • Rings map onto the TPU torus’s dedicated inter-chip links, both directions → bidirectional streams halve trips → even heavily-partitioned dimensions keep transfers under compute.
  • A cost model gates each rewrite → operations that would lose (thin compute, small shards) keep native collectives → the pass never has to be globally right, only locally honest — which is what makes fleet-wide automatic deployment safe.

What it costs

  • Ring bandwidth discount. The decomposed sequence drives one mesh dimension of the torus rather than the full collective topology — the reason the cost gate exists at all.
  • Prologue/epilogue overhead. Bidirectional transfer needs a pre-shift before the loop and a realigning addition after it, charged as unoverlappable in the estimate.
  • Scheduler and hardware coupling. In-flight asynchronous transfers are capped by the system’s synchronization flags, and the whole design leans on TPU ICI links and XLA internals — porting to GPU clusters (the paper suggests NVLink-class fabrics) is plausible but undemonstrated here.
  • Not all communication is covered. AlltoAll (mixture-of-experts routing) and AllGathers without a dependent Einsum stay synchronous — visible in T5’s ~10% AlltoAll runtime share and GLaM’s ~40% utilization ceiling.
  • Compute must be big enough to hide behind. Narrow architectures (BigSSL_10B, GLaM_1T) get modest benefit — overlap hides latency; it cannot create arithmetic.

The evaluation

The technique ships in XLA and is measured on production-class workloads: six models from 10 billion to 1.03 trillion parameters — GPT-style (1T), the Meena conversational model (507B), Google’s MLPerf giant BERT (258B), T5 (513B), the GLaM mixture-of-experts model (1T), and the BigSSL speech model (10B) — running on TPU v4 pods of 128 to 2,048 chips, via JAX and TensorFlow frontends. The baseline is the same partitioning with ordinary synchronous collectives — Google’s own tuned production configuration.

Synchronous collectives vs decomposed overlap on TPU v4 pods

PropertyBaseline: gather → multiply → scatterThis work: looped, overlapped decomposition
Training throughput (six production models)
Peak FLOPS utilization
Communication cost
Where it helps least—
Energy
TPU v4 pods, 128–2,048 chips per model; throughput normalized to each configuration's peak FLOPS; models via JAX (GPT_1T, MLPerf_200B, T5_300B) and TensorFlow (Meena_500B, GLaM_1T, BigSSL_10B); identical chips and partitioning strategy for baseline and technique. Ablations: bottom-up scheduling beats top-down by ~5%; unrolling helps uniformly; bidirectional transfer matters most at high partition counts.Underlined values explain themselves when clicked.

Check your understanding

  1. 1. The decomposed ring of point-to-point transfers actually uses LESS interconnect bandwidth than the native torus AllGather it replaces. Predict when the rewrite still wins — and when it loses.

  2. 2. Why must the Einsum be decomposed along with the collective — why not just make the AllGather asynchronous and start it earlier?

  3. 3. In the Einsum→ReduceScatter loop, what circulates around the ring?

  4. 4. XLA’s standard fusion pass — normally pure benefit — could silently destroy this optimization. How?

Related papers on Machine Learning Systems

See all 212 →