GitHub

@@ -471,7 +471,10 @@ We'll apply a `lax.fori_loop`, which is a version of a for loop that can be comp

471471

```{code-cell} ipython3

472472

cpu = jax.devices("cpu")[0]

473473474-

@partial(jax.jit, static_argnames=("n",), device=cpu)

474+

# Pin the input to the CPU, which keeps the whole computation there

475+

x0_cpu = jax.device_put(0.1, cpu)

476+477+

@partial(jax.jit, static_argnames=("n",))

475478

def qm_jax_fori(x0, n, α=4.0):

476479477480

x = jnp.empty(n + 1).at[0].set(x0)

@@ -485,7 +488,7 @@ def qm_jax_fori(x0, n, α=4.0):

485488

```

486489487490

* We hold `n` static because it affects array size and hence JAX wants to specialize on its value in the compiled code.

488-

* We pin to the CPU via `device=cpu` because this sequential workload consists of many small operations, leaving little opportunity for GPU parallelism.

491+

* We pin the input to the CPU with `jax.device_put` (which keeps the whole computation on the CPU) because this sequential workload consists of many small operations, leaving little opportunity for GPU parallelism.

489492490493

Important: Although `at[t].set` appears to create a new array at each step, inside a JIT-compiled function the compiler detects that the old array is no longer needed and performs the update in place!

491494

@@ -494,7 +497,7 @@ Let's time it with the same parameters:

494497

```{code-cell} ipython3

495498

with qe.Timer():

496499

# First run

497-

x_jax = qm_jax_fori(0.1, n)

500+

x_jax = qm_jax_fori(x0_cpu, n)

498501

# Hold interpreter

499502

x_jax.block_until_ready()

500503

```

@@ -504,7 +507,7 @@ Let's run it again to eliminate compilation overhead:

504507

```{code-cell} ipython3

505508

with qe.Timer():

506509

# Second run

507-

x_jax = qm_jax_fori(0.1, n)

510+

x_jax = qm_jax_fori(x0_cpu, n)

508511

# Hold interpreter

509512

x_jax.block_until_ready()

510513

```

@@ -521,7 +524,7 @@ although the syntax is difficult to remember.

521524522525523526

```{code-cell} ipython3

524-

@partial(jax.jit, static_argnames=("n",), device=cpu)

527+

@partial(jax.jit, static_argnames=("n",))

525528

def qm_jax_scan(x0, n, α=4.0):

526529

def update(x, t):

527530

x_new = α * x * (1 - x)

@@ -538,7 +541,7 @@ Let's time it with the same parameters:

538541

```{code-cell} ipython3

539542

with qe.Timer():

540543

# First run

541-

x_jax = qm_jax_scan(0.1, n)

544+

x_jax = qm_jax_scan(x0_cpu, n)

542545

# Hold interpreter

543546

x_jax.block_until_ready()

544547

```

@@ -548,7 +551,7 @@ Let's run it again to eliminate compilation overhead:

548551

```{code-cell} ipython3

549552

with qe.Timer():

550553

# Second run

551-

x_jax = qm_jax_scan(0.1, n)

554+

x_jax = qm_jax_scan(x0_cpu, n)

552555

# Hold interpreter

553556

x_jax.block_until_ready()

554557

```

Read the original on github.com ↗