Jupyter prilično jednostavno omogućava paralelizaciju koda. Dokumentaciju možete naći ovdje: Using IPython for parallel computing.
No modifikacija Jupyter notebook u Sagemath oblaku to ne omogućuje. No u Sagemath oblaku možemo pokretnuti čisti Jupyter notebook tako što u otvorimo link oblika
https://cloud.sagemath.com/[id_projekta]/port/jupyter
u pregledniku. Kako se ovim pokreće server, možda je potrebno poslije nekoliko sekundi osvježiti stranicu.
Dobit ćemo Jupyter Dashboard, s popisom notebooka. Ako kliknemo na menu Clusters možemo podesiti broj procesora (# of engines). Stavimo recimo na 4.
U nastavku ćemo pokazati na jednostavnom primjeru kako koristiti paralelizaciju u Jupyter notebooku.
In [1]:
from IPython.parallel import Client
In [2]:
cli = Client()
S atributom ids
dobijamo listu procesora u klasteru:
In [3]:
cli.ids
Out[3]:
Sada možemo izabirati što se izvodi na kojem procesoru.
In [4]:
def getpid():
""" vraća jedinstveni ID trenutnog procesora """
import os
return os.getpid()
In [5]:
# procesor u kojem se vrti notebook
getpid()
Out[5]:
In [6]:
# na prvom procesoru
cli[0].apply_sync(getpid)
Out[6]:
In [7]:
# na svim procesorima
cli[:].apply_sync(getpid)
Out[7]:
Najjednostavnije korištenje je pomoću dekoratora:
@view.parallel(block=True)
Primjer:
In [8]:
dview = cli[:]
In [9]:
@dview.parallel(block=True)
def dummy_task(delay):
""" funkcija koja ništa ne radi """
import os, time
t0 = time.time()
pid = os.getpid()
time.sleep(delay)
t1 = time.time()
return [pid, t0, t1]
In [10]:
# generiramo slučajno kašnjenje
import numpy
delay_times = numpy.random.rand(4)
Da bi mapirali funkciju dummy_task
na podatke, koristimo map
metodu u dummy_task
:
In [11]:
dummy_task.map(delay_times)
Out[11]:
Napravimo sada to na više zadataka i vizualizirajmo što se dešava:
In [18]:
from matplotlib.pyplot import *
%matplotlib inline
def visualize_tasks(results):
res = numpy.array(results)
fig, ax = subplots(figsize=(10, res.shape[1]))
yticks = []
yticklabels = []
tmin = min(res[:,1])
for n, pid in enumerate(numpy.unique(res[:,0])):
yticks.append(n)
yticklabels.append("%d" % pid)
for m in numpy.where(res[:,0] == pid)[0]:
ax.add_patch(Rectangle((res[m,1] - tmin, n-0.25),res[m,2] - res[m,1], 0.5, color="green", alpha=0.5))
ax.set_ylim(-.5, n+.5)
ax.set_xlim(0, max(res[:,2]) - tmin + 0.)
ax.set_yticks(yticks)
ax.set_yticklabels(yticklabels)
ax.set_ylabel("PID")
ax.set_xlabel("sekunde")
In [13]:
delay_times = numpy.random.rand(64)
In [19]:
result = dummy_task.map(delay_times)
visualize_tasks(result)
Za balansiranje možemo koristiti load_balanced_view
metodu:
In [20]:
lbview = cli.load_balanced_view()
In [21]:
@lbview.parallel(block=True)
def dummy_task_load_balanced(delay):
""" funkcija koja ništa ne radi """
import os, time
t0 = time.time()
pid = os.getpid()
time.sleep(delay)
t1 = time.time()
return [pid, t0, t1]
In [22]:
result = dummy_task_load_balanced.map(delay_times)
visualize_tasks(result)
Vidimo da su sada procesori bolje iskorišteni.