liveBook · Manning

Parallelism

As explained in Deep Learning with JAX

The execution of computation concurrently across multiple devices or cores.

Parallelization runs computation concurrently across multiple devices. In JAX, it is useful when independent portions of work can be processed simultaneously, including large-scale neural-network training and scientific simulations. section 7.0

The most direct JAX mechanism is pmap(), or parallel map. It applies the same program to data chunks on multiple devices using single-program multiple-data (SPMD) execution. Adding devices increases throughput, although communication overhead usually makes scaling sublinear. section 7.0

Control parallel execution with:

  • in_axes, which selects the input dimension mapped across devices. section 7.5
  • out_axes, which controls the layout of mapped outputs. section 7.5
  • axis_name and optionally axis_index_groups, which enable collective communication across all devices or device groups. section 7.5
  • Array shapes whose mapped dimension does not exceed jax.local_device_count(); inputs must be reshaped when necessary. section 7.5

Because JAX transformations are composable, pmap() can be nested or combined with vmap() and grad(). It also supports data-parallel neural-network training. In multihost configurations, each process handles local devices and data while collective operations communicate across global devices. section 7.5 section 1.1.2

Tensor sharding provides a more implicit alternative: shard arrays first, then leave computation code unchanged while the compiler partitions work across devices. Earlier mechanisms such as xmap() and pjit() are retained mainly for historical or legacy-code context. section 7.0 section 8.1

Parallel dot-product calculations

A distributed-array example defines a dot(v1, v2) function with jnp.vdot, creates large vectors, and shards the arrays before computation. The dot-product code remains unchanged while the calculation proceeds in parallel; array-sharding visualization reveals tensor placement. section 8.1

Listing 8.1 Parallelizing dot product calculations using distributed arrays
from jax.experimental import mesh_utils                             #1
from jax.sharding import PositionalSharding                         #1
from jax import random
 
def dot(v1, v2):                                                    #2
  return jnp.vdot(v1, v2)
 
rng_key = random.PRNGKey(42)
 
vs = random.normal(rng_key, shape=(8_000,10_000))                   #3
v1s = vs[:4_000,:]                                                  #3
v2s = vs[4_000:,:]                                                  #3
 
v1s.shape, v2s.shape
 
>>> (4000, 10000), (4000, 10000))
 
jax.debug.visualize_array_sharding(v1s)                             #4
 
>>> ┌────────────────────────────────────────────────────────────┐
>>> │                                                            │
>>> │                                                            │
>>> │                                                            │
>>> │                                                            │
>>> │                           TPU 0                            │
>>> │                                                            │
>>> │                                                            │
>>> │                                                            │
>>> │                                                            │
>>> └────────────────────────────────────────────────────────────┘
 
sharding = PositionalSharding(mesh_utils.create_device_mesh((8,1))) #5
sharding
 
>>> PositionalSharding([[{TPU 0}]                                   #6
>>>                     [{TPU 1}]                                   #6
>>>                     [{TPU 2}]                                   #6
>>>                     [{TPU 3}]                                   #6
>>>                     [{TPU 6}]                                   #6
>>>                     [{TPU 7}]                                   #6
>>>                     [{TPU 4}]                                   #6
>>>                     [{TPU 5}]])                                 #6
 
v1sp = jax.device_put(v1s, sharding)                                #7
v2sp = jax.device_put(v2s, sharding)                                #7
 
type(v1sp)
 
>>> jaxlib.xla_extension.ArrayImpl                                  #8
 
jax.debug.visualize_array_sharding(v1sp)                            #9
 
>>> ┌────────────────────────────────────────────────────────────┐
>>> │                           TPU 0                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 1                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 2                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 3                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 6                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 7                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 4                            │
>>> ├────────────────────────────────────────────────────────────┤
>>> │                           TPU 5                            │
>>> └────────────────────────────────────────────────────────────┘
 
d = jax.vmap(dot)(v1sp, v2sp)                                       #10
 
d.shape
 
>>> (4000,)                                                         #11
 
jax.debug.visualize_array_sharding(d)                               #12
 
>>> ┌───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐
>>> │ TPU 0 │ TPU 1 │ TPU 2 │ TPU 3 │ TPU 6 │ TPU 7 │ TPU 4 │ TPU 5 │
>>> └───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘
#1 Imports modules for sharding
#2 Our familiar function for calculating a dot product between two vectors
#3 This time, we generate vectors wider than before.
#4 Visualizes sharding; the whole tensor resides on a single device
#5 Creates 2D mesh to shard the rank-2 tensor’s first axis across eight TPUs
#6 create_device_mesh returns the most performant ordering of devices given the shape.
#7 Shards tensors across their first axis
#8 Checks the type
#9 Checks that the tensor is now sharded across multiple devices
#10 Performs the computation
#11 The shape is as expected.
#12 The result is also distributed across many devices.

Inspect the distributed-array listing to see array creation, slicing, dot-product definition, and sharding visualization. section 8.1

How Deep Learning with JAX covers it

How deep it goes
Explains pmap() parameters, SPMD execution, collectives, data parallelism, and multihost behavior, while deferring tensor-sharding details.
Examples
Demonstrates distributed dot-product calculations with sharded arrays and visualized tensor placement.
What it assumes
Assumes pure-function JAX code and array-shape awareness, while introducing device mapping, SPMD execution, and multihost coordination.
What you can do after
Apply pmap() to split arrays across devices, configure mapped axes and collectives, and implement data-parallel training.
How it connects
Connects parallelization with pmap(), tensor sharding, SPMD, collective operations, vmap(), grad(), JIT compilation, and data-parallel training.

Also explained in

Compare how 37 books explain parallelism →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Deep Learning with JAX. The section links point to the parts of the book it draws on.