##// END OF EJS Templates
document new AsyncResult properties
MinRK -
Show More
@@ -0,0 +1,116 b''
1 .. _parallel_asyncresult:
2
3 ======================
4 The AsyncResult object
5 ======================
6
7 In non-blocking mode, :meth:`apply` submits the command to be executed and
8 then returns a :class:`~.AsyncResult` object immediately. The
9 AsyncResult object gives you a way of getting a result at a later
10 time through its :meth:`get` method, but it also collects metadata
11 on execution.
12
13
14 Beyond multiprocessing's AsyncResult
15 ====================================
16
17 .. Note::
18
19 The :class:`~.AsyncResult` object provides a superset of the interface in
20 :py:class:`multiprocessing.pool.AsyncResult`. See the
21 `official Python documentation <http://docs.python.org/library/multiprocessing#multiprocessing.pool.AsyncResult>`_
22 for more on the basics of this interface.
23
24 Our AsyncResult objects add a number of convenient features for working with
25 parallel results, beyond what is provided by the original AsyncResult.
26
27
28 get_dict
29 --------
30
31 First, is :meth:`.AsyncResult.get_dict`, which pulls results as a dictionary
32 keyed by engine_id, rather than a flat list. This is useful for quickly
33 coordinating or distributing information about all of the engines.
34
35 As an example, here is a quick call that gives every engine a dict showing
36 the PID of every other engine:
37
38 .. sourcecode:: ipython
39
40 In [10]: ar = rc[:].apply_async(os.getpid)
41 In [11]: pids = ar.get_dict()
42 In [12]: rc[:]['pid_map'] = pids
43
44 This trick is particularly useful when setting up inter-engine communication,
45 as in IPython's :file:`examples/parallel/interengine` examples.
46
47
48 Metadata
49 ========
50
51 IPython.parallel tracks some metadata about the tasks, which is stored
52 in the :attr:`.Client.metadata` dict. The AsyncResult object gives you an
53 interface for this information as well, including timestamps stdout/err,
54 and engine IDs.
55
56
57 Timing
58 ------
59
60 IPython tracks various timestamps as :py:class:`.datetime` objects,
61 and the AsyncResult object has a few properties that turn these into useful
62 times (in seconds as floats).
63
64 For use while the tasks are still pending:
65
66 * :attr:`ar.elapsed` is just the elapsed seconds since submission, for use
67 before the AsyncResult is complete.
68 * :attr:`ar.progress` is the number of tasks that have completed. Fractional progress
69 would be::
70
71 1.0 * ar.progress / len(ar)
72
73 * :meth:`AsyncResult.wait_interactive` will wait for the result to finish, but
74 print out status updates on progress and elapsed time while it waits.
75
76 For use after the tasks are done:
77
78 * :attr:`ar.serial_time` is the sum of the computation time of all of the tasks
79 done in parallel.
80 * :attr:`ar.wall_time` is the time between the first task submitted and last result
81 received. This is the actual cost of computation, including IPython overhead.
82
83
84 .. note::
85
86 wall_time is only precise if the Client is waiting for results when
87 the task finished, because the `received` timestamp is made when the result is
88 unpacked by the Client, triggered by the :meth:`~Client.spin` call. If you
89 are doing work in the Client, and not waiting/spinning, then `received` might
90 be artificially high.
91
92 An often interesting metric is the time it actually cost to do the work in parallel
93 relative to the serial computation, and this can be given simply with
94
95 .. sourcecode:: python
96
97 speedup = ar.serial_time / ar.wall_time
98
99
100 Map results are iterable!
101 =========================
102
103 When an AsyncResult object has multiple results (e.g. the :class:`~AsyncMapResult`
104 object), you can actually iterate through them, and act on the results as they arrive:
105
106 .. literalinclude:: ../../examples/parallel/itermapresult.py
107 :language: python
108 :lines: 20-66
109
110 .. seealso::
111
112 When AsyncResult or the AsyncMapResult don't provide what you need (for instance,
113 handling individual results as they arrive, but with metadata), you can always
114 just split the original result's ``msg_ids`` attribute, and handle them as you like.
115
116 For an example of this, see :file:`docs/examples/parallel/customresult.py`
@@ -1,23 +1,24 b''
1 .. _parallel_index:
1 .. _parallel_index:
2
2
3 ====================================
3 ====================================
4 Using IPython for parallel computing
4 Using IPython for parallel computing
5 ====================================
5 ====================================
6
6
7 .. toctree::
7 .. toctree::
8 :maxdepth: 2
8 :maxdepth: 2
9
9
10 parallel_intro.txt
10 parallel_intro.txt
11 parallel_process.txt
11 parallel_process.txt
12 parallel_multiengine.txt
12 parallel_multiengine.txt
13 parallel_task.txt
13 parallel_task.txt
14 asyncresult.txt
14 parallel_mpi.txt
15 parallel_mpi.txt
15 parallel_db.txt
16 parallel_db.txt
16 parallel_security.txt
17 parallel_security.txt
17 parallel_winhpc.txt
18 parallel_winhpc.txt
18 parallel_demos.txt
19 parallel_demos.txt
19 dag_dependencies.txt
20 dag_dependencies.txt
20 parallel_details.txt
21 parallel_details.txt
21 parallel_transition.txt
22 parallel_transition.txt
22
23
23
24
@@ -1,869 +1,865 b''
1 .. _parallel_multiengine:
1 .. _parallel_multiengine:
2
2
3 ==========================
3 ==========================
4 IPython's Direct interface
4 IPython's Direct interface
5 ==========================
5 ==========================
6
6
7 The direct, or multiengine, interface represents one possible way of working with a set of
7 The direct, or multiengine, interface represents one possible way of working with a set of
8 IPython engines. The basic idea behind the multiengine interface is that the
8 IPython engines. The basic idea behind the multiengine interface is that the
9 capabilities of each engine are directly and explicitly exposed to the user.
9 capabilities of each engine are directly and explicitly exposed to the user.
10 Thus, in the multiengine interface, each engine is given an id that is used to
10 Thus, in the multiengine interface, each engine is given an id that is used to
11 identify the engine and give it work to do. This interface is very intuitive
11 identify the engine and give it work to do. This interface is very intuitive
12 and is designed with interactive usage in mind, and is the best place for
12 and is designed with interactive usage in mind, and is the best place for
13 new users of IPython to begin.
13 new users of IPython to begin.
14
14
15 Starting the IPython controller and engines
15 Starting the IPython controller and engines
16 ===========================================
16 ===========================================
17
17
18 To follow along with this tutorial, you will need to start the IPython
18 To follow along with this tutorial, you will need to start the IPython
19 controller and four IPython engines. The simplest way of doing this is to use
19 controller and four IPython engines. The simplest way of doing this is to use
20 the :command:`ipcluster` command::
20 the :command:`ipcluster` command::
21
21
22 $ ipcluster start -n 4
22 $ ipcluster start -n 4
23
23
24 For more detailed information about starting the controller and engines, see
24 For more detailed information about starting the controller and engines, see
25 our :ref:`introduction <parallel_overview>` to using IPython for parallel computing.
25 our :ref:`introduction <parallel_overview>` to using IPython for parallel computing.
26
26
27 Creating a ``DirectView`` instance
27 Creating a ``DirectView`` instance
28 ==================================
28 ==================================
29
29
30 The first step is to import the IPython :mod:`IPython.parallel`
30 The first step is to import the IPython :mod:`IPython.parallel`
31 module and then create a :class:`.Client` instance:
31 module and then create a :class:`.Client` instance:
32
32
33 .. sourcecode:: ipython
33 .. sourcecode:: ipython
34
34
35 In [1]: from IPython.parallel import Client
35 In [1]: from IPython.parallel import Client
36
36
37 In [2]: rc = Client()
37 In [2]: rc = Client()
38
38
39 This form assumes that the default connection information (stored in
39 This form assumes that the default connection information (stored in
40 :file:`ipcontroller-client.json` found in :file:`IPYTHON_DIR/profile_default/security`) is
40 :file:`ipcontroller-client.json` found in :file:`IPYTHON_DIR/profile_default/security`) is
41 accurate. If the controller was started on a remote machine, you must copy that connection
41 accurate. If the controller was started on a remote machine, you must copy that connection
42 file to the client machine, or enter its contents as arguments to the Client constructor:
42 file to the client machine, or enter its contents as arguments to the Client constructor:
43
43
44 .. sourcecode:: ipython
44 .. sourcecode:: ipython
45
45
46 # If you have copied the json connector file from the controller:
46 # If you have copied the json connector file from the controller:
47 In [2]: rc = Client('/path/to/ipcontroller-client.json')
47 In [2]: rc = Client('/path/to/ipcontroller-client.json')
48 # or to connect with a specific profile you have set up:
48 # or to connect with a specific profile you have set up:
49 In [3]: rc = Client(profile='mpi')
49 In [3]: rc = Client(profile='mpi')
50
50
51
51
52 To make sure there are engines connected to the controller, users can get a list
52 To make sure there are engines connected to the controller, users can get a list
53 of engine ids:
53 of engine ids:
54
54
55 .. sourcecode:: ipython
55 .. sourcecode:: ipython
56
56
57 In [3]: rc.ids
57 In [3]: rc.ids
58 Out[3]: [0, 1, 2, 3]
58 Out[3]: [0, 1, 2, 3]
59
59
60 Here we see that there are four engines ready to do work for us.
60 Here we see that there are four engines ready to do work for us.
61
61
62 For direct execution, we will make use of a :class:`DirectView` object, which can be
62 For direct execution, we will make use of a :class:`DirectView` object, which can be
63 constructed via list-access to the client:
63 constructed via list-access to the client:
64
64
65 .. sourcecode:: ipython
65 .. sourcecode:: ipython
66
66
67 In [4]: dview = rc[:] # use all engines
67 In [4]: dview = rc[:] # use all engines
68
68
69 .. seealso::
69 .. seealso::
70
70
71 For more information, see the in-depth explanation of :ref:`Views <parallel_details>`.
71 For more information, see the in-depth explanation of :ref:`Views <parallel_details>`.
72
72
73
73
74 Quick and easy parallelism
74 Quick and easy parallelism
75 ==========================
75 ==========================
76
76
77 In many cases, you simply want to apply a Python function to a sequence of
77 In many cases, you simply want to apply a Python function to a sequence of
78 objects, but *in parallel*. The client interface provides a simple way
78 objects, but *in parallel*. The client interface provides a simple way
79 of accomplishing this: using the DirectView's :meth:`~DirectView.map` method.
79 of accomplishing this: using the DirectView's :meth:`~DirectView.map` method.
80
80
81 Parallel map
81 Parallel map
82 ------------
82 ------------
83
83
84 Python's builtin :func:`map` functions allows a function to be applied to a
84 Python's builtin :func:`map` functions allows a function to be applied to a
85 sequence element-by-element. This type of code is typically trivial to
85 sequence element-by-element. This type of code is typically trivial to
86 parallelize. In fact, since IPython's interface is all about functions anyway,
86 parallelize. In fact, since IPython's interface is all about functions anyway,
87 you can just use the builtin :func:`map` with a :class:`RemoteFunction`, or a
87 you can just use the builtin :func:`map` with a :class:`RemoteFunction`, or a
88 DirectView's :meth:`map` method:
88 DirectView's :meth:`map` method:
89
89
90 .. sourcecode:: ipython
90 .. sourcecode:: ipython
91
91
92 In [62]: serial_result = map(lambda x:x**10, range(32))
92 In [62]: serial_result = map(lambda x:x**10, range(32))
93
93
94 In [63]: parallel_result = dview.map_sync(lambda x: x**10, range(32))
94 In [63]: parallel_result = dview.map_sync(lambda x: x**10, range(32))
95
95
96 In [67]: serial_result==parallel_result
96 In [67]: serial_result==parallel_result
97 Out[67]: True
97 Out[67]: True
98
98
99
99
100 .. note::
100 .. note::
101
101
102 The :class:`DirectView`'s version of :meth:`map` does
102 The :class:`DirectView`'s version of :meth:`map` does
103 not do dynamic load balancing. For a load balanced version, use a
103 not do dynamic load balancing. For a load balanced version, use a
104 :class:`LoadBalancedView`.
104 :class:`LoadBalancedView`.
105
105
106 .. seealso::
106 .. seealso::
107
107
108 :meth:`map` is implemented via :class:`ParallelFunction`.
108 :meth:`map` is implemented via :class:`ParallelFunction`.
109
109
110 Remote function decorators
110 Remote function decorators
111 --------------------------
111 --------------------------
112
112
113 Remote functions are just like normal functions, but when they are called,
113 Remote functions are just like normal functions, but when they are called,
114 they execute on one or more engines, rather than locally. IPython provides
114 they execute on one or more engines, rather than locally. IPython provides
115 two decorators:
115 two decorators:
116
116
117 .. sourcecode:: ipython
117 .. sourcecode:: ipython
118
118
119 In [10]: @dview.remote(block=True)
119 In [10]: @dview.remote(block=True)
120 ....: def getpid():
120 ....: def getpid():
121 ....: import os
121 ....: import os
122 ....: return os.getpid()
122 ....: return os.getpid()
123 ....:
123 ....:
124
124
125 In [11]: getpid()
125 In [11]: getpid()
126 Out[11]: [12345, 12346, 12347, 12348]
126 Out[11]: [12345, 12346, 12347, 12348]
127
127
128 The ``@parallel`` decorator creates parallel functions, that break up an element-wise
128 The ``@parallel`` decorator creates parallel functions, that break up an element-wise
129 operations and distribute them, reconstructing the result.
129 operations and distribute them, reconstructing the result.
130
130
131 .. sourcecode:: ipython
131 .. sourcecode:: ipython
132
132
133 In [12]: import numpy as np
133 In [12]: import numpy as np
134
134
135 In [13]: A = np.random.random((64,48))
135 In [13]: A = np.random.random((64,48))
136
136
137 In [14]: @dview.parallel(block=True)
137 In [14]: @dview.parallel(block=True)
138 ....: def pmul(A,B):
138 ....: def pmul(A,B):
139 ....: return A*B
139 ....: return A*B
140
140
141 In [15]: C_local = A*A
141 In [15]: C_local = A*A
142
142
143 In [16]: C_remote = pmul(A,A)
143 In [16]: C_remote = pmul(A,A)
144
144
145 In [17]: (C_local == C_remote).all()
145 In [17]: (C_local == C_remote).all()
146 Out[17]: True
146 Out[17]: True
147
147
148 Calling a ``@parallel`` function *does not* correspond to map. It is used for splitting
148 Calling a ``@parallel`` function *does not* correspond to map. It is used for splitting
149 element-wise operations that operate on a sequence or array. For ``map`` behavior,
149 element-wise operations that operate on a sequence or array. For ``map`` behavior,
150 parallel functions do have a map method.
150 parallel functions do have a map method.
151
151
152 ==================== ============================ =============================
152 ==================== ============================ =============================
153 call pfunc(seq) pfunc.map(seq)
153 call pfunc(seq) pfunc.map(seq)
154 ==================== ============================ =============================
154 ==================== ============================ =============================
155 # of tasks # of engines (1 per engine) # of engines (1 per engine)
155 # of tasks # of engines (1 per engine) # of engines (1 per engine)
156 # of remote calls # of engines (1 per engine) ``len(seq)``
156 # of remote calls # of engines (1 per engine) ``len(seq)``
157 argument to remote ``seq[i:j]`` (sub-sequence) ``seq[i]`` (single element)
157 argument to remote ``seq[i:j]`` (sub-sequence) ``seq[i]`` (single element)
158 ==================== ============================ =============================
158 ==================== ============================ =============================
159
159
160 A quick example to illustrate the difference in arguments for the two modes:
160 A quick example to illustrate the difference in arguments for the two modes:
161
161
162 .. sourcecode:: ipython
162 .. sourcecode:: ipython
163
163
164 In [16]: @dview.parallel(block=True)
164 In [16]: @dview.parallel(block=True)
165 ....: def echo(x):
165 ....: def echo(x):
166 ....: return str(x)
166 ....: return str(x)
167 ....:
167 ....:
168
168
169 In [17]: echo(range(5))
169 In [17]: echo(range(5))
170 Out[17]: ['[0, 1]', '[2]', '[3]', '[4]']
170 Out[17]: ['[0, 1]', '[2]', '[3]', '[4]']
171
171
172 In [18]: echo.map(range(5))
172 In [18]: echo.map(range(5))
173 Out[18]: ['0', '1', '2', '3', '4']
173 Out[18]: ['0', '1', '2', '3', '4']
174
174
175
175
176 .. seealso::
176 .. seealso::
177
177
178 See the :func:`~.remotefunction.parallel` and :func:`~.remotefunction.remote`
178 See the :func:`~.remotefunction.parallel` and :func:`~.remotefunction.remote`
179 decorators for options.
179 decorators for options.
180
180
181 Calling Python functions
181 Calling Python functions
182 ========================
182 ========================
183
183
184 The most basic type of operation that can be performed on the engines is to
184 The most basic type of operation that can be performed on the engines is to
185 execute Python code or call Python functions. Executing Python code can be
185 execute Python code or call Python functions. Executing Python code can be
186 done in blocking or non-blocking mode (non-blocking is default) using the
186 done in blocking or non-blocking mode (non-blocking is default) using the
187 :meth:`.View.execute` method, and calling functions can be done via the
187 :meth:`.View.execute` method, and calling functions can be done via the
188 :meth:`.View.apply` method.
188 :meth:`.View.apply` method.
189
189
190 apply
190 apply
191 -----
191 -----
192
192
193 The main method for doing remote execution (in fact, all methods that
193 The main method for doing remote execution (in fact, all methods that
194 communicate with the engines are built on top of it), is :meth:`View.apply`.
194 communicate with the engines are built on top of it), is :meth:`View.apply`.
195
195
196 We strive to provide the cleanest interface we can, so `apply` has the following
196 We strive to provide the cleanest interface we can, so `apply` has the following
197 signature:
197 signature:
198
198
199 .. sourcecode:: python
199 .. sourcecode:: python
200
200
201 view.apply(f, *args, **kwargs)
201 view.apply(f, *args, **kwargs)
202
202
203 There are various ways to call functions with IPython, and these flags are set as
203 There are various ways to call functions with IPython, and these flags are set as
204 attributes of the View. The ``DirectView`` has just two of these flags:
204 attributes of the View. The ``DirectView`` has just two of these flags:
205
205
206 dv.block : bool
206 dv.block : bool
207 whether to wait for the result, or return an :class:`AsyncResult` object
207 whether to wait for the result, or return an :class:`AsyncResult` object
208 immediately
208 immediately
209 dv.track : bool
209 dv.track : bool
210 whether to instruct pyzmq to track when zeromq is done sending the message.
210 whether to instruct pyzmq to track when zeromq is done sending the message.
211 This is primarily useful for non-copying sends of numpy arrays that you plan to
211 This is primarily useful for non-copying sends of numpy arrays that you plan to
212 edit in-place. You need to know when it becomes safe to edit the buffer
212 edit in-place. You need to know when it becomes safe to edit the buffer
213 without corrupting the message.
213 without corrupting the message.
214 dv.targets : int, list of ints
214 dv.targets : int, list of ints
215 which targets this view is associated with.
215 which targets this view is associated with.
216
216
217
217
218 Creating a view is simple: index-access on a client creates a :class:`.DirectView`.
218 Creating a view is simple: index-access on a client creates a :class:`.DirectView`.
219
219
220 .. sourcecode:: ipython
220 .. sourcecode:: ipython
221
221
222 In [4]: view = rc[1:3]
222 In [4]: view = rc[1:3]
223 Out[4]: <DirectView [1, 2]>
223 Out[4]: <DirectView [1, 2]>
224
224
225 In [5]: view.apply<tab>
225 In [5]: view.apply<tab>
226 view.apply view.apply_async view.apply_sync
226 view.apply view.apply_async view.apply_sync
227
227
228 For convenience, you can set block temporarily for a single call with the extra sync/async methods.
228 For convenience, you can set block temporarily for a single call with the extra sync/async methods.
229
229
230 Blocking execution
230 Blocking execution
231 ------------------
231 ------------------
232
232
233 In blocking mode, the :class:`.DirectView` object (called ``dview`` in
233 In blocking mode, the :class:`.DirectView` object (called ``dview`` in
234 these examples) submits the command to the controller, which places the
234 these examples) submits the command to the controller, which places the
235 command in the engines' queues for execution. The :meth:`apply` call then
235 command in the engines' queues for execution. The :meth:`apply` call then
236 blocks until the engines are done executing the command:
236 blocks until the engines are done executing the command:
237
237
238 .. sourcecode:: ipython
238 .. sourcecode:: ipython
239
239
240 In [2]: dview = rc[:] # A DirectView of all engines
240 In [2]: dview = rc[:] # A DirectView of all engines
241 In [3]: dview.block=True
241 In [3]: dview.block=True
242 In [4]: dview['a'] = 5
242 In [4]: dview['a'] = 5
243
243
244 In [5]: dview['b'] = 10
244 In [5]: dview['b'] = 10
245
245
246 In [6]: dview.apply(lambda x: a+b+x, 27)
246 In [6]: dview.apply(lambda x: a+b+x, 27)
247 Out[6]: [42, 42, 42, 42]
247 Out[6]: [42, 42, 42, 42]
248
248
249 You can also select blocking execution on a call-by-call basis with the :meth:`apply_sync`
249 You can also select blocking execution on a call-by-call basis with the :meth:`apply_sync`
250 method:
250 method:
251
251
252 In [7]: dview.block=False
252 In [7]: dview.block=False
253
253
254 In [8]: dview.apply_sync(lambda x: a+b+x, 27)
254 In [8]: dview.apply_sync(lambda x: a+b+x, 27)
255 Out[8]: [42, 42, 42, 42]
255 Out[8]: [42, 42, 42, 42]
256
256
257 Python commands can be executed as strings on specific engines by using a View's ``execute``
257 Python commands can be executed as strings on specific engines by using a View's ``execute``
258 method:
258 method:
259
259
260 .. sourcecode:: ipython
260 .. sourcecode:: ipython
261
261
262 In [6]: rc[::2].execute('c=a+b')
262 In [6]: rc[::2].execute('c=a+b')
263
263
264 In [7]: rc[1::2].execute('c=a-b')
264 In [7]: rc[1::2].execute('c=a-b')
265
265
266 In [8]: dview['c'] # shorthand for dview.pull('c', block=True)
266 In [8]: dview['c'] # shorthand for dview.pull('c', block=True)
267 Out[8]: [15, -5, 15, -5]
267 Out[8]: [15, -5, 15, -5]
268
268
269
269
270 Non-blocking execution
270 Non-blocking execution
271 ----------------------
271 ----------------------
272
272
273 In non-blocking mode, :meth:`apply` submits the command to be executed and
273 In non-blocking mode, :meth:`apply` submits the command to be executed and
274 then returns a :class:`AsyncResult` object immediately. The
274 then returns a :class:`AsyncResult` object immediately. The
275 :class:`AsyncResult` object gives you a way of getting a result at a later
275 :class:`AsyncResult` object gives you a way of getting a result at a later
276 time through its :meth:`get` method.
276 time through its :meth:`get` method.
277
277
278 .. Note::
278 .. seealso::
279
280 The :class:`AsyncResult` object provides a superset of the interface in
281 :py:class:`multiprocessing.pool.AsyncResult`. See the
282 `official Python documentation <http://docs.python.org/library/multiprocessing#multiprocessing.pool.AsyncResult>`_
283 for more.
284
279
280 Docs on the :ref:`AsyncResult <parallel_asyncresult>` object.
285
281
286 This allows you to quickly submit long running commands without blocking your
282 This allows you to quickly submit long running commands without blocking your
287 local Python/IPython session:
283 local Python/IPython session:
288
284
289 .. sourcecode:: ipython
285 .. sourcecode:: ipython
290
286
291 # define our function
287 # define our function
292 In [6]: def wait(t):
288 In [6]: def wait(t):
293 ....: import time
289 ....: import time
294 ....: tic = time.time()
290 ....: tic = time.time()
295 ....: time.sleep(t)
291 ....: time.sleep(t)
296 ....: return time.time()-tic
292 ....: return time.time()-tic
297
293
298 # In non-blocking mode
294 # In non-blocking mode
299 In [7]: ar = dview.apply_async(wait, 2)
295 In [7]: ar = dview.apply_async(wait, 2)
300
296
301 # Now block for the result
297 # Now block for the result
302 In [8]: ar.get()
298 In [8]: ar.get()
303 Out[8]: [2.0006198883056641, 1.9997570514678955, 1.9996809959411621, 2.0003249645233154]
299 Out[8]: [2.0006198883056641, 1.9997570514678955, 1.9996809959411621, 2.0003249645233154]
304
300
305 # Again in non-blocking mode
301 # Again in non-blocking mode
306 In [9]: ar = dview.apply_async(wait, 10)
302 In [9]: ar = dview.apply_async(wait, 10)
307
303
308 # Poll to see if the result is ready
304 # Poll to see if the result is ready
309 In [10]: ar.ready()
305 In [10]: ar.ready()
310 Out[10]: False
306 Out[10]: False
311
307
312 # ask for the result, but wait a maximum of 1 second:
308 # ask for the result, but wait a maximum of 1 second:
313 In [45]: ar.get(1)
309 In [45]: ar.get(1)
314 ---------------------------------------------------------------------------
310 ---------------------------------------------------------------------------
315 TimeoutError Traceback (most recent call last)
311 TimeoutError Traceback (most recent call last)
316 /home/you/<ipython-input-45-7cd858bbb8e0> in <module>()
312 /home/you/<ipython-input-45-7cd858bbb8e0> in <module>()
317 ----> 1 ar.get(1)
313 ----> 1 ar.get(1)
318
314
319 /path/to/site-packages/IPython/parallel/asyncresult.pyc in get(self, timeout)
315 /path/to/site-packages/IPython/parallel/asyncresult.pyc in get(self, timeout)
320 62 raise self._exception
316 62 raise self._exception
321 63 else:
317 63 else:
322 ---> 64 raise error.TimeoutError("Result not ready.")
318 ---> 64 raise error.TimeoutError("Result not ready.")
323 65
319 65
324 66 def ready(self):
320 66 def ready(self):
325
321
326 TimeoutError: Result not ready.
322 TimeoutError: Result not ready.
327
323
328 .. Note::
324 .. Note::
329
325
330 Note the import inside the function. This is a common model, to ensure
326 Note the import inside the function. This is a common model, to ensure
331 that the appropriate modules are imported where the task is run. You can
327 that the appropriate modules are imported where the task is run. You can
332 also manually import modules into the engine(s) namespace(s) via
328 also manually import modules into the engine(s) namespace(s) via
333 :meth:`view.execute('import numpy')`.
329 :meth:`view.execute('import numpy')`.
334
330
335 Often, it is desirable to wait until a set of :class:`AsyncResult` objects
331 Often, it is desirable to wait until a set of :class:`AsyncResult` objects
336 are done. For this, there is a the method :meth:`wait`. This method takes a
332 are done. For this, there is a the method :meth:`wait`. This method takes a
337 tuple of :class:`AsyncResult` objects (or `msg_ids` or indices to the client's History),
333 tuple of :class:`AsyncResult` objects (or `msg_ids` or indices to the client's History),
338 and blocks until all of the associated results are ready:
334 and blocks until all of the associated results are ready:
339
335
340 .. sourcecode:: ipython
336 .. sourcecode:: ipython
341
337
342 In [72]: dview.block=False
338 In [72]: dview.block=False
343
339
344 # A trivial list of AsyncResults objects
340 # A trivial list of AsyncResults objects
345 In [73]: pr_list = [dview.apply_async(wait, 3) for i in range(10)]
341 In [73]: pr_list = [dview.apply_async(wait, 3) for i in range(10)]
346
342
347 # Wait until all of them are done
343 # Wait until all of them are done
348 In [74]: dview.wait(pr_list)
344 In [74]: dview.wait(pr_list)
349
345
350 # Then, their results are ready using get() or the `.r` attribute
346 # Then, their results are ready using get() or the `.r` attribute
351 In [75]: pr_list[0].get()
347 In [75]: pr_list[0].get()
352 Out[75]: [2.9982571601867676, 2.9982588291168213, 2.9987530708312988, 2.9990990161895752]
348 Out[75]: [2.9982571601867676, 2.9982588291168213, 2.9987530708312988, 2.9990990161895752]
353
349
354
350
355
351
356 The ``block`` and ``targets`` keyword arguments and attributes
352 The ``block`` and ``targets`` keyword arguments and attributes
357 --------------------------------------------------------------
353 --------------------------------------------------------------
358
354
359 Most DirectView methods (excluding :meth:`apply`) accept ``block`` and
355 Most DirectView methods (excluding :meth:`apply`) accept ``block`` and
360 ``targets`` as keyword arguments. As we have seen above, these keyword arguments control the
356 ``targets`` as keyword arguments. As we have seen above, these keyword arguments control the
361 blocking mode and which engines the command is applied to. The :class:`View` class also has
357 blocking mode and which engines the command is applied to. The :class:`View` class also has
362 :attr:`block` and :attr:`targets` attributes that control the default behavior when the keyword
358 :attr:`block` and :attr:`targets` attributes that control the default behavior when the keyword
363 arguments are not provided. Thus the following logic is used for :attr:`block` and :attr:`targets`:
359 arguments are not provided. Thus the following logic is used for :attr:`block` and :attr:`targets`:
364
360
365 * If no keyword argument is provided, the instance attributes are used.
361 * If no keyword argument is provided, the instance attributes are used.
366 * Keyword argument, if provided override the instance attributes for
362 * Keyword argument, if provided override the instance attributes for
367 the duration of a single call.
363 the duration of a single call.
368
364
369 The following examples demonstrate how to use the instance attributes:
365 The following examples demonstrate how to use the instance attributes:
370
366
371 .. sourcecode:: ipython
367 .. sourcecode:: ipython
372
368
373 In [16]: dview.targets = [0,2]
369 In [16]: dview.targets = [0,2]
374
370
375 In [17]: dview.block = False
371 In [17]: dview.block = False
376
372
377 In [18]: ar = dview.apply(lambda : 10)
373 In [18]: ar = dview.apply(lambda : 10)
378
374
379 In [19]: ar.get()
375 In [19]: ar.get()
380 Out[19]: [10, 10]
376 Out[19]: [10, 10]
381
377
382 In [16]: dview.targets = v.client.ids # all engines (4)
378 In [16]: dview.targets = v.client.ids # all engines (4)
383
379
384 In [21]: dview.block = True
380 In [21]: dview.block = True
385
381
386 In [22]: dview.apply(lambda : 42)
382 In [22]: dview.apply(lambda : 42)
387 Out[22]: [42, 42, 42, 42]
383 Out[22]: [42, 42, 42, 42]
388
384
389 The :attr:`block` and :attr:`targets` instance attributes of the
385 The :attr:`block` and :attr:`targets` instance attributes of the
390 :class:`.DirectView` also determine the behavior of the parallel magic commands.
386 :class:`.DirectView` also determine the behavior of the parallel magic commands.
391
387
392 Parallel magic commands
388 Parallel magic commands
393 -----------------------
389 -----------------------
394
390
395 We provide a few IPython magic commands (``%px``, ``%autopx`` and ``%result``)
391 We provide a few IPython magic commands (``%px``, ``%autopx`` and ``%result``)
396 that make it more pleasant to execute Python commands on the engines
392 that make it more pleasant to execute Python commands on the engines
397 interactively. These are simply shortcuts to :meth:`execute` and
393 interactively. These are simply shortcuts to :meth:`execute` and
398 :meth:`get_result` of the :class:`DirectView`. The ``%px`` magic executes a single
394 :meth:`get_result` of the :class:`DirectView`. The ``%px`` magic executes a single
399 Python command on the engines specified by the :attr:`targets` attribute of the
395 Python command on the engines specified by the :attr:`targets` attribute of the
400 :class:`DirectView` instance:
396 :class:`DirectView` instance:
401
397
402 .. sourcecode:: ipython
398 .. sourcecode:: ipython
403
399
404 # Create a DirectView for all targets
400 # Create a DirectView for all targets
405 In [22]: dv = rc[:]
401 In [22]: dv = rc[:]
406
402
407 # Make this DirectView active for parallel magic commands
403 # Make this DirectView active for parallel magic commands
408 In [23]: dv.activate()
404 In [23]: dv.activate()
409
405
410 In [24]: dv.block=True
406 In [24]: dv.block=True
411
407
412 # import numpy here and everywhere
408 # import numpy here and everywhere
413 In [25]: with dv.sync_imports():
409 In [25]: with dv.sync_imports():
414 ....: import numpy
410 ....: import numpy
415 importing numpy on engine(s)
411 importing numpy on engine(s)
416
412
417 In [27]: %px a = numpy.random.rand(2,2)
413 In [27]: %px a = numpy.random.rand(2,2)
418 Parallel execution on engines: [0, 1, 2, 3]
414 Parallel execution on engines: [0, 1, 2, 3]
419
415
420 In [28]: %px ev = numpy.linalg.eigvals(a)
416 In [28]: %px ev = numpy.linalg.eigvals(a)
421 Parallel execution on engines: [0, 1, 2, 3]
417 Parallel execution on engines: [0, 1, 2, 3]
422
418
423 In [28]: dv['ev']
419 In [28]: dv['ev']
424 Out[28]: [ array([ 1.09522024, -0.09645227]),
420 Out[28]: [ array([ 1.09522024, -0.09645227]),
425 ....: array([ 1.21435496, -0.35546712]),
421 ....: array([ 1.21435496, -0.35546712]),
426 ....: array([ 0.72180653, 0.07133042]),
422 ....: array([ 0.72180653, 0.07133042]),
427 ....: array([ 1.46384341, 1.04353244e-04])
423 ....: array([ 1.46384341, 1.04353244e-04])
428 ....: ]
424 ....: ]
429
425
430 The ``%result`` magic gets the most recent result, or takes an argument
426 The ``%result`` magic gets the most recent result, or takes an argument
431 specifying the index of the result to be requested. It is simply a shortcut to the
427 specifying the index of the result to be requested. It is simply a shortcut to the
432 :meth:`get_result` method:
428 :meth:`get_result` method:
433
429
434 .. sourcecode:: ipython
430 .. sourcecode:: ipython
435
431
436 In [29]: dv.apply_async(lambda : ev)
432 In [29]: dv.apply_async(lambda : ev)
437
433
438 In [30]: %result
434 In [30]: %result
439 Out[30]: [ [ 1.28167017 0.14197338],
435 Out[30]: [ [ 1.28167017 0.14197338],
440 ....: [-0.14093616 1.27877273],
436 ....: [-0.14093616 1.27877273],
441 ....: [-0.37023573 1.06779409],
437 ....: [-0.37023573 1.06779409],
442 ....: [ 0.83664764 -0.25602658] ]
438 ....: [ 0.83664764 -0.25602658] ]
443
439
444 The ``%autopx`` magic switches to a mode where everything you type is executed
440 The ``%autopx`` magic switches to a mode where everything you type is executed
445 on the engines given by the :attr:`targets` attribute:
441 on the engines given by the :attr:`targets` attribute:
446
442
447 .. sourcecode:: ipython
443 .. sourcecode:: ipython
448
444
449 In [30]: dv.block=False
445 In [30]: dv.block=False
450
446
451 In [31]: %autopx
447 In [31]: %autopx
452 Auto Parallel Enabled
448 Auto Parallel Enabled
453 Type %autopx to disable
449 Type %autopx to disable
454
450
455 In [32]: max_evals = []
451 In [32]: max_evals = []
456 <IPython.parallel.AsyncResult object at 0x17b8a70>
452 <IPython.parallel.AsyncResult object at 0x17b8a70>
457
453
458 In [33]: for i in range(100):
454 In [33]: for i in range(100):
459 ....: a = numpy.random.rand(10,10)
455 ....: a = numpy.random.rand(10,10)
460 ....: a = a+a.transpose()
456 ....: a = a+a.transpose()
461 ....: evals = numpy.linalg.eigvals(a)
457 ....: evals = numpy.linalg.eigvals(a)
462 ....: max_evals.append(evals[0].real)
458 ....: max_evals.append(evals[0].real)
463 ....:
459 ....:
464 ....:
460 ....:
465 <IPython.parallel.AsyncResult object at 0x17af8f0>
461 <IPython.parallel.AsyncResult object at 0x17af8f0>
466
462
467 In [34]: %autopx
463 In [34]: %autopx
468 Auto Parallel Disabled
464 Auto Parallel Disabled
469
465
470 In [35]: dv.block=True
466 In [35]: dv.block=True
471
467
472 In [36]: px ans= "Average max eigenvalue is: %f"%(sum(max_evals)/len(max_evals))
468 In [36]: px ans= "Average max eigenvalue is: %f"%(sum(max_evals)/len(max_evals))
473 Parallel execution on engines: [0, 1, 2, 3]
469 Parallel execution on engines: [0, 1, 2, 3]
474
470
475 In [37]: dv['ans']
471 In [37]: dv['ans']
476 Out[37]: [ 'Average max eigenvalue is: 10.1387247332',
472 Out[37]: [ 'Average max eigenvalue is: 10.1387247332',
477 ....: 'Average max eigenvalue is: 10.2076902286',
473 ....: 'Average max eigenvalue is: 10.2076902286',
478 ....: 'Average max eigenvalue is: 10.1891484655',
474 ....: 'Average max eigenvalue is: 10.1891484655',
479 ....: 'Average max eigenvalue is: 10.1158837784',]
475 ....: 'Average max eigenvalue is: 10.1158837784',]
480
476
481
477
482 Moving Python objects around
478 Moving Python objects around
483 ============================
479 ============================
484
480
485 In addition to calling functions and executing code on engines, you can
481 In addition to calling functions and executing code on engines, you can
486 transfer Python objects to and from your IPython session and the engines. In
482 transfer Python objects to and from your IPython session and the engines. In
487 IPython, these operations are called :meth:`push` (sending an object to the
483 IPython, these operations are called :meth:`push` (sending an object to the
488 engines) and :meth:`pull` (getting an object from the engines).
484 engines) and :meth:`pull` (getting an object from the engines).
489
485
490 Basic push and pull
486 Basic push and pull
491 -------------------
487 -------------------
492
488
493 Here are some examples of how you use :meth:`push` and :meth:`pull`:
489 Here are some examples of how you use :meth:`push` and :meth:`pull`:
494
490
495 .. sourcecode:: ipython
491 .. sourcecode:: ipython
496
492
497 In [38]: dview.push(dict(a=1.03234,b=3453))
493 In [38]: dview.push(dict(a=1.03234,b=3453))
498 Out[38]: [None,None,None,None]
494 Out[38]: [None,None,None,None]
499
495
500 In [39]: dview.pull('a')
496 In [39]: dview.pull('a')
501 Out[39]: [ 1.03234, 1.03234, 1.03234, 1.03234]
497 Out[39]: [ 1.03234, 1.03234, 1.03234, 1.03234]
502
498
503 In [40]: dview.pull('b', targets=0)
499 In [40]: dview.pull('b', targets=0)
504 Out[40]: 3453
500 Out[40]: 3453
505
501
506 In [41]: dview.pull(('a','b'))
502 In [41]: dview.pull(('a','b'))
507 Out[41]: [ [1.03234, 3453], [1.03234, 3453], [1.03234, 3453], [1.03234, 3453] ]
503 Out[41]: [ [1.03234, 3453], [1.03234, 3453], [1.03234, 3453], [1.03234, 3453] ]
508
504
509 In [43]: dview.push(dict(c='speed'))
505 In [43]: dview.push(dict(c='speed'))
510 Out[43]: [None,None,None,None]
506 Out[43]: [None,None,None,None]
511
507
512 In non-blocking mode :meth:`push` and :meth:`pull` also return
508 In non-blocking mode :meth:`push` and :meth:`pull` also return
513 :class:`AsyncResult` objects:
509 :class:`AsyncResult` objects:
514
510
515 .. sourcecode:: ipython
511 .. sourcecode:: ipython
516
512
517 In [48]: ar = dview.pull('a', block=False)
513 In [48]: ar = dview.pull('a', block=False)
518
514
519 In [49]: ar.get()
515 In [49]: ar.get()
520 Out[49]: [1.03234, 1.03234, 1.03234, 1.03234]
516 Out[49]: [1.03234, 1.03234, 1.03234, 1.03234]
521
517
522
518
523 Dictionary interface
519 Dictionary interface
524 --------------------
520 --------------------
525
521
526 Since a Python namespace is just a :class:`dict`, :class:`DirectView` objects provide
522 Since a Python namespace is just a :class:`dict`, :class:`DirectView` objects provide
527 dictionary-style access by key and methods such as :meth:`get` and
523 dictionary-style access by key and methods such as :meth:`get` and
528 :meth:`update` for convenience. This make the remote namespaces of the engines
524 :meth:`update` for convenience. This make the remote namespaces of the engines
529 appear as a local dictionary. Underneath, these methods call :meth:`apply`:
525 appear as a local dictionary. Underneath, these methods call :meth:`apply`:
530
526
531 .. sourcecode:: ipython
527 .. sourcecode:: ipython
532
528
533 In [51]: dview['a']=['foo','bar']
529 In [51]: dview['a']=['foo','bar']
534
530
535 In [52]: dview['a']
531 In [52]: dview['a']
536 Out[52]: [ ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'] ]
532 Out[52]: [ ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'] ]
537
533
538 Scatter and gather
534 Scatter and gather
539 ------------------
535 ------------------
540
536
541 Sometimes it is useful to partition a sequence and push the partitions to
537 Sometimes it is useful to partition a sequence and push the partitions to
542 different engines. In MPI language, this is know as scatter/gather and we
538 different engines. In MPI language, this is know as scatter/gather and we
543 follow that terminology. However, it is important to remember that in
539 follow that terminology. However, it is important to remember that in
544 IPython's :class:`Client` class, :meth:`scatter` is from the
540 IPython's :class:`Client` class, :meth:`scatter` is from the
545 interactive IPython session to the engines and :meth:`gather` is from the
541 interactive IPython session to the engines and :meth:`gather` is from the
546 engines back to the interactive IPython session. For scatter/gather operations
542 engines back to the interactive IPython session. For scatter/gather operations
547 between engines, MPI, pyzmq, or some other direct interconnect should be used.
543 between engines, MPI, pyzmq, or some other direct interconnect should be used.
548
544
549 .. sourcecode:: ipython
545 .. sourcecode:: ipython
550
546
551 In [58]: dview.scatter('a',range(16))
547 In [58]: dview.scatter('a',range(16))
552 Out[58]: [None,None,None,None]
548 Out[58]: [None,None,None,None]
553
549
554 In [59]: dview['a']
550 In [59]: dview['a']
555 Out[59]: [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15] ]
551 Out[59]: [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15] ]
556
552
557 In [60]: dview.gather('a')
553 In [60]: dview.gather('a')
558 Out[60]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
554 Out[60]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
559
555
560 Other things to look at
556 Other things to look at
561 =======================
557 =======================
562
558
563 How to do parallel list comprehensions
559 How to do parallel list comprehensions
564 --------------------------------------
560 --------------------------------------
565
561
566 In many cases list comprehensions are nicer than using the map function. While
562 In many cases list comprehensions are nicer than using the map function. While
567 we don't have fully parallel list comprehensions, it is simple to get the
563 we don't have fully parallel list comprehensions, it is simple to get the
568 basic effect using :meth:`scatter` and :meth:`gather`:
564 basic effect using :meth:`scatter` and :meth:`gather`:
569
565
570 .. sourcecode:: ipython
566 .. sourcecode:: ipython
571
567
572 In [66]: dview.scatter('x',range(64))
568 In [66]: dview.scatter('x',range(64))
573
569
574 In [67]: %px y = [i**10 for i in x]
570 In [67]: %px y = [i**10 for i in x]
575 Parallel execution on engines: [0, 1, 2, 3]
571 Parallel execution on engines: [0, 1, 2, 3]
576 Out[67]:
572 Out[67]:
577
573
578 In [68]: y = dview.gather('y')
574 In [68]: y = dview.gather('y')
579
575
580 In [69]: print y
576 In [69]: print y
581 [0, 1, 1024, 59049, 1048576, 9765625, 60466176, 282475249, 1073741824,...]
577 [0, 1, 1024, 59049, 1048576, 9765625, 60466176, 282475249, 1073741824,...]
582
578
583 Remote imports
579 Remote imports
584 --------------
580 --------------
585
581
586 Sometimes you will want to import packages both in your interactive session
582 Sometimes you will want to import packages both in your interactive session
587 and on your remote engines. This can be done with the :class:`ContextManager`
583 and on your remote engines. This can be done with the :class:`ContextManager`
588 created by a DirectView's :meth:`sync_imports` method:
584 created by a DirectView's :meth:`sync_imports` method:
589
585
590 .. sourcecode:: ipython
586 .. sourcecode:: ipython
591
587
592 In [69]: with dview.sync_imports():
588 In [69]: with dview.sync_imports():
593 ....: import numpy
589 ....: import numpy
594 importing numpy on engine(s)
590 importing numpy on engine(s)
595
591
596 Any imports made inside the block will also be performed on the view's engines.
592 Any imports made inside the block will also be performed on the view's engines.
597 sync_imports also takes a `local` boolean flag that defaults to True, which specifies
593 sync_imports also takes a `local` boolean flag that defaults to True, which specifies
598 whether the local imports should also be performed. However, support for `local=False`
594 whether the local imports should also be performed. However, support for `local=False`
599 has not been implemented, so only packages that can be imported locally will work
595 has not been implemented, so only packages that can be imported locally will work
600 this way.
596 this way.
601
597
602 You can also specify imports via the ``@require`` decorator. This is a decorator
598 You can also specify imports via the ``@require`` decorator. This is a decorator
603 designed for use in Dependencies, but can be used to handle remote imports as well.
599 designed for use in Dependencies, but can be used to handle remote imports as well.
604 Modules or module names passed to ``@require`` will be imported before the decorated
600 Modules or module names passed to ``@require`` will be imported before the decorated
605 function is called. If they cannot be imported, the decorated function will never
601 function is called. If they cannot be imported, the decorated function will never
606 execution, and will fail with an UnmetDependencyError.
602 execution, and will fail with an UnmetDependencyError.
607
603
608 .. sourcecode:: ipython
604 .. sourcecode:: ipython
609
605
610 In [69]: from IPython.parallel import require
606 In [69]: from IPython.parallel import require
611
607
612 In [70]: @require('re'):
608 In [70]: @require('re'):
613 ....: def findall(pat, x):
609 ....: def findall(pat, x):
614 ....: # re is guaranteed to be available
610 ....: # re is guaranteed to be available
615 ....: return re.findall(pat, x)
611 ....: return re.findall(pat, x)
616
612
617 # you can also pass modules themselves, that you already have locally:
613 # you can also pass modules themselves, that you already have locally:
618 In [71]: @require(time):
614 In [71]: @require(time):
619 ....: def wait(t):
615 ....: def wait(t):
620 ....: time.sleep(t)
616 ....: time.sleep(t)
621 ....: return t
617 ....: return t
622
618
623 .. _parallel_exceptions:
619 .. _parallel_exceptions:
624
620
625 Parallel exceptions
621 Parallel exceptions
626 -------------------
622 -------------------
627
623
628 In the multiengine interface, parallel commands can raise Python exceptions,
624 In the multiengine interface, parallel commands can raise Python exceptions,
629 just like serial commands. But, it is a little subtle, because a single
625 just like serial commands. But, it is a little subtle, because a single
630 parallel command can actually raise multiple exceptions (one for each engine
626 parallel command can actually raise multiple exceptions (one for each engine
631 the command was run on). To express this idea, we have a
627 the command was run on). To express this idea, we have a
632 :exc:`CompositeError` exception class that will be raised in most cases. The
628 :exc:`CompositeError` exception class that will be raised in most cases. The
633 :exc:`CompositeError` class is a special type of exception that wraps one or
629 :exc:`CompositeError` class is a special type of exception that wraps one or
634 more other types of exceptions. Here is how it works:
630 more other types of exceptions. Here is how it works:
635
631
636 .. sourcecode:: ipython
632 .. sourcecode:: ipython
637
633
638 In [76]: dview.block=True
634 In [76]: dview.block=True
639
635
640 In [77]: dview.execute('1/0')
636 In [77]: dview.execute('1/0')
641 ---------------------------------------------------------------------------
637 ---------------------------------------------------------------------------
642 CompositeError Traceback (most recent call last)
638 CompositeError Traceback (most recent call last)
643 /home/user/<ipython-input-10-5d56b303a66c> in <module>()
639 /home/user/<ipython-input-10-5d56b303a66c> in <module>()
644 ----> 1 dview.execute('1/0')
640 ----> 1 dview.execute('1/0')
645
641
646 /path/to/site-packages/IPython/parallel/client/view.pyc in execute(self, code, targets, block)
642 /path/to/site-packages/IPython/parallel/client/view.pyc in execute(self, code, targets, block)
647 591 default: self.block
643 591 default: self.block
648 592 """
644 592 """
649 --> 593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
645 --> 593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
650 594
646 594
651 595 def run(self, filename, targets=None, block=None):
647 595 def run(self, filename, targets=None, block=None):
652
648
653 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
649 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
654
650
655 /path/to/site-packages/IPython/parallel/client/view.pyc in sync_results(f, self, *args, **kwargs)
651 /path/to/site-packages/IPython/parallel/client/view.pyc in sync_results(f, self, *args, **kwargs)
656 55 def sync_results(f, self, *args, **kwargs):
652 55 def sync_results(f, self, *args, **kwargs):
657 56 """sync relevant results from self.client to our results attribute."""
653 56 """sync relevant results from self.client to our results attribute."""
658 ---> 57 ret = f(self, *args, **kwargs)
654 ---> 57 ret = f(self, *args, **kwargs)
659 58 delta = self.outstanding.difference(self.client.outstanding)
655 58 delta = self.outstanding.difference(self.client.outstanding)
660 59 completed = self.outstanding.intersection(delta)
656 59 completed = self.outstanding.intersection(delta)
661
657
662 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
658 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
663
659
664 /path/to/site-packages/IPython/parallel/client/view.pyc in save_ids(f, self, *args, **kwargs)
660 /path/to/site-packages/IPython/parallel/client/view.pyc in save_ids(f, self, *args, **kwargs)
665 44 n_previous = len(self.client.history)
661 44 n_previous = len(self.client.history)
666 45 try:
662 45 try:
667 ---> 46 ret = f(self, *args, **kwargs)
663 ---> 46 ret = f(self, *args, **kwargs)
668 47 finally:
664 47 finally:
669 48 nmsgs = len(self.client.history) - n_previous
665 48 nmsgs = len(self.client.history) - n_previous
670
666
671 /path/to/site-packages/IPython/parallel/client/view.pyc in _really_apply(self, f, args, kwargs, targets, block, track)
667 /path/to/site-packages/IPython/parallel/client/view.pyc in _really_apply(self, f, args, kwargs, targets, block, track)
672 529 if block:
668 529 if block:
673 530 try:
669 530 try:
674 --> 531 return ar.get()
670 --> 531 return ar.get()
675 532 except KeyboardInterrupt:
671 532 except KeyboardInterrupt:
676 533 pass
672 533 pass
677
673
678 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
674 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
679 101 return self._result
675 101 return self._result
680 102 else:
676 102 else:
681 --> 103 raise self._exception
677 --> 103 raise self._exception
682 104 else:
678 104 else:
683 105 raise error.TimeoutError("Result not ready.")
679 105 raise error.TimeoutError("Result not ready.")
684
680
685 CompositeError: one or more exceptions from call to method: _execute
681 CompositeError: one or more exceptions from call to method: _execute
686 [0:apply]: ZeroDivisionError: integer division or modulo by zero
682 [0:apply]: ZeroDivisionError: integer division or modulo by zero
687 [1:apply]: ZeroDivisionError: integer division or modulo by zero
683 [1:apply]: ZeroDivisionError: integer division or modulo by zero
688 [2:apply]: ZeroDivisionError: integer division or modulo by zero
684 [2:apply]: ZeroDivisionError: integer division or modulo by zero
689 [3:apply]: ZeroDivisionError: integer division or modulo by zero
685 [3:apply]: ZeroDivisionError: integer division or modulo by zero
690
686
691 Notice how the error message printed when :exc:`CompositeError` is raised has
687 Notice how the error message printed when :exc:`CompositeError` is raised has
692 information about the individual exceptions that were raised on each engine.
688 information about the individual exceptions that were raised on each engine.
693 If you want, you can even raise one of these original exceptions:
689 If you want, you can even raise one of these original exceptions:
694
690
695 .. sourcecode:: ipython
691 .. sourcecode:: ipython
696
692
697 In [80]: try:
693 In [80]: try:
698 ....: dview.execute('1/0')
694 ....: dview.execute('1/0')
699 ....: except parallel.error.CompositeError, e:
695 ....: except parallel.error.CompositeError, e:
700 ....: e.raise_exception()
696 ....: e.raise_exception()
701 ....:
697 ....:
702 ....:
698 ....:
703 ---------------------------------------------------------------------------
699 ---------------------------------------------------------------------------
704 RemoteError Traceback (most recent call last)
700 RemoteError Traceback (most recent call last)
705 /home/user/<ipython-input-17-8597e7e39858> in <module>()
701 /home/user/<ipython-input-17-8597e7e39858> in <module>()
706 2 dview.execute('1/0')
702 2 dview.execute('1/0')
707 3 except CompositeError as e:
703 3 except CompositeError as e:
708 ----> 4 e.raise_exception()
704 ----> 4 e.raise_exception()
709
705
710 /path/to/site-packages/IPython/parallel/error.pyc in raise_exception(self, excid)
706 /path/to/site-packages/IPython/parallel/error.pyc in raise_exception(self, excid)
711 266 raise IndexError("an exception with index %i does not exist"%excid)
707 266 raise IndexError("an exception with index %i does not exist"%excid)
712 267 else:
708 267 else:
713 --> 268 raise RemoteError(en, ev, etb, ei)
709 --> 268 raise RemoteError(en, ev, etb, ei)
714 269
710 269
715 270
711 270
716
712
717 RemoteError: ZeroDivisionError(integer division or modulo by zero)
713 RemoteError: ZeroDivisionError(integer division or modulo by zero)
718 Traceback (most recent call last):
714 Traceback (most recent call last):
719 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
715 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
720 exec code in working,working
716 exec code in working,working
721 File "<string>", line 1, in <module>
717 File "<string>", line 1, in <module>
722 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
718 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
723 exec code in globals()
719 exec code in globals()
724 File "<string>", line 1, in <module>
720 File "<string>", line 1, in <module>
725 ZeroDivisionError: integer division or modulo by zero
721 ZeroDivisionError: integer division or modulo by zero
726
722
727 If you are working in IPython, you can simple type ``%debug`` after one of
723 If you are working in IPython, you can simple type ``%debug`` after one of
728 these :exc:`CompositeError` exceptions is raised, and inspect the exception
724 these :exc:`CompositeError` exceptions is raised, and inspect the exception
729 instance:
725 instance:
730
726
731 .. sourcecode:: ipython
727 .. sourcecode:: ipython
732
728
733 In [81]: dview.execute('1/0')
729 In [81]: dview.execute('1/0')
734 ---------------------------------------------------------------------------
730 ---------------------------------------------------------------------------
735 CompositeError Traceback (most recent call last)
731 CompositeError Traceback (most recent call last)
736 /home/user/<ipython-input-10-5d56b303a66c> in <module>()
732 /home/user/<ipython-input-10-5d56b303a66c> in <module>()
737 ----> 1 dview.execute('1/0')
733 ----> 1 dview.execute('1/0')
738
734
739 /path/to/site-packages/IPython/parallel/client/view.pyc in execute(self, code, targets, block)
735 /path/to/site-packages/IPython/parallel/client/view.pyc in execute(self, code, targets, block)
740 591 default: self.block
736 591 default: self.block
741 592 """
737 592 """
742 --> 593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
738 --> 593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
743 594
739 594
744 595 def run(self, filename, targets=None, block=None):
740 595 def run(self, filename, targets=None, block=None):
745
741
746 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
742 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
747
743
748 /path/to/site-packages/IPython/parallel/client/view.pyc in sync_results(f, self, *args, **kwargs)
744 /path/to/site-packages/IPython/parallel/client/view.pyc in sync_results(f, self, *args, **kwargs)
749 55 def sync_results(f, self, *args, **kwargs):
745 55 def sync_results(f, self, *args, **kwargs):
750 56 """sync relevant results from self.client to our results attribute."""
746 56 """sync relevant results from self.client to our results attribute."""
751 ---> 57 ret = f(self, *args, **kwargs)
747 ---> 57 ret = f(self, *args, **kwargs)
752 58 delta = self.outstanding.difference(self.client.outstanding)
748 58 delta = self.outstanding.difference(self.client.outstanding)
753 59 completed = self.outstanding.intersection(delta)
749 59 completed = self.outstanding.intersection(delta)
754
750
755 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
751 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
756
752
757 /path/to/site-packages/IPython/parallel/client/view.pyc in save_ids(f, self, *args, **kwargs)
753 /path/to/site-packages/IPython/parallel/client/view.pyc in save_ids(f, self, *args, **kwargs)
758 44 n_previous = len(self.client.history)
754 44 n_previous = len(self.client.history)
759 45 try:
755 45 try:
760 ---> 46 ret = f(self, *args, **kwargs)
756 ---> 46 ret = f(self, *args, **kwargs)
761 47 finally:
757 47 finally:
762 48 nmsgs = len(self.client.history) - n_previous
758 48 nmsgs = len(self.client.history) - n_previous
763
759
764 /path/to/site-packages/IPython/parallel/client/view.pyc in _really_apply(self, f, args, kwargs, targets, block, track)
760 /path/to/site-packages/IPython/parallel/client/view.pyc in _really_apply(self, f, args, kwargs, targets, block, track)
765 529 if block:
761 529 if block:
766 530 try:
762 530 try:
767 --> 531 return ar.get()
763 --> 531 return ar.get()
768 532 except KeyboardInterrupt:
764 532 except KeyboardInterrupt:
769 533 pass
765 533 pass
770
766
771 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
767 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
772 101 return self._result
768 101 return self._result
773 102 else:
769 102 else:
774 --> 103 raise self._exception
770 --> 103 raise self._exception
775 104 else:
771 104 else:
776 105 raise error.TimeoutError("Result not ready.")
772 105 raise error.TimeoutError("Result not ready.")
777
773
778 CompositeError: one or more exceptions from call to method: _execute
774 CompositeError: one or more exceptions from call to method: _execute
779 [0:apply]: ZeroDivisionError: integer division or modulo by zero
775 [0:apply]: ZeroDivisionError: integer division or modulo by zero
780 [1:apply]: ZeroDivisionError: integer division or modulo by zero
776 [1:apply]: ZeroDivisionError: integer division or modulo by zero
781 [2:apply]: ZeroDivisionError: integer division or modulo by zero
777 [2:apply]: ZeroDivisionError: integer division or modulo by zero
782 [3:apply]: ZeroDivisionError: integer division or modulo by zero
778 [3:apply]: ZeroDivisionError: integer division or modulo by zero
783
779
784 In [82]: %debug
780 In [82]: %debug
785 > /path/to/site-packages/IPython/parallel/client/asyncresult.py(103)get()
781 > /path/to/site-packages/IPython/parallel/client/asyncresult.py(103)get()
786 102 else:
782 102 else:
787 --> 103 raise self._exception
783 --> 103 raise self._exception
788 104 else:
784 104 else:
789
785
790 # With the debugger running, self._exception is the exceptions instance. We can tab complete
786 # With the debugger running, self._exception is the exceptions instance. We can tab complete
791 # on it and see the extra methods that are available.
787 # on it and see the extra methods that are available.
792 ipdb> self._exception.<tab>
788 ipdb> self._exception.<tab>
793 e.__class__ e.__getitem__ e.__new__ e.__setstate__ e.args
789 e.__class__ e.__getitem__ e.__new__ e.__setstate__ e.args
794 e.__delattr__ e.__getslice__ e.__reduce__ e.__str__ e.elist
790 e.__delattr__ e.__getslice__ e.__reduce__ e.__str__ e.elist
795 e.__dict__ e.__hash__ e.__reduce_ex__ e.__weakref__ e.message
791 e.__dict__ e.__hash__ e.__reduce_ex__ e.__weakref__ e.message
796 e.__doc__ e.__init__ e.__repr__ e._get_engine_str e.print_tracebacks
792 e.__doc__ e.__init__ e.__repr__ e._get_engine_str e.print_tracebacks
797 e.__getattribute__ e.__module__ e.__setattr__ e._get_traceback e.raise_exception
793 e.__getattribute__ e.__module__ e.__setattr__ e._get_traceback e.raise_exception
798 ipdb> self._exception.print_tracebacks()
794 ipdb> self._exception.print_tracebacks()
799 [0:apply]:
795 [0:apply]:
800 Traceback (most recent call last):
796 Traceback (most recent call last):
801 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
797 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
802 exec code in working,working
798 exec code in working,working
803 File "<string>", line 1, in <module>
799 File "<string>", line 1, in <module>
804 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
800 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
805 exec code in globals()
801 exec code in globals()
806 File "<string>", line 1, in <module>
802 File "<string>", line 1, in <module>
807 ZeroDivisionError: integer division or modulo by zero
803 ZeroDivisionError: integer division or modulo by zero
808
804
809
805
810 [1:apply]:
806 [1:apply]:
811 Traceback (most recent call last):
807 Traceback (most recent call last):
812 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
808 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
813 exec code in working,working
809 exec code in working,working
814 File "<string>", line 1, in <module>
810 File "<string>", line 1, in <module>
815 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
811 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
816 exec code in globals()
812 exec code in globals()
817 File "<string>", line 1, in <module>
813 File "<string>", line 1, in <module>
818 ZeroDivisionError: integer division or modulo by zero
814 ZeroDivisionError: integer division or modulo by zero
819
815
820
816
821 [2:apply]:
817 [2:apply]:
822 Traceback (most recent call last):
818 Traceback (most recent call last):
823 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
819 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
824 exec code in working,working
820 exec code in working,working
825 File "<string>", line 1, in <module>
821 File "<string>", line 1, in <module>
826 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
822 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
827 exec code in globals()
823 exec code in globals()
828 File "<string>", line 1, in <module>
824 File "<string>", line 1, in <module>
829 ZeroDivisionError: integer division or modulo by zero
825 ZeroDivisionError: integer division or modulo by zero
830
826
831
827
832 [3:apply]:
828 [3:apply]:
833 Traceback (most recent call last):
829 Traceback (most recent call last):
834 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
830 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
835 exec code in working,working
831 exec code in working,working
836 File "<string>", line 1, in <module>
832 File "<string>", line 1, in <module>
837 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
833 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
838 exec code in globals()
834 exec code in globals()
839 File "<string>", line 1, in <module>
835 File "<string>", line 1, in <module>
840 ZeroDivisionError: integer division or modulo by zero
836 ZeroDivisionError: integer division or modulo by zero
841
837
842
838
843 All of this same error handling magic even works in non-blocking mode:
839 All of this same error handling magic even works in non-blocking mode:
844
840
845 .. sourcecode:: ipython
841 .. sourcecode:: ipython
846
842
847 In [83]: dview.block=False
843 In [83]: dview.block=False
848
844
849 In [84]: ar = dview.execute('1/0')
845 In [84]: ar = dview.execute('1/0')
850
846
851 In [85]: ar.get()
847 In [85]: ar.get()
852 ---------------------------------------------------------------------------
848 ---------------------------------------------------------------------------
853 CompositeError Traceback (most recent call last)
849 CompositeError Traceback (most recent call last)
854 /home/user/<ipython-input-21-8531eb3d26fb> in <module>()
850 /home/user/<ipython-input-21-8531eb3d26fb> in <module>()
855 ----> 1 ar.get()
851 ----> 1 ar.get()
856
852
857 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
853 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
858 101 return self._result
854 101 return self._result
859 102 else:
855 102 else:
860 --> 103 raise self._exception
856 --> 103 raise self._exception
861 104 else:
857 104 else:
862 105 raise error.TimeoutError("Result not ready.")
858 105 raise error.TimeoutError("Result not ready.")
863
859
864 CompositeError: one or more exceptions from call to method: _execute
860 CompositeError: one or more exceptions from call to method: _execute
865 [0:apply]: ZeroDivisionError: integer division or modulo by zero
861 [0:apply]: ZeroDivisionError: integer division or modulo by zero
866 [1:apply]: ZeroDivisionError: integer division or modulo by zero
862 [1:apply]: ZeroDivisionError: integer division or modulo by zero
867 [2:apply]: ZeroDivisionError: integer division or modulo by zero
863 [2:apply]: ZeroDivisionError: integer division or modulo by zero
868 [3:apply]: ZeroDivisionError: integer division or modulo by zero
864 [3:apply]: ZeroDivisionError: integer division or modulo by zero
869
865
@@ -1,492 +1,471 b''
1 .. _parallel_task:
1 .. _parallel_task:
2
2
3 ==========================
3 ==========================
4 The IPython task interface
4 The IPython task interface
5 ==========================
5 ==========================
6
6
7 The task interface to the cluster presents the engines as a fault tolerant,
7 The task interface to the cluster presents the engines as a fault tolerant,
8 dynamic load-balanced system of workers. Unlike the multiengine interface, in
8 dynamic load-balanced system of workers. Unlike the multiengine interface, in
9 the task interface the user have no direct access to individual engines. By
9 the task interface the user have no direct access to individual engines. By
10 allowing the IPython scheduler to assign work, this interface is simultaneously
10 allowing the IPython scheduler to assign work, this interface is simultaneously
11 simpler and more powerful.
11 simpler and more powerful.
12
12
13 Best of all, the user can use both of these interfaces running at the same time
13 Best of all, the user can use both of these interfaces running at the same time
14 to take advantage of their respective strengths. When the user can break up
14 to take advantage of their respective strengths. When the user can break up
15 the user's work into segments that do not depend on previous execution, the
15 the user's work into segments that do not depend on previous execution, the
16 task interface is ideal. But it also has more power and flexibility, allowing
16 task interface is ideal. But it also has more power and flexibility, allowing
17 the user to guide the distribution of jobs, without having to assign tasks to
17 the user to guide the distribution of jobs, without having to assign tasks to
18 engines explicitly.
18 engines explicitly.
19
19
20 Starting the IPython controller and engines
20 Starting the IPython controller and engines
21 ===========================================
21 ===========================================
22
22
23 To follow along with this tutorial, you will need to start the IPython
23 To follow along with this tutorial, you will need to start the IPython
24 controller and four IPython engines. The simplest way of doing this is to use
24 controller and four IPython engines. The simplest way of doing this is to use
25 the :command:`ipcluster` command::
25 the :command:`ipcluster` command::
26
26
27 $ ipcluster start -n 4
27 $ ipcluster start -n 4
28
28
29 For more detailed information about starting the controller and engines, see
29 For more detailed information about starting the controller and engines, see
30 our :ref:`introduction <parallel_overview>` to using IPython for parallel computing.
30 our :ref:`introduction <parallel_overview>` to using IPython for parallel computing.
31
31
32 Creating a ``LoadBalancedView`` instance
32 Creating a ``LoadBalancedView`` instance
33 ========================================
33 ========================================
34
34
35 The first step is to import the IPython :mod:`IPython.parallel`
35 The first step is to import the IPython :mod:`IPython.parallel`
36 module and then create a :class:`.Client` instance, and we will also be using
36 module and then create a :class:`.Client` instance, and we will also be using
37 a :class:`LoadBalancedView`, here called `lview`:
37 a :class:`LoadBalancedView`, here called `lview`:
38
38
39 .. sourcecode:: ipython
39 .. sourcecode:: ipython
40
40
41 In [1]: from IPython.parallel import Client
41 In [1]: from IPython.parallel import Client
42
42
43 In [2]: rc = Client()
43 In [2]: rc = Client()
44
44
45
45
46 This form assumes that the controller was started on localhost with default
46 This form assumes that the controller was started on localhost with default
47 configuration. If not, the location of the controller must be given as an
47 configuration. If not, the location of the controller must be given as an
48 argument to the constructor:
48 argument to the constructor:
49
49
50 .. sourcecode:: ipython
50 .. sourcecode:: ipython
51
51
52 # for a visible LAN controller listening on an external port:
52 # for a visible LAN controller listening on an external port:
53 In [2]: rc = Client('tcp://192.168.1.16:10101')
53 In [2]: rc = Client('tcp://192.168.1.16:10101')
54 # or to connect with a specific profile you have set up:
54 # or to connect with a specific profile you have set up:
55 In [3]: rc = Client(profile='mpi')
55 In [3]: rc = Client(profile='mpi')
56
56
57 For load-balanced execution, we will make use of a :class:`LoadBalancedView` object, which can
57 For load-balanced execution, we will make use of a :class:`LoadBalancedView` object, which can
58 be constructed via the client's :meth:`load_balanced_view` method:
58 be constructed via the client's :meth:`load_balanced_view` method:
59
59
60 .. sourcecode:: ipython
60 .. sourcecode:: ipython
61
61
62 In [4]: lview = rc.load_balanced_view() # default load-balanced view
62 In [4]: lview = rc.load_balanced_view() # default load-balanced view
63
63
64 .. seealso::
64 .. seealso::
65
65
66 For more information, see the in-depth explanation of :ref:`Views <parallel_details>`.
66 For more information, see the in-depth explanation of :ref:`Views <parallel_details>`.
67
67
68
68
69 Quick and easy parallelism
69 Quick and easy parallelism
70 ==========================
70 ==========================
71
71
72 In many cases, you simply want to apply a Python function to a sequence of
72 In many cases, you simply want to apply a Python function to a sequence of
73 objects, but *in parallel*. Like the multiengine interface, these can be
73 objects, but *in parallel*. Like the multiengine interface, these can be
74 implemented via the task interface. The exact same tools can perform these
74 implemented via the task interface. The exact same tools can perform these
75 actions in load-balanced ways as well as multiplexed ways: a parallel version
75 actions in load-balanced ways as well as multiplexed ways: a parallel version
76 of :func:`map` and :func:`@parallel` function decorator. If one specifies the
76 of :func:`map` and :func:`@parallel` function decorator. If one specifies the
77 argument `balanced=True`, then they are dynamically load balanced. Thus, if the
77 argument `balanced=True`, then they are dynamically load balanced. Thus, if the
78 execution time per item varies significantly, you should use the versions in
78 execution time per item varies significantly, you should use the versions in
79 the task interface.
79 the task interface.
80
80
81 Parallel map
81 Parallel map
82 ------------
82 ------------
83
83
84 To load-balance :meth:`map`,simply use a LoadBalancedView:
84 To load-balance :meth:`map`,simply use a LoadBalancedView:
85
85
86 .. sourcecode:: ipython
86 .. sourcecode:: ipython
87
87
88 In [62]: lview.block = True
88 In [62]: lview.block = True
89
89
90 In [63]: serial_result = map(lambda x:x**10, range(32))
90 In [63]: serial_result = map(lambda x:x**10, range(32))
91
91
92 In [64]: parallel_result = lview.map(lambda x:x**10, range(32))
92 In [64]: parallel_result = lview.map(lambda x:x**10, range(32))
93
93
94 In [65]: serial_result==parallel_result
94 In [65]: serial_result==parallel_result
95 Out[65]: True
95 Out[65]: True
96
96
97 Parallel function decorator
97 Parallel function decorator
98 ---------------------------
98 ---------------------------
99
99
100 Parallel functions are just like normal function, but they can be called on
100 Parallel functions are just like normal function, but they can be called on
101 sequences and *in parallel*. The multiengine interface provides a decorator
101 sequences and *in parallel*. The multiengine interface provides a decorator
102 that turns any Python function into a parallel function:
102 that turns any Python function into a parallel function:
103
103
104 .. sourcecode:: ipython
104 .. sourcecode:: ipython
105
105
106 In [10]: @lview.parallel()
106 In [10]: @lview.parallel()
107 ....: def f(x):
107 ....: def f(x):
108 ....: return 10.0*x**4
108 ....: return 10.0*x**4
109 ....:
109 ....:
110
110
111 In [11]: f.map(range(32)) # this is done in parallel
111 In [11]: f.map(range(32)) # this is done in parallel
112 Out[11]: [0.0,10.0,160.0,...]
112 Out[11]: [0.0,10.0,160.0,...]
113
113
114 .. _parallel_taskmap:
115
116 Map results are iterable!
117 -------------------------
118
119 When an AsyncResult object actually maps multiple results (e.g. the :class:`~AsyncMapResult`
120 object), you can actually iterate through them, and act on the results as they arrive:
121
122 .. literalinclude:: ../../examples/parallel/itermapresult.py
123 :language: python
124 :lines: 9-34
125
126 .. seealso::
127
128 When AsyncResult or the AsyncMapResult don't provide what you need (for instance,
129 handling individual results as they arrive, but with metadata), you can always
130 just split the original result's ``msg_ids`` attribute, and handle them as you like.
131
132 For an example of this, see :file:`docs/examples/parallel/customresult.py`
133
134
135 .. _parallel_dependencies:
114 .. _parallel_dependencies:
136
115
137 Dependencies
116 Dependencies
138 ============
117 ============
139
118
140 Often, pure atomic load-balancing is too primitive for your work. In these cases, you
119 Often, pure atomic load-balancing is too primitive for your work. In these cases, you
141 may want to associate some kind of `Dependency` that describes when, where, or whether
120 may want to associate some kind of `Dependency` that describes when, where, or whether
142 a task can be run. In IPython, we provide two types of dependencies:
121 a task can be run. In IPython, we provide two types of dependencies:
143 `Functional Dependencies`_ and `Graph Dependencies`_
122 `Functional Dependencies`_ and `Graph Dependencies`_
144
123
145 .. note::
124 .. note::
146
125
147 It is important to note that the pure ZeroMQ scheduler does not support dependencies,
126 It is important to note that the pure ZeroMQ scheduler does not support dependencies,
148 and you will see errors or warnings if you try to use dependencies with the pure
127 and you will see errors or warnings if you try to use dependencies with the pure
149 scheduler.
128 scheduler.
150
129
151 Functional Dependencies
130 Functional Dependencies
152 -----------------------
131 -----------------------
153
132
154 Functional dependencies are used to determine whether a given engine is capable of running
133 Functional dependencies are used to determine whether a given engine is capable of running
155 a particular task. This is implemented via a special :class:`Exception` class,
134 a particular task. This is implemented via a special :class:`Exception` class,
156 :class:`UnmetDependency`, found in `IPython.parallel.error`. Its use is very simple:
135 :class:`UnmetDependency`, found in `IPython.parallel.error`. Its use is very simple:
157 if a task fails with an UnmetDependency exception, then the scheduler, instead of relaying
136 if a task fails with an UnmetDependency exception, then the scheduler, instead of relaying
158 the error up to the client like any other error, catches the error, and submits the task
137 the error up to the client like any other error, catches the error, and submits the task
159 to a different engine. This will repeat indefinitely, and a task will never be submitted
138 to a different engine. This will repeat indefinitely, and a task will never be submitted
160 to a given engine a second time.
139 to a given engine a second time.
161
140
162 You can manually raise the :class:`UnmetDependency` yourself, but IPython has provided
141 You can manually raise the :class:`UnmetDependency` yourself, but IPython has provided
163 some decorators for facilitating this behavior.
142 some decorators for facilitating this behavior.
164
143
165 There are two decorators and a class used for functional dependencies:
144 There are two decorators and a class used for functional dependencies:
166
145
167 .. sourcecode:: ipython
146 .. sourcecode:: ipython
168
147
169 In [9]: from IPython.parallel import depend, require, dependent
148 In [9]: from IPython.parallel import depend, require, dependent
170
149
171 @require
150 @require
172 ********
151 ********
173
152
174 The simplest sort of dependency is requiring that a Python module is available. The
153 The simplest sort of dependency is requiring that a Python module is available. The
175 ``@require`` decorator lets you define a function that will only run on engines where names
154 ``@require`` decorator lets you define a function that will only run on engines where names
176 you specify are importable:
155 you specify are importable:
177
156
178 .. sourcecode:: ipython
157 .. sourcecode:: ipython
179
158
180 In [10]: @require('numpy', 'zmq')
159 In [10]: @require('numpy', 'zmq')
181 ....: def myfunc():
160 ....: def myfunc():
182 ....: return dostuff()
161 ....: return dostuff()
183
162
184 Now, any time you apply :func:`myfunc`, the task will only run on a machine that has
163 Now, any time you apply :func:`myfunc`, the task will only run on a machine that has
185 numpy and pyzmq available, and when :func:`myfunc` is called, numpy and zmq will be imported.
164 numpy and pyzmq available, and when :func:`myfunc` is called, numpy and zmq will be imported.
186
165
187 @depend
166 @depend
188 *******
167 *******
189
168
190 The ``@depend`` decorator lets you decorate any function with any *other* function to
169 The ``@depend`` decorator lets you decorate any function with any *other* function to
191 evaluate the dependency. The dependency function will be called at the start of the task,
170 evaluate the dependency. The dependency function will be called at the start of the task,
192 and if it returns ``False``, then the dependency will be considered unmet, and the task
171 and if it returns ``False``, then the dependency will be considered unmet, and the task
193 will be assigned to another engine. If the dependency returns *anything other than
172 will be assigned to another engine. If the dependency returns *anything other than
194 ``False``*, the rest of the task will continue.
173 ``False``*, the rest of the task will continue.
195
174
196 .. sourcecode:: ipython
175 .. sourcecode:: ipython
197
176
198 In [10]: def platform_specific(plat):
177 In [10]: def platform_specific(plat):
199 ....: import sys
178 ....: import sys
200 ....: return sys.platform == plat
179 ....: return sys.platform == plat
201
180
202 In [11]: @depend(platform_specific, 'darwin')
181 In [11]: @depend(platform_specific, 'darwin')
203 ....: def mactask():
182 ....: def mactask():
204 ....: do_mac_stuff()
183 ....: do_mac_stuff()
205
184
206 In [12]: @depend(platform_specific, 'nt')
185 In [12]: @depend(platform_specific, 'nt')
207 ....: def wintask():
186 ....: def wintask():
208 ....: do_windows_stuff()
187 ....: do_windows_stuff()
209
188
210 In this case, any time you apply ``mytask``, it will only run on an OSX machine.
189 In this case, any time you apply ``mytask``, it will only run on an OSX machine.
211 ``@depend`` is just like ``apply``, in that it has a ``@depend(f,*args,**kwargs)``
190 ``@depend`` is just like ``apply``, in that it has a ``@depend(f,*args,**kwargs)``
212 signature.
191 signature.
213
192
214 dependents
193 dependents
215 **********
194 **********
216
195
217 You don't have to use the decorators on your tasks, if for instance you may want
196 You don't have to use the decorators on your tasks, if for instance you may want
218 to run tasks with a single function but varying dependencies, you can directly construct
197 to run tasks with a single function but varying dependencies, you can directly construct
219 the :class:`dependent` object that the decorators use:
198 the :class:`dependent` object that the decorators use:
220
199
221 .. sourcecode::ipython
200 .. sourcecode::ipython
222
201
223 In [13]: def mytask(*args):
202 In [13]: def mytask(*args):
224 ....: dostuff()
203 ....: dostuff()
225
204
226 In [14]: mactask = dependent(mytask, platform_specific, 'darwin')
205 In [14]: mactask = dependent(mytask, platform_specific, 'darwin')
227 # this is the same as decorating the declaration of mytask with @depend
206 # this is the same as decorating the declaration of mytask with @depend
228 # but you can do it again:
207 # but you can do it again:
229
208
230 In [15]: wintask = dependent(mytask, platform_specific, 'nt')
209 In [15]: wintask = dependent(mytask, platform_specific, 'nt')
231
210
232 # in general:
211 # in general:
233 In [16]: t = dependent(f, g, *dargs, **dkwargs)
212 In [16]: t = dependent(f, g, *dargs, **dkwargs)
234
213
235 # is equivalent to:
214 # is equivalent to:
236 In [17]: @depend(g, *dargs, **dkwargs)
215 In [17]: @depend(g, *dargs, **dkwargs)
237 ....: def t(a,b,c):
216 ....: def t(a,b,c):
238 ....: # contents of f
217 ....: # contents of f
239
218
240 Graph Dependencies
219 Graph Dependencies
241 ------------------
220 ------------------
242
221
243 Sometimes you want to restrict the time and/or location to run a given task as a function
222 Sometimes you want to restrict the time and/or location to run a given task as a function
244 of the time and/or location of other tasks. This is implemented via a subclass of
223 of the time and/or location of other tasks. This is implemented via a subclass of
245 :class:`set`, called a :class:`Dependency`. A Dependency is just a set of `msg_ids`
224 :class:`set`, called a :class:`Dependency`. A Dependency is just a set of `msg_ids`
246 corresponding to tasks, and a few attributes to guide how to decide when the Dependency
225 corresponding to tasks, and a few attributes to guide how to decide when the Dependency
247 has been met.
226 has been met.
248
227
249 The switches we provide for interpreting whether a given dependency set has been met:
228 The switches we provide for interpreting whether a given dependency set has been met:
250
229
251 any|all
230 any|all
252 Whether the dependency is considered met if *any* of the dependencies are done, or
231 Whether the dependency is considered met if *any* of the dependencies are done, or
253 only after *all* of them have finished. This is set by a Dependency's :attr:`all`
232 only after *all* of them have finished. This is set by a Dependency's :attr:`all`
254 boolean attribute, which defaults to ``True``.
233 boolean attribute, which defaults to ``True``.
255
234
256 success [default: True]
235 success [default: True]
257 Whether to consider tasks that succeeded as fulfilling dependencies.
236 Whether to consider tasks that succeeded as fulfilling dependencies.
258
237
259 failure [default : False]
238 failure [default : False]
260 Whether to consider tasks that failed as fulfilling dependencies.
239 Whether to consider tasks that failed as fulfilling dependencies.
261 using `failure=True,success=False` is useful for setting up cleanup tasks, to be run
240 using `failure=True,success=False` is useful for setting up cleanup tasks, to be run
262 only when tasks have failed.
241 only when tasks have failed.
263
242
264 Sometimes you want to run a task after another, but only if that task succeeded. In this case,
243 Sometimes you want to run a task after another, but only if that task succeeded. In this case,
265 ``success`` should be ``True`` and ``failure`` should be ``False``. However sometimes you may
244 ``success`` should be ``True`` and ``failure`` should be ``False``. However sometimes you may
266 not care whether the task succeeds, and always want the second task to run, in which case you
245 not care whether the task succeeds, and always want the second task to run, in which case you
267 should use `success=failure=True`. The default behavior is to only use successes.
246 should use `success=failure=True`. The default behavior is to only use successes.
268
247
269 There are other switches for interpretation that are made at the *task* level. These are
248 There are other switches for interpretation that are made at the *task* level. These are
270 specified via keyword arguments to the client's :meth:`apply` method.
249 specified via keyword arguments to the client's :meth:`apply` method.
271
250
272 after,follow
251 after,follow
273 You may want to run a task *after* a given set of dependencies have been run and/or
252 You may want to run a task *after* a given set of dependencies have been run and/or
274 run it *where* another set of dependencies are met. To support this, every task has an
253 run it *where* another set of dependencies are met. To support this, every task has an
275 `after` dependency to restrict time, and a `follow` dependency to restrict
254 `after` dependency to restrict time, and a `follow` dependency to restrict
276 destination.
255 destination.
277
256
278 timeout
257 timeout
279 You may also want to set a time-limit for how long the scheduler should wait before a
258 You may also want to set a time-limit for how long the scheduler should wait before a
280 task's dependencies are met. This is done via a `timeout`, which defaults to 0, which
259 task's dependencies are met. This is done via a `timeout`, which defaults to 0, which
281 indicates that the task should never timeout. If the timeout is reached, and the
260 indicates that the task should never timeout. If the timeout is reached, and the
282 scheduler still hasn't been able to assign the task to an engine, the task will fail
261 scheduler still hasn't been able to assign the task to an engine, the task will fail
283 with a :class:`DependencyTimeout`.
262 with a :class:`DependencyTimeout`.
284
263
285 .. note::
264 .. note::
286
265
287 Dependencies only work within the task scheduler. You cannot instruct a load-balanced
266 Dependencies only work within the task scheduler. You cannot instruct a load-balanced
288 task to run after a job submitted via the MUX interface.
267 task to run after a job submitted via the MUX interface.
289
268
290 The simplest form of Dependencies is with `all=True,success=True,failure=False`. In these cases,
269 The simplest form of Dependencies is with `all=True,success=True,failure=False`. In these cases,
291 you can skip using Dependency objects, and just pass msg_ids or AsyncResult objects as the
270 you can skip using Dependency objects, and just pass msg_ids or AsyncResult objects as the
292 `follow` and `after` keywords to :meth:`client.apply`:
271 `follow` and `after` keywords to :meth:`client.apply`:
293
272
294 .. sourcecode:: ipython
273 .. sourcecode:: ipython
295
274
296 In [14]: client.block=False
275 In [14]: client.block=False
297
276
298 In [15]: ar = lview.apply(f, args, kwargs)
277 In [15]: ar = lview.apply(f, args, kwargs)
299
278
300 In [16]: ar2 = lview.apply(f2)
279 In [16]: ar2 = lview.apply(f2)
301
280
302 In [17]: with lview.temp_flags(after=[ar,ar2]):
281 In [17]: with lview.temp_flags(after=[ar,ar2]):
303 ....: ar3 = lview.apply(f3)
282 ....: ar3 = lview.apply(f3)
304
283
305 In [18]: with lview.temp_flags(follow=[ar], timeout=2.5)
284 In [18]: with lview.temp_flags(follow=[ar], timeout=2.5)
306 ....: ar4 = lview.apply(f3)
285 ....: ar4 = lview.apply(f3)
307
286
308 .. seealso::
287 .. seealso::
309
288
310 Some parallel workloads can be described as a `Directed Acyclic Graph
289 Some parallel workloads can be described as a `Directed Acyclic Graph
311 <http://en.wikipedia.org/wiki/Directed_acyclic_graph>`_, or DAG. See :ref:`DAG
290 <http://en.wikipedia.org/wiki/Directed_acyclic_graph>`_, or DAG. See :ref:`DAG
312 Dependencies <dag_dependencies>` for an example demonstrating how to use map a NetworkX DAG
291 Dependencies <dag_dependencies>` for an example demonstrating how to use map a NetworkX DAG
313 onto task dependencies.
292 onto task dependencies.
314
293
315
294
316 Impossible Dependencies
295 Impossible Dependencies
317 ***********************
296 ***********************
318
297
319 The schedulers do perform some analysis on graph dependencies to determine whether they
298 The schedulers do perform some analysis on graph dependencies to determine whether they
320 are not possible to be met. If the scheduler does discover that a dependency cannot be
299 are not possible to be met. If the scheduler does discover that a dependency cannot be
321 met, then the task will fail with an :class:`ImpossibleDependency` error. This way, if the
300 met, then the task will fail with an :class:`ImpossibleDependency` error. This way, if the
322 scheduler realized that a task can never be run, it won't sit indefinitely in the
301 scheduler realized that a task can never be run, it won't sit indefinitely in the
323 scheduler clogging the pipeline.
302 scheduler clogging the pipeline.
324
303
325 The basic cases that are checked:
304 The basic cases that are checked:
326
305
327 * depending on nonexistent messages
306 * depending on nonexistent messages
328 * `follow` dependencies were run on more than one machine and `all=True`
307 * `follow` dependencies were run on more than one machine and `all=True`
329 * any dependencies failed and `all=True,success=True,failures=False`
308 * any dependencies failed and `all=True,success=True,failures=False`
330 * all dependencies failed and `all=False,success=True,failure=False`
309 * all dependencies failed and `all=False,success=True,failure=False`
331
310
332 .. warning::
311 .. warning::
333
312
334 This analysis has not been proven to be rigorous, so it is likely possible for tasks
313 This analysis has not been proven to be rigorous, so it is likely possible for tasks
335 to become impossible to run in obscure situations, so a timeout may be a good choice.
314 to become impossible to run in obscure situations, so a timeout may be a good choice.
336
315
337
316
338 Retries and Resubmit
317 Retries and Resubmit
339 ====================
318 ====================
340
319
341 Retries
320 Retries
342 -------
321 -------
343
322
344 Another flag for tasks is `retries`. This is an integer, specifying how many times
323 Another flag for tasks is `retries`. This is an integer, specifying how many times
345 a task should be resubmitted after failure. This is useful for tasks that should still run
324 a task should be resubmitted after failure. This is useful for tasks that should still run
346 if their engine was shutdown, or may have some statistical chance of failing. The default
325 if their engine was shutdown, or may have some statistical chance of failing. The default
347 is to not retry tasks.
326 is to not retry tasks.
348
327
349 Resubmit
328 Resubmit
350 --------
329 --------
351
330
352 Sometimes you may want to re-run a task. This could be because it failed for some reason, and
331 Sometimes you may want to re-run a task. This could be because it failed for some reason, and
353 you have fixed the error, or because you want to restore the cluster to an interrupted state.
332 you have fixed the error, or because you want to restore the cluster to an interrupted state.
354 For this, the :class:`Client` has a :meth:`rc.resubmit` method. This simply takes one or more
333 For this, the :class:`Client` has a :meth:`rc.resubmit` method. This simply takes one or more
355 msg_ids, and returns an :class:`AsyncHubResult` for the result(s). You cannot resubmit
334 msg_ids, and returns an :class:`AsyncHubResult` for the result(s). You cannot resubmit
356 a task that is pending - only those that have finished, either successful or unsuccessful.
335 a task that is pending - only those that have finished, either successful or unsuccessful.
357
336
358 .. _parallel_schedulers:
337 .. _parallel_schedulers:
359
338
360 Schedulers
339 Schedulers
361 ==========
340 ==========
362
341
363 There are a variety of valid ways to determine where jobs should be assigned in a
342 There are a variety of valid ways to determine where jobs should be assigned in a
364 load-balancing situation. In IPython, we support several standard schemes, and
343 load-balancing situation. In IPython, we support several standard schemes, and
365 even make it easy to define your own. The scheme can be selected via the ``scheme``
344 even make it easy to define your own. The scheme can be selected via the ``scheme``
366 argument to :command:`ipcontroller`, or in the :attr:`TaskScheduler.schemename` attribute
345 argument to :command:`ipcontroller`, or in the :attr:`TaskScheduler.schemename` attribute
367 of a controller config object.
346 of a controller config object.
368
347
369 The built-in routing schemes:
348 The built-in routing schemes:
370
349
371 To select one of these schemes, simply do::
350 To select one of these schemes, simply do::
372
351
373 $ ipcontroller --scheme=<schemename>
352 $ ipcontroller --scheme=<schemename>
374 for instance:
353 for instance:
375 $ ipcontroller --scheme=lru
354 $ ipcontroller --scheme=lru
376
355
377 lru: Least Recently Used
356 lru: Least Recently Used
378
357
379 Always assign work to the least-recently-used engine. A close relative of
358 Always assign work to the least-recently-used engine. A close relative of
380 round-robin, it will be fair with respect to the number of tasks, agnostic
359 round-robin, it will be fair with respect to the number of tasks, agnostic
381 with respect to runtime of each task.
360 with respect to runtime of each task.
382
361
383 plainrandom: Plain Random
362 plainrandom: Plain Random
384
363
385 Randomly picks an engine on which to run.
364 Randomly picks an engine on which to run.
386
365
387 twobin: Two-Bin Random
366 twobin: Two-Bin Random
388
367
389 **Requires numpy**
368 **Requires numpy**
390
369
391 Pick two engines at random, and use the LRU of the two. This is known to be better
370 Pick two engines at random, and use the LRU of the two. This is known to be better
392 than plain random in many cases, but requires a small amount of computation.
371 than plain random in many cases, but requires a small amount of computation.
393
372
394 leastload: Least Load
373 leastload: Least Load
395
374
396 **This is the default scheme**
375 **This is the default scheme**
397
376
398 Always assign tasks to the engine with the fewest outstanding tasks (LRU breaks tie).
377 Always assign tasks to the engine with the fewest outstanding tasks (LRU breaks tie).
399
378
400 weighted: Weighted Two-Bin Random
379 weighted: Weighted Two-Bin Random
401
380
402 **Requires numpy**
381 **Requires numpy**
403
382
404 Pick two engines at random using the number of outstanding tasks as inverse weights,
383 Pick two engines at random using the number of outstanding tasks as inverse weights,
405 and use the one with the lower load.
384 and use the one with the lower load.
406
385
407 Greedy Assignment
386 Greedy Assignment
408 -----------------
387 -----------------
409
388
410 Tasks are assigned greedily as they are submitted. If their dependencies are
389 Tasks are assigned greedily as they are submitted. If their dependencies are
411 met, they will be assigned to an engine right away, and multiple tasks can be
390 met, they will be assigned to an engine right away, and multiple tasks can be
412 assigned to an engine at a given time. This limit is set with the
391 assigned to an engine at a given time. This limit is set with the
413 ``TaskScheduler.hwm`` (high water mark) configurable:
392 ``TaskScheduler.hwm`` (high water mark) configurable:
414
393
415 .. sourcecode:: python
394 .. sourcecode:: python
416
395
417 # the most common choices are:
396 # the most common choices are:
418 c.TaskSheduler.hwm = 0 # (minimal latency, default in IPython ≀ 0.12)
397 c.TaskSheduler.hwm = 0 # (minimal latency, default in IPython ≀ 0.12)
419 # or
398 # or
420 c.TaskScheduler.hwm = 1 # (most-informed balancing, default in > 0.12)
399 c.TaskScheduler.hwm = 1 # (most-informed balancing, default in > 0.12)
421
400
422 In IPython ≀ 0.12,the default is 0, or no-limit. That is, there is no limit to the number of
401 In IPython ≀ 0.12,the default is 0, or no-limit. That is, there is no limit to the number of
423 tasks that can be outstanding on a given engine. This greatly benefits the
402 tasks that can be outstanding on a given engine. This greatly benefits the
424 latency of execution, because network traffic can be hidden behind computation.
403 latency of execution, because network traffic can be hidden behind computation.
425 However, this means that workload is assigned without knowledge of how long
404 However, this means that workload is assigned without knowledge of how long
426 each task might take, and can result in poor load-balancing, particularly for
405 each task might take, and can result in poor load-balancing, particularly for
427 submitting a collection of heterogeneous tasks all at once. You can limit this
406 submitting a collection of heterogeneous tasks all at once. You can limit this
428 effect by setting hwm to a positive integer, 1 being maximum load-balancing (a
407 effect by setting hwm to a positive integer, 1 being maximum load-balancing (a
429 task will never be waiting if there is an idle engine), and any larger number
408 task will never be waiting if there is an idle engine), and any larger number
430 being a compromise between load-balance and latency-hiding.
409 being a compromise between load-balance and latency-hiding.
431
410
432 In practice, some users have been confused by having this optimization on by
411 In practice, some users have been confused by having this optimization on by
433 default, and the default value has been changed to 1. This can be slower,
412 default, and the default value has been changed to 1. This can be slower,
434 but has more obvious behavior and won't result in assigning too many tasks to
413 but has more obvious behavior and won't result in assigning too many tasks to
435 some engines in heterogeneous cases.
414 some engines in heterogeneous cases.
436
415
437
416
438 Pure ZMQ Scheduler
417 Pure ZMQ Scheduler
439 ------------------
418 ------------------
440
419
441 For maximum throughput, the 'pure' scheme is not Python at all, but a C-level
420 For maximum throughput, the 'pure' scheme is not Python at all, but a C-level
442 :class:`MonitoredQueue` from PyZMQ, which uses a ZeroMQ ``DEALER`` socket to perform all
421 :class:`MonitoredQueue` from PyZMQ, which uses a ZeroMQ ``DEALER`` socket to perform all
443 load-balancing. This scheduler does not support any of the advanced features of the Python
422 load-balancing. This scheduler does not support any of the advanced features of the Python
444 :class:`.Scheduler`.
423 :class:`.Scheduler`.
445
424
446 Disabled features when using the ZMQ Scheduler:
425 Disabled features when using the ZMQ Scheduler:
447
426
448 * Engine unregistration
427 * Engine unregistration
449 Task farming will be disabled if an engine unregisters.
428 Task farming will be disabled if an engine unregisters.
450 Further, if an engine is unregistered during computation, the scheduler may not recover.
429 Further, if an engine is unregistered during computation, the scheduler may not recover.
451 * Dependencies
430 * Dependencies
452 Since there is no Python logic inside the Scheduler, routing decisions cannot be made
431 Since there is no Python logic inside the Scheduler, routing decisions cannot be made
453 based on message content.
432 based on message content.
454 * Early destination notification
433 * Early destination notification
455 The Python schedulers know which engine gets which task, and notify the Hub. This
434 The Python schedulers know which engine gets which task, and notify the Hub. This
456 allows graceful handling of Engines coming and going. There is no way to know
435 allows graceful handling of Engines coming and going. There is no way to know
457 where ZeroMQ messages have gone, so there is no way to know what tasks are on which
436 where ZeroMQ messages have gone, so there is no way to know what tasks are on which
458 engine until they *finish*. This makes recovery from engine shutdown very difficult.
437 engine until they *finish*. This makes recovery from engine shutdown very difficult.
459
438
460
439
461 .. note::
440 .. note::
462
441
463 TODO: performance comparisons
442 TODO: performance comparisons
464
443
465
444
466
445
467
446
468 More details
447 More details
469 ============
448 ============
470
449
471 The :class:`LoadBalancedView` has many more powerful features that allow quite a bit
450 The :class:`LoadBalancedView` has many more powerful features that allow quite a bit
472 of flexibility in how tasks are defined and run. The next places to look are
451 of flexibility in how tasks are defined and run. The next places to look are
473 in the following classes:
452 in the following classes:
474
453
475 * :class:`~IPython.parallel.client.view.LoadBalancedView`
454 * :class:`~IPython.parallel.client.view.LoadBalancedView`
476 * :class:`~IPython.parallel.client.asyncresult.AsyncResult`
455 * :class:`~IPython.parallel.client.asyncresult.AsyncResult`
477 * :meth:`~IPython.parallel.client.view.LoadBalancedView.apply`
456 * :meth:`~IPython.parallel.client.view.LoadBalancedView.apply`
478 * :mod:`~IPython.parallel.controller.dependency`
457 * :mod:`~IPython.parallel.controller.dependency`
479
458
480 The following is an overview of how to use these classes together:
459 The following is an overview of how to use these classes together:
481
460
482 1. Create a :class:`Client` and :class:`LoadBalancedView`
461 1. Create a :class:`Client` and :class:`LoadBalancedView`
483 2. Define some functions to be run as tasks
462 2. Define some functions to be run as tasks
484 3. Submit your tasks to using the :meth:`apply` method of your
463 3. Submit your tasks to using the :meth:`apply` method of your
485 :class:`LoadBalancedView` instance.
464 :class:`LoadBalancedView` instance.
486 4. Use :meth:`.Client.get_result` to get the results of the
465 4. Use :meth:`.Client.get_result` to get the results of the
487 tasks, or use the :meth:`AsyncResult.get` method of the results to wait
466 tasks, or use the :meth:`AsyncResult.get` method of the results to wait
488 for and then receive the results.
467 for and then receive the results.
489
468
490 .. seealso::
469 .. seealso::
491
470
492 A demo of :ref:`DAG Dependencies <dag_dependencies>` with NetworkX and IPython.
471 A demo of :ref:`DAG Dependencies <dag_dependencies>` with NetworkX and IPython.
General Comments 0
You need to be logged in to leave comments. Login now