IPython has a few magics to make working with your engines a bit nicer in IPython, e.g. via a Jupyter notebook.
As always, first we start a cluster (or connect to an existing one):
import ipyparallel as ipp
rc = ipp.Cluster(n=4).start_and_connect_sync()
dv = rc[:]
rc.ids
Creating a Client registers the parallel magics %px, %%px, %pxresult, %pxconfig, and %autopx.
These magics are initially associated with a DirectView, always associated with all currently registered engines.
First, we can execute single lines remotely with %px:
%px a=5
%px print(a)
%px a
with dv.sync_imports():
import sys
%px from __future__ import print_function
%px print("ERROR", file=sys.stderr)
You don't have to wait for results. The %pxconfig magic lets you change the default blocking/targets for the %px magics:
%pxconfig --noblock
%px import time
%px time.sleep(1)
%px time.time()
But you will notice that this didn't output the result of the last command.
For this, we have %pxresult, which displays the output of the latest request:
%pxresult
Remember, an IPython engine is IPython, so you can do magics remotely as well!
%pxconfig --block
%px %matplotlib inline
%%px can be used to lower the priority of the engines to improve system performance under heavy CPU load.
%%px
import psutil
psutil.Process().nice(20 if psutil.POSIX else psutil.IDLE_PRIORITY_CLASS)
%%px
import numpy as np
import matplotlib.pyplot as plt
%%px can also be used as a cell magic, for submitting whole blocks.
This one acceps --block and --noblock flags to specify
the blocking behavior, though the default is unchanged.
dv.scatter('id', dv.targets, flatten=True)
dv['stride'] = len(dv)
%%px --noblock
x = np.linspace(0,np.pi,1000)
for n in range(id,12, stride):
print(n)
plt.plot(x,np.sin(n*x))
plt.title("Plot %i" % id)
%pxresult
It also lets you choose some amount of the grouping of the outputs with --group-outputs:
The choices are:
engine - all of an engine's output is collected togethertype - where stdout of each engine is grouped, etc. (the default)order - same as type, but individual displaypub outputs are interleaved.
That is, it will output the first plot from each engine, then the second from each,
etc.%%px --group-outputs=engine
x = np.linspace(0,np.pi,1000)
for n in range(id+1,12, stride):
print(n)
plt.figure()
plt.plot(x,np.sin(n*x))
plt.title("Plot %i" % n)
When you specify 'order', then individual display outputs (e.g. plots) will be interleaved.
%pxresult takes the same output-ordering arguments as %%px,
so you can view the previous result in a variety of different ways with a few sequential calls to %pxresult:
%pxresult --group-outputs=order
When a DirectView has a single target, the output is a bit simpler (no prefixes on stdout/err, etc.):
from __future__ import print_function
def generate_output():
"""function for testing output
publishes two outputs of each type, and returns something
"""
import sys,os
from IPython.display import display, HTML, Math
print("stdout")
print("stderr", file=sys.stderr)
display(HTML("<b>HTML</b>"))
print("stdout2")
print("stderr2", file=sys.stderr)
display(Math(r"\alpha=\beta"))
return os.getpid()
dv['generate_output'] = generate_output
You can also have more than one set of parallel magics registered at a time.
The View.activate() method takes a suffix argument, which is added to 'px'.
e0 = rc[-1]
e0.block = True
e0.activate('0')
%px0 generate_output()
%px generate_output()
As mentioned above, we can redisplay those same results with various grouping:
%pxresult --group-outputs order
%pxresult --group-outputs engine
When you raise exceptions with the parallel exception, the CompositeError raised locally will display your remote traceback.
%%px
from numpy.random import random
A = random((100, 100, 'invalid shape'))
Sometimes, an error will occur on just one engine, while the rest are still working.
When this happens, you will see errors immediately, and can interrupt the execution:
dv.scatter("rank", rc.ids, flatten=True)
%%px
import time
if rank == 0:
raise RuntimeError("rank 0 failed!")
time.sleep(10)
Remember, Engines are IPython too, so the cell that is run remotely by %%px can in turn use a cell magic.
%%px
%%timeit
from numpy.random import random
from numpy.linalg import norm
A = random((100, 100))
norm(A, 2)
You can instruct %%px to also execute the cell locally.
This is useful for interactive definitions,
or if you want to load a data source everywhere,
not just on the engines.
%%px --local
import os
thispid = os.getpid()
print(thispid)