Specification

Design Space 04: Distributed Programming Model

Series: AI-Native Programming Language Research — Design Space Memos Document 4 of 6 (planned: shapes, AD-through-effects, sparsity, distribution, verification, hardware abstraction)


1. Problem statement

Modern foundation-model training runs across thousands of accelerators. Inference for frontier models uses pipelines that span dozens of devices. Even fine-tuning a 70B model is a multi-GPU exercise. Distribution is no longer an optimization — it is the default execution mode for non-trivial ML.

Yet the programming model is essentially HPC code from 1995 with a thin Python wrapper. The user thinks in terms of:

The single most successful piece of compiler-driven distribution is GSPMD (Xu et al., 2021): the user annotates a few key tensors with sharding specifications, and the compiler propagates sharding through the rest of the graph and inserts collectives automatically. JAX pjit / shard_map, PyTorch's DTensor, and Alpa all build on this idea. It works. And it works because, in essence, GSPMD treats sharding as a type and propagation as type inference — without saying so explicitly.

The thesis of this memo: sharding is the fourth refinement axis on the tensor type, alongside shape (memo 1), aliasing (memo 2), and sparsity (memo 3). Treating it that way makes communication a type-changing coercion, makes auto-parallelization a constraint-solving problem over the type, and unifies distribution with the rest of the language design. GSPMD has the implementation; what's missing is the formal type system and the surface language that exposes it.


2. Taxonomy of parallelism

A distributed ML program is decomposed along several orthogonal axes. The main ones:

Strategy What's sharded What's replicated Communication
Data parallel (DP) Input batch Model All-reduce on gradients
Tensor parallel (TP) Individual tensor ops (matmul along a dim) Activations between TP regions All-reduce / all-gather within TP group
Pipeline parallel (PP) Layer groups across stages Within a stage Point-to-point (activations forward, gradients backward)
Sequence parallel (SP) Sequence-dimension activations Layer weights All-gather on activations
Context parallel (CP) / Ring attention Sequence dim of K/V in attention Q Ring all-gather pattern
Expert parallel (EP) MoE experts across devices Router All-to-all on tokens
Optimizer state sharding (ZeRO-1/2/3 / FSDP) Optimizer state, gradients, params Model logically All-gather params on demand

Real frontier models use all of these simultaneously. PaLM used 12-way TP × 64-way DP × 2-way PP. Frontier MoE models layer EP on top. Long-context models add SP and CP. The combinatorics are real, and choosing the right configuration is an optimization problem nobody actually solves — they pick a configuration that worked for a similar model and call it done.

The taxonomic observation: all of these are statements about how a tensor's axes map to a device mesh's axes. DP is "batch axis sharded over the data mesh axis." TP is "hidden axis sharded over the model mesh axis." SP is "sequence axis sharded over the data mesh axis." Different names for the same kind of object: a sharding specification.

This is exactly what GSPMD made explicit. It just hasn't propagated into language design.


3. Mesh as a type-level construct

A device mesh is a logical grid of devices with named axes. A 64-GPU cluster might be configured as a [8, 8] mesh with axes ("data", "model"), or as a [4, 4, 4] mesh with axes ("data", "model", "pipeline").

In current systems the mesh is a runtime configuration object (jax.sharding.Mesh, torch.distributed.DeviceMesh). In a type-aware language it should be a type-level construct: a kind, a phantom type, or a parameter on the tensor type. Concretely:

Tensor f16 [B, S, D] on mesh M with shard {B: data, S: seq, D: model}

The mesh M is a type-level value with named axes; the sharding specification maps tensor axes to mesh axes. This is exactly what jax.sharding.NamedSharding represents at runtime — the proposal is to lift it to compile time so the type checker can reason about it.

What you get from this lift:

  1. Mesh-axis arithmetic in the type system. A [B, S, D] tensor with B → data and another [D, V] tensor with D → model produce a result whose sharding can be computed from the operation, just as the result shape can. Most of GSPMD's propagation rules are typeable this way.

  2. Sharding mismatches as type errors. If you try to add two tensors with incompatible shardings, the compiler tells you, just as it would for incompatible shapes. Today this is a runtime error or a silent-resharding-with-bad-performance.

  3. Resharding as an explicit (and visible) coercion. When the compiler inserts an all-gather to reshard a tensor, it shows up in the IR (and ideally in profiler tooling) as an explicit operation, not as a hidden cost.

  4. Mesh polymorphism for portability. A function written generically over a mesh axis variable can run on different mesh sizes without modification. This is the "scale agnostic" property GSPMD argues for, made first-class in the type system.

The closest existing work: PartIR (Google), shard_map (JAX), and PyTorch DTensor all expose explicit sharding types. None of them lift the mesh itself to compile time.


4. Communication as type-changing operations

The most compelling consequence of typing sharding: collectives become type-changing operations whose insertion the compiler can infer.

Concrete examples:

In a typed setting, the compiler runs an inference pass that walks the graph, propagates sharding types through operations, and inserts the cheapest collective when shardings don't match at an operation boundary. This is what GSPMD does, dressed up as type inference.

Cost-aware insertion. The cheap-collective-insertion problem is non-trivial because there are usually multiple correct ways to make shardings agree. all_gather then operate vs. operate-then-all_reduce produce the same result with different costs. Alpa's contribution (Zheng et al., OSDI 2022) is essentially a cost model for this choice. A typed language should expose the cost model as part of the compiler's type-directed search.

Effect interpretation. From the memo 2 framing: communication is an effect. Comm is a effect with operations all_reduce, all_gather, etc. A handler interprets these on a particular substrate (NCCL, Gloo, MPI, simulator). The typed sharding determines which communication is needed; the handler determines how it's executed. The compositional handler story extends naturally.


5. Comparison across systems

MPI / NCCL Megatron-LM DeepSpeed / ZeRO JAX pjit PyTorch DTensor / FSDP Alpa GSPMD
Sharding in type ✓ (NamedSharding) ✓ (DTensor spec) Internal ✓ (compiler IR)
Mesh as construct Implicit (process groups) Implicit ✓ (runtime) ✓ (runtime)
Compiler-inserted collectives Partial
Auto-parallelization Partial (ZeRO levels) ✗ (manual sharding) ✓ (search)
Cost model for sharding Heuristic Limited
Mesh polymorphism Limited Limited Limited
Composes with AD Manual Manual Manual
Composes with dynamic shape N/A Limited Limited Limited Limited
Composes with sparsity
Heterogeneous mesh Limited Limited Limited Limited

The pattern visible in this table: the more compiler-driven a system is, the closer it gets to type-system semantics — but none of them complete the journey. GSPMD has the propagation; Alpa has the cost model; JAX has the surface API. The unified design has all three plus integration with the rest of the type system.

The bottom three rows (composing with shape, sparsity, AD) are the integration points where current systems break down. Sharding × sparsity is the production pain point in MoE deployment; sharding × dynamic shape is the LLM-serving pain point; sharding × AD just barely works because everyone treats AD as a separate compiler pass that re-runs sharding propagation on the backward.


6. The auto-parallelization problem

Given a model and a mesh, what is the optimal sharding strategy? This is the open question.

Alpa's framing (Zheng et al., OSDI 2022): split the problem into intra-operator (how to shard one matmul across devices) and inter-operator (how to assign layers to pipeline stages) parallelism. Solve intra-op with integer linear programming; solve inter-op with dynamic programming. This produces sharding plans competitive with hand-tuned Megatron-LM on standard benchmarks.

What Alpa doesn't solve:

What a language-level approach could add:

  1. Constraint-based search, where the user gives partial annotations (e.g., "the embedding axis is replicated, the batch axis is data-parallel") and the compiler infers the rest. This is like Hindley-Milner type inference where the user can annotate and the rest is inferred.

  2. Compositional cost models, where the cost of a complex pipeline is computed from the costs of its parts. The handler-based effect framing helps: each Comm handler has a cost model.

  3. Online repartitioning, where the runtime can change the sharding strategy based on observed load (e.g., MoE expert imbalance). The type system records valid shardings; the runtime picks among them.

  4. Verification of sharding correctness, in the sense of "the program with this sharding produces the same result as the unsharded program." This is again where memo 5 (verification) becomes relevant — sharding transformations are program transformations whose semantic preservation is non-trivial.


7. The hard composition problems

Where the unified-type-system thesis gets stress-tested.

7.1 Sharding × dynamic shape

If the sequence length is symbolic, can it be sharded along the SP axis? Yes — but only if the shape is a multiple of the mesh axis size, which is a constraint on the symbolic shape variable. This wants to be a refinement-typed property: Tensor f16 [B, S where S % data_axis_size == 0, D].

LLM serving with continuous batching is the canonical hard case. The batch dimension grows and shrinks dynamically; the sharding along it must be valid for the current batch size. Most current systems either pad to a fixed multiple (wasted compute) or skip data parallelism entirely on the dynamic axis.

7.2 Sharding × sparsity

Sharding a sparse tensor across devices: how? Two approaches:

For dynamic sparsity (MoE), neither is satisfactory. Expert parallelism shards experts (which are dense) across devices; the sparsity is in the routing, not in the weights. The all-to-all that dispatches tokens to experts is a giant communication operation that bottlenecks scaling. Nobody has solved load-balanced expert parallelism with dynamic routing.

7.3 Sharding × AD

The backward pass produces gradients with shardings determined by the forward shardings. In simple cases this is automatic (GSPMD handles it). The hard cases:

The unified-type-system answer: the AD transformation propagates sharding types through the backward pass, just as it propagates shapes and sparsity. The collectives in the backward pass are inferred the same way as in the forward.

7.4 Heterogeneous meshes

Modern training clusters are not uniform. Different GPU generations, CPU offload, optical fabric vs. PCIe, etc. The communication cost between devices is non-uniform. Pure GSPMD-style propagation assumes a uniform mesh; production systems handle heterogeneity with bespoke code.

The cleanest framing: a mesh has not just axes but also cost annotations on its edges. Sharding choice considers both correctness and the cost graph. This is closer to a heterogeneous parallel computing model (cf. Sequoia, Legion) than to BSP.


8. Open research problems

  1. A fully typed sharding calculus. Formal type system where sharding is a type, mesh is a kind/parameter, collectives are coercions, and resharding is a coercion. Prove subject reduction (well-typed sharded programs reduce to well-typed sharded programs) and semantic preservation (sharded program = unsharded program).

  2. Sharding inference with partial annotations. Like Hindley-Milner, but for sharding. The user annotates a few key tensors; the compiler infers the rest. Currently in JAX you must annotate at every pjit boundary; better inference is possible.

  3. Cost-aware sharding inference. Given multiple correct shardings, pick the cheapest. Alpa solves a version of this; doing it as part of the type system is open.

  4. Sharding for dynamic shape. What's the right abstraction for "this axis is sharded if it's a multiple of the mesh size, replicated otherwise"? Refinement types, conditional shardings, or something new?

  5. Sharding for dynamic sparsity (MoE). Load-balanced expert parallelism is the production-scale open problem. Solutions in MegaBlocks address the kernel level; the distributed MoE story is mostly hand-tuned.

  6. Verified sharding transformations. The CompCert-style question: can we prove that a sharded program produces the same result (up to numerical reordering) as the unsharded one? Connects to memo 5.

  7. Heterogeneous and hierarchical meshes. Real clusters are NUMA, multi-tier, multi-generation. Sharding should account for this; current systems mostly don't.

  8. Online resharding. Training dynamics can favor different shardings at different points (warm-up vs. main training, different MoE load). A language that makes resharding cheap and the type system enforces correctness across resharding could enable adaptive parallelism.

  9. Interaction with effects (memo 2). Communication-as-effect composes with other effects (Random, State) in non-trivial ways. The order of with reverseMode handle and with allReduce handle matters. Working this out compositionally is open.


9. Recommendation for an AI-native language design

Concrete positions, in priority order:

The thesis: the GSPMD compiler is a type inferencer in disguise, and the auto-parallelization problem is constraint solving over the type. A language that puts sharding in the type system from day one absorbs GSPMD, Alpa, and ZeRO into one framework rather than three. The mesh becomes a kind; collectives become coercions; pipeline schedules become continuations; auto-parallelization becomes inference.


10. References

Foundational distributed systems / HPC

Compiler-driven sharding

Hand-coded parallelism (the strategies the compiler should subsume)

Distributed tensor abstractions

Parallel programming languages (research lineage)

Polyhedral / scheduling foundations (relevant for tiling and distribution)

Production-scale training reports (concrete configurations)


11. Connections to the rest of the series

12. Cross-memo synthesis

Four memos in, the unified-type-system thesis can be stated more precisely:

The "tensor" of an AI-native language is a type with several orthogonal refinement axes:

  1. Shape — dimension sizes, possibly symbolic, possibly with constraints (memo 1).
  2. Effects — purity, mutation, randomness, divergence, the things AD needs to know (memo 2).
  3. Structure — sparsity patterns, low-rank factorizations, quantization, hardware layouts (memo 3).
  4. Sharding — distribution across a typed mesh (this memo).

Operations on tensors compose along all four axes. The compiler infers the result's refinements from the operands. Mismatches at operation boundaries are either type errors or compiler-inserted coercions (broadcast for shape, resharding for sharding, format conversion for sparsity, AD-handler insertion for effects).

The user writes algorithms; the compiler manages the four refinement axes. This is the program model that current systems implement piecemeal.

Memos 5 (verification) and 6 (hardware abstraction) will test whether this framework holds up under the verification load and the hardware-portability load respectively. If it does, the synthesis is the design document for an actual language.

13. Next memo

Design Space 05: Verification. ML compilers do enormous program transformations: AD, sharding propagation, kernel fusion, layout selection, quantization. Each transformation must preserve semantics, and most do not have proofs. A CompCert-for-ML, or a verified-AD, or a verified-sharding-pass, is a real research target — and the most concretely achievable contribution in this whole design space, given existing verification infrastructure (Coq, Lean 4, F*, Frama-C). This is the memo most directly aligned with formal-methods backgrounds.