SlideShare a Scribd company logo
1 of 47
Download to read offline
Robustifying concurrent.futures
Thomas Moreau - Olivier Grisel
1 / 26
Embarassingly parallel computation in python
using a pool of workers
Three API available:
multiprocessing : rst implementation.
concurrent.futures : reimplementation using multiprocessing
under the hood.
loky : robusti cation of concurrent.futures .
2 / 26
The concurrent.futures API
3 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
fit_model is the function that we want to run
asynchronously.
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
A new job is submitted to the Executor .
The Future object future1 holds the state of
the computation.
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
A new job is submitted to the Executor .
The Future object future1 holds the state of
the computation.
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
A new job is submitted to the Executor .
The Future object future1 holds the state of
the computation.
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
A new job is submitted to the Executor .
The Future object future1 holds the state of
the computation.
Wait for the computation to end and return
the result with f.result .
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
A new job is submitted to the Executor .
The Future object future1 holds the state of
the computation.
Wait for the computation to end and return
the result with f.result .
The ressources are cleaned up.
The Executor : a worker pool
4 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
fit_model is the function that we want to run
asynchronously.
We instanciate a ThreadPoolExecutor with 4
threads.
A new job is submitted to the Executor .
The Future object future1 holds the state of
the computation.
Wait for the computation to end and return
the result with f.result .
The ressources are cleaned up.
The Executor : a worker pool
Submitting more than one job returns an iterator: executor.map
4 / 26
Blocking methods
f.result(timeout=None)
f.exception(timeout=None)
wait for computations to be done.
The Future object: an asynchronous result state.
States
Future objects hold the state of the asynchronous computations, wich can
be in one of 4 states: Not started, Running, Cancelled and Done
The state of a Future can be checked using f.running, f.cancelled,
f.done .
5 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
from concurrent.futures import ThreadPoolExecutor
def fit_model(params):
# Heavy computation
return model
# Create an executor with 4 threads
with ThreadPoolExecutor(max_workers=4) as executor:
# Submit an asynchronous job and return a Future
future1 = executor.submit(fit_model, param1)
# Submit other job
future2 = executor.submit(fit_model, param2)
# Run other computation
...
# Blocking call, wait and return the result
model1 = future1.result(timeout=None)
model2 = future2.result(timeout=None)
# The ressources have been cleaned up
print(model1, model2)
The Executor : a worker pool
6 / 26
Choosing the type of worker: Thread or
Process ?
7 / 26
Running on multiple cores
Python GIL
The internal implementation of python interpreter relies on a "Global
Interpreter Lock" (GIL), protecting the concurrent access to python objects:
Only one thread can acquire it.
Not designed for e cient multicore computing.
Global lock everytime we access a python object.
Released when performing long I/O operations or by some libraries.
(e.g. numpy, openMP,..) 8 / 26
Real system thread:
pthread
windows thread
All the computation are done
with a single interpreter.
Advantages:
Fast spawning
Reduced memory overhead
No communication overhead
(shared python objects)
Thread
9 / 26
Real system thread:
pthread
windows thread
All the computation are done
with a single interpreter.
Advantages:
Fast spawning
Reduced memory overhead
No communication overhead
(shared python objects)
Thread
Wait... shared python objects and a single interpreter?!?
9 / 26
Real system thread:
pthread
windows thread
All the computation are done
with a single interpreter.
Advantages:
Fast spawning
Reduced memory overhead
No communication overhead
(shared python objects)
Thread
Wait... shared python objects and a single interpreter?!?
There is only one GIL!
9 / 26
Thread
Multiple threads running python code:
This is not quicker than sequential even on a multicore machine.
10 / 26
Thread
Threads hold the GIL when running python code.
They release it when blocking for I/O:
Or when using some c library:
11 / 26
Create a new python interpreter
per worker.
Each worker run in its own
interpreter.
Inconvenients:
Slow spawning
Higher memory overhead
Higher communication overhead.
Process
12 / 26
Create a new python interpreter
per worker.
Each worker run in its own
interpreter.
Inconvenients:
Slow spawning
Higher memory overhead
Higher communication overhead.
Process
But there is no GIL!
The computation can be done in parallel even for python code.
12 / 26
Create a new python interpreter
per worker.
Each worker run in its own
interpreter.
Inconvenients:
Slow spawning
Higher memory overhead
Higher communication overhead.
Process
But there is no GIL!
The computation can be done in parallel even for python code.
Method to create a new interpreter: fork or spawn
12 / 26
Advantages:
Low spawning overhead.
The interpreter is warm
imported.
Inconvenient:
Bad interaction with
multithreaded programs
Does not respect the POSIX
speci cations
Launching a new interpreter: fork
Duplicate the current interpreter. (Only available on UNIX)
Some libraries crash: numpy on OSX, openMP, ...⇒
13 / 26
Advantages:
Safe (respect POSIX)
Fresh interpreter without
extra libraries.
Inconvenient:
Slower to start.
Need to reload the libraries,
rede ne the functions...
Launching a new interpreter: spawn
Create a new interpreter from scratch.
14 / 26
Comparison between Thread and Process
Thread Process (fork) Process (spawn)
E cient multicore run
No communication overhead
POSIX safe
No spawning overhead
15 / 26
Comparison between Thread and Process
Thread Process (fork) Process (spawn)
E cient multicore run
No communication overhead
POSIX safe
No spawning overhead
Hide the spawning overhead by reusing the pool of processes.⇒
16 / 26
Reusing a ProcessPoolExecutor .
17 / 26
Reusing a ProcessPoolExecutor .
To Avoid the spawning overhead, reuse a previously started
ProcessPoolExecutor .
The spawning overhead is only paid once.
Easy using a global pool of process.
Main issue: is that robust?
18 / 26
Managing the state of the executor
Example deadlock
>>> from concurrent.futures import ProcessPoolExecutor
>>> with ProcessPoolExecutor(max_workers=4) as e:
... e.submit(lambda: 1).result()
...
Traceback (most recent call last):
File "/usr/lib/python3.6/multiprocessing/queues.py", line 241, in _feed
obj = _ForkingPickler.dumps(obj)
File "/usr/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fcff0184d08>:
attribute lookup <lambda> on __main__ failed
^C
It can be tricky to know which submit call crashed the Executor .
19 / 26
Managing the state of the executor
Example deadlock
>>> from concurrent.futures import ProcessPoolExecutor
>>> with ProcessPoolExecutor(max_workers=4) as e:
... e.submit(lambda: 1)
...
Traceback (most recent call last):
File "/usr/lib/python3.6/multiprocessing/queues.py", line 241, in _feed
obj = _ForkingPickler.dumps(obj)
File "/usr/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7f5c787bd488>:
attribute lookup <lambda> on __main__ failed
^C
Even worse, shutdown itself is deadlocked.
20 / 26
Reusable pool of workers: loky .
21 / 26
loky : a robust pool of workers
>>> from loky import ProcessPoolExecutor
>>> class CrashAtPickle(object):
... """Bad object that triggers a segfault at unpickling time."""
... def __reduce__(self):
... raise RuntimeError()
...
>>> with ProcessPoolExecutor(max_workers=4) as e:
... e.submit(CrashAtPickle()).result()
...
Traceback (most recent call last):
...
RuntimeError
Traceback (most recent call last):
...
BrokenExecutor: The QueueFeederThread was terminated abruptly while feeding a
new job. This can be due to a job pickling error.
>>>
Return and raise a user friendly exception.
Fix some other deadlocks.
22 / 26
>>> from loky import get_reusable_executor
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(id, 42).result()
139655595838272
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(CrashAtUnpickle()).result()
Traceback (most recent call last):
...
BrokenExecutorError
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
1
>>> excutor.submit(id, 42).result()
139655595838272
Create a ProcessPoolExecutor using the
factory function get_reusable_executor .
A reusable ProcessPoolExecutor
23 / 26
>>> from loky import get_reusable_executor
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(id, 42).result()
139655595838272
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(CrashAtUnpickle()).result()
Traceback (most recent call last):
...
BrokenExecutorError
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
1
>>> excutor.submit(id, 42).result()
139655595838272
Create a ProcessPoolExecutor using the
factory function get_reusable_executor .
The executor can be used exactly as
ProcessPoolExecutor .
A reusable ProcessPoolExecutor
23 / 26
>>> from loky import get_reusable_executor
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(id, 42).result()
139655595838272
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(CrashAtUnpickle()).result()
Traceback (most recent call last):
...
BrokenExecutorError
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
1
>>> excutor.submit(id, 42).result()
139655595838272
Create a ProcessPoolExecutor using the
factory function get_reusable_executor .
The executor can be used exactly as
ProcessPoolExecutor .
When the factory is called elsewhere, reuse
the same executor if it is working.
A reusable ProcessPoolExecutor
23 / 26
>>> from loky import get_reusable_executor
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(id, 42).result()
139655595838272
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
0
>>> excutor.submit(CrashAtUnpickle()).result()
Traceback (most recent call last):
...
BrokenExecutorError
>>> excutor = get_reusable_executor(max_workers=4)
>>> print(excutor.executor_id)
1
>>> excutor.submit(id, 42).result()
139655595838272
Create a ProcessPoolExecutor using the
factory function get_reusable_executor .
The executor can be used exactly as
ProcessPoolExecutor .
When the factory is called elsewhere, reuse
the same executor if it is working.
When the executor is broken, automatically
re-spawn a new one.
A reusable ProcessPoolExecutor
23 / 26
Conclusion
24 / 26
Conclusion
Thread can be e cient to run multicore programs if your
code releases the GIL.
Else, you should use Process with spawn and try to reuse the
pool of process as much as possible.
loky can help you do that ;).
Improves the management of a pool of workers in projects
such as joblib .
25 / 26
Thanks for your attention!
Slides available at tommoral.github.io/pyparis17/
More on the GIL by Dave Beazley : dabeaz.com/python/GIL.pdf
Loky project : github.com/tommoral/loky
@tomamoral
26 / 26

More Related Content

What's hot

Inline assembly language programs in c
Inline assembly language programs in cInline assembly language programs in c
Inline assembly language programs in c
Tech_MX
 
Operator overloadng
Operator overloadngOperator overloadng
Operator overloadng
preethalal
 
#OOP_D_ITS - 5th - C++ Oop Operator Overloading
#OOP_D_ITS - 5th - C++ Oop Operator Overloading#OOP_D_ITS - 5th - C++ Oop Operator Overloading
#OOP_D_ITS - 5th - C++ Oop Operator Overloading
Hadziq Fabroyir
 

What's hot (20)

User defined functions.1
User defined functions.1User defined functions.1
User defined functions.1
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
Bca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloadingBca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloading
 
Basic information of function in cpu
Basic information of function in cpuBasic information of function in cpu
Basic information of function in cpu
 
16717 functions in C++
16717 functions in C++16717 functions in C++
16717 functions in C++
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
Functions in c++
Functions in c++Functions in c++
Functions in c++
 
Inline assembly language programs in c
Inline assembly language programs in cInline assembly language programs in c
Inline assembly language programs in c
 
Operator overloadng
Operator overloadngOperator overloadng
Operator overloadng
 
Iterators and Generators
Iterators and GeneratorsIterators and Generators
Iterators and Generators
 
#OOP_D_ITS - 5th - C++ Oop Operator Overloading
#OOP_D_ITS - 5th - C++ Oop Operator Overloading#OOP_D_ITS - 5th - C++ Oop Operator Overloading
#OOP_D_ITS - 5th - C++ Oop Operator Overloading
 
Synapse india complain sharing info on chapter 8 operator overloading
Synapse india complain sharing info on chapter 8   operator overloadingSynapse india complain sharing info on chapter 8   operator overloading
Synapse india complain sharing info on chapter 8 operator overloading
 
Lecture#6 functions in c++
Lecture#6 functions in c++Lecture#6 functions in c++
Lecture#6 functions in c++
 
Beyond Mere Actors
Beyond Mere ActorsBeyond Mere Actors
Beyond Mere Actors
 
Unary operator overloading
Unary operator overloadingUnary operator overloading
Unary operator overloading
 
Mca 2nd sem u-4 operator overloading
Mca 2nd  sem u-4 operator overloadingMca 2nd  sem u-4 operator overloading
Mca 2nd sem u-4 operator overloading
 
Operator overloading
Operator overloadingOperator overloading
Operator overloading
 
Thinking in Functions
Thinking in FunctionsThinking in Functions
Thinking in Functions
 
operator overloading in C++
operator overloading in C++operator overloading in C++
operator overloading in C++
 

Similar to Robustifying concurrent.futures, Thomas Moreau

Chapter_1.__Functions_in_C++[1].pdf
Chapter_1.__Functions_in_C++[1].pdfChapter_1.__Functions_in_C++[1].pdf
Chapter_1.__Functions_in_C++[1].pdf
TeshaleSiyum
 
Chapter 1. Functions in C++.pdf
Chapter 1.  Functions in C++.pdfChapter 1.  Functions in C++.pdf
Chapter 1. Functions in C++.pdf
TeshaleSiyum
 
Windows Phone 8 - 3.5 Async Programming
Windows Phone 8 - 3.5 Async ProgrammingWindows Phone 8 - 3.5 Async Programming
Windows Phone 8 - 3.5 Async Programming
Oliver Scheer
 
Bottom of FormCreate your own FunctionFunctionsFor eac.docx
Bottom of FormCreate your own FunctionFunctionsFor eac.docxBottom of FormCreate your own FunctionFunctionsFor eac.docx
Bottom of FormCreate your own FunctionFunctionsFor eac.docx
AASTHA76
 

Similar to Robustifying concurrent.futures, Thomas Moreau (20)

Effective java item 80 and 81
Effective java   item 80 and 81Effective java   item 80 and 81
Effective java item 80 and 81
 
Parallel Programming With Dot Net
Parallel Programming With Dot NetParallel Programming With Dot Net
Parallel Programming With Dot Net
 
What's New In Python 2.5
What's New In Python 2.5What's New In Python 2.5
What's New In Python 2.5
 
Concurrent Programming in Java
Concurrent Programming in JavaConcurrent Programming in Java
Concurrent Programming in Java
 
اسلاید ارائه اول جلسه ۱۰ کلاس پایتون برای هکر های قانونی
اسلاید ارائه اول جلسه ۱۰ کلاس پایتون برای هکر های قانونی اسلاید ارائه اول جلسه ۱۰ کلاس پایتون برای هکر های قانونی
اسلاید ارائه اول جلسه ۱۰ کلاس پایتون برای هکر های قانونی
 
Chapter 4
Chapter 4Chapter 4
Chapter 4
 
JavaCro'15 - Spring @Async - Dragan Juričić
JavaCro'15 - Spring @Async - Dragan JuričićJavaCro'15 - Spring @Async - Dragan Juričić
JavaCro'15 - Spring @Async - Dragan Juričić
 
Java concurrency
Java concurrencyJava concurrency
Java concurrency
 
Chapter_1.__Functions_in_C++[1].pdf
Chapter_1.__Functions_in_C++[1].pdfChapter_1.__Functions_in_C++[1].pdf
Chapter_1.__Functions_in_C++[1].pdf
 
Chapter 1. Functions in C++.pdf
Chapter 1.  Functions in C++.pdfChapter 1.  Functions in C++.pdf
Chapter 1. Functions in C++.pdf
 
Windows Phone 8 - 3.5 Async Programming
Windows Phone 8 - 3.5 Async ProgrammingWindows Phone 8 - 3.5 Async Programming
Windows Phone 8 - 3.5 Async Programming
 
Java concurrency model - The Future Task
Java concurrency model - The Future TaskJava concurrency model - The Future Task
Java concurrency model - The Future Task
 
Using Akka Futures
Using Akka FuturesUsing Akka Futures
Using Akka Futures
 
Python testing using mock and pytest
Python testing using mock and pytestPython testing using mock and pytest
Python testing using mock and pytest
 
function.pptx
function.pptxfunction.pptx
function.pptx
 
Leveraging Completable Futures to handle your query results Asynchrhonously
Leveraging Completable Futures to handle your query results AsynchrhonouslyLeveraging Completable Futures to handle your query results Asynchrhonously
Leveraging Completable Futures to handle your query results Asynchrhonously
 
Lecture 4, c++(complete reference,herbet sheidt)chapter-14
Lecture 4, c++(complete reference,herbet sheidt)chapter-14Lecture 4, c++(complete reference,herbet sheidt)chapter-14
Lecture 4, c++(complete reference,herbet sheidt)chapter-14
 
Bottom of FormCreate your own FunctionFunctionsFor eac.docx
Bottom of FormCreate your own FunctionFunctionsFor eac.docxBottom of FormCreate your own FunctionFunctionsFor eac.docx
Bottom of FormCreate your own FunctionFunctionsFor eac.docx
 
Scala Future & Promises
Scala Future & PromisesScala Future & Promises
Scala Future & Promises
 
ForLoopandUserDefinedFunctions.pptx
ForLoopandUserDefinedFunctions.pptxForLoopandUserDefinedFunctions.pptx
ForLoopandUserDefinedFunctions.pptx
 

More from Pôle Systematic Paris-Region

More from Pôle Systematic Paris-Region (20)

OSIS19_IoT :Transparent remote connectivity to short-range IoT devices, by Na...
OSIS19_IoT :Transparent remote connectivity to short-range IoT devices, by Na...OSIS19_IoT :Transparent remote connectivity to short-range IoT devices, by Na...
OSIS19_IoT :Transparent remote connectivity to short-range IoT devices, by Na...
 
OSIS19_Cloud : SAFC: Scheduling and Allocation Framework for Containers in a ...
OSIS19_Cloud : SAFC: Scheduling and Allocation Framework for Containers in a ...OSIS19_Cloud : SAFC: Scheduling and Allocation Framework for Containers in a ...
OSIS19_Cloud : SAFC: Scheduling and Allocation Framework for Containers in a ...
 
OSIS19_Cloud : Qu’apporte l’observabilité à la gestion de configuration? par ...
OSIS19_Cloud : Qu’apporte l’observabilité à la gestion de configuration? par ...OSIS19_Cloud : Qu’apporte l’observabilité à la gestion de configuration? par ...
OSIS19_Cloud : Qu’apporte l’observabilité à la gestion de configuration? par ...
 
OSIS19_Cloud : Performance and power management in virtualized data centers, ...
OSIS19_Cloud : Performance and power management in virtualized data centers, ...OSIS19_Cloud : Performance and power management in virtualized data centers, ...
OSIS19_Cloud : Performance and power management in virtualized data centers, ...
 
OSIS19_Cloud : Des objets dans le cloud, et qui y restent -- L'expérience du ...
OSIS19_Cloud : Des objets dans le cloud, et qui y restent -- L'expérience du ...OSIS19_Cloud : Des objets dans le cloud, et qui y restent -- L'expérience du ...
OSIS19_Cloud : Des objets dans le cloud, et qui y restent -- L'expérience du ...
 
OSIS19_Cloud : Attribution automatique de ressources pour micro-services, Alt...
OSIS19_Cloud : Attribution automatique de ressources pour micro-services, Alt...OSIS19_Cloud : Attribution automatique de ressources pour micro-services, Alt...
OSIS19_Cloud : Attribution automatique de ressources pour micro-services, Alt...
 
OSIS19_IoT : State of the art in security for embedded systems and IoT, by Pi...
OSIS19_IoT : State of the art in security for embedded systems and IoT, by Pi...OSIS19_IoT : State of the art in security for embedded systems and IoT, by Pi...
OSIS19_IoT : State of the art in security for embedded systems and IoT, by Pi...
 
Osis19_IoT: Proof of Pointer Programs with Ownership in SPARK, by Yannick Moy
Osis19_IoT: Proof of Pointer Programs with Ownership in SPARK, by Yannick MoyOsis19_IoT: Proof of Pointer Programs with Ownership in SPARK, by Yannick Moy
Osis19_IoT: Proof of Pointer Programs with Ownership in SPARK, by Yannick Moy
 
Osis18_Cloud : Pas de commun sans communauté ?
Osis18_Cloud : Pas de commun sans communauté ?Osis18_Cloud : Pas de commun sans communauté ?
Osis18_Cloud : Pas de commun sans communauté ?
 
Osis18_Cloud : Projet Wolphin
Osis18_Cloud : Projet Wolphin Osis18_Cloud : Projet Wolphin
Osis18_Cloud : Projet Wolphin
 
Osis18_Cloud : Virtualisation efficace d’architectures NUMA
Osis18_Cloud : Virtualisation efficace d’architectures NUMAOsis18_Cloud : Virtualisation efficace d’architectures NUMA
Osis18_Cloud : Virtualisation efficace d’architectures NUMA
 
Osis18_Cloud : DeepTorrent Stockage distribué perenne basé sur Bittorrent
Osis18_Cloud : DeepTorrent Stockage distribué perenne basé sur BittorrentOsis18_Cloud : DeepTorrent Stockage distribué perenne basé sur Bittorrent
Osis18_Cloud : DeepTorrent Stockage distribué perenne basé sur Bittorrent
 
Osis18_Cloud : Software-heritage
Osis18_Cloud : Software-heritageOsis18_Cloud : Software-heritage
Osis18_Cloud : Software-heritage
 
OSIS18_IoT: L'approche machine virtuelle pour les microcontrôleurs, le projet...
OSIS18_IoT: L'approche machine virtuelle pour les microcontrôleurs, le projet...OSIS18_IoT: L'approche machine virtuelle pour les microcontrôleurs, le projet...
OSIS18_IoT: L'approche machine virtuelle pour les microcontrôleurs, le projet...
 
OSIS18_IoT: La securite des objets connectes a bas cout avec l'os et riot
OSIS18_IoT: La securite des objets connectes a bas cout avec l'os et riotOSIS18_IoT: La securite des objets connectes a bas cout avec l'os et riot
OSIS18_IoT: La securite des objets connectes a bas cout avec l'os et riot
 
OSIS18_IoT : Solution de mise au point pour les systemes embarques, par Julio...
OSIS18_IoT : Solution de mise au point pour les systemes embarques, par Julio...OSIS18_IoT : Solution de mise au point pour les systemes embarques, par Julio...
OSIS18_IoT : Solution de mise au point pour les systemes embarques, par Julio...
 
OSIS18_IoT : Securisation du reseau des objets connectes, par Nicolas LE SAUZ...
OSIS18_IoT : Securisation du reseau des objets connectes, par Nicolas LE SAUZ...OSIS18_IoT : Securisation du reseau des objets connectes, par Nicolas LE SAUZ...
OSIS18_IoT : Securisation du reseau des objets connectes, par Nicolas LE SAUZ...
 
OSIS18_IoT : Ada and SPARK - Defense in Depth for Safe Micro-controller Progr...
OSIS18_IoT : Ada and SPARK - Defense in Depth for Safe Micro-controller Progr...OSIS18_IoT : Ada and SPARK - Defense in Depth for Safe Micro-controller Progr...
OSIS18_IoT : Ada and SPARK - Defense in Depth for Safe Micro-controller Progr...
 
OSIS18_IoT : RTEMS pour l'IoT professionnel, par Pierre Ficheux (Smile ECS)
OSIS18_IoT : RTEMS pour l'IoT professionnel, par Pierre Ficheux (Smile ECS)OSIS18_IoT : RTEMS pour l'IoT professionnel, par Pierre Ficheux (Smile ECS)
OSIS18_IoT : RTEMS pour l'IoT professionnel, par Pierre Ficheux (Smile ECS)
 
PyParis 2017 / Un mooc python, by thierry parmentelat
PyParis 2017 / Un mooc python, by thierry parmentelatPyParis 2017 / Un mooc python, by thierry parmentelat
PyParis 2017 / Un mooc python, by thierry parmentelat
 

Recently uploaded

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Recently uploaded (20)

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 

Robustifying concurrent.futures, Thomas Moreau

  • 2. Embarassingly parallel computation in python using a pool of workers Three API available: multiprocessing : rst implementation. concurrent.futures : reimplementation using multiprocessing under the hood. loky : robusti cation of concurrent.futures . 2 / 26
  • 4. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model fit_model is the function that we want to run asynchronously. The Executor : a worker pool 4 / 26
  • 5. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. The Executor : a worker pool 4 / 26
  • 6. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. A new job is submitted to the Executor . The Future object future1 holds the state of the computation. The Executor : a worker pool 4 / 26
  • 7. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. A new job is submitted to the Executor . The Future object future1 holds the state of the computation. The Executor : a worker pool 4 / 26
  • 8. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. A new job is submitted to the Executor . The Future object future1 holds the state of the computation. The Executor : a worker pool 4 / 26
  • 9. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. A new job is submitted to the Executor . The Future object future1 holds the state of the computation. Wait for the computation to end and return the result with f.result . The Executor : a worker pool 4 / 26
  • 10. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. A new job is submitted to the Executor . The Future object future1 holds the state of the computation. Wait for the computation to end and return the result with f.result . The ressources are cleaned up. The Executor : a worker pool 4 / 26
  • 11. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) fit_model is the function that we want to run asynchronously. We instanciate a ThreadPoolExecutor with 4 threads. A new job is submitted to the Executor . The Future object future1 holds the state of the computation. Wait for the computation to end and return the result with f.result . The ressources are cleaned up. The Executor : a worker pool Submitting more than one job returns an iterator: executor.map 4 / 26
  • 12. Blocking methods f.result(timeout=None) f.exception(timeout=None) wait for computations to be done. The Future object: an asynchronous result state. States Future objects hold the state of the asynchronous computations, wich can be in one of 4 states: Not started, Running, Cancelled and Done The state of a Future can be checked using f.running, f.cancelled, f.done . 5 / 26
  • 13. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 14. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 15. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 16. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 17. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 18. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 19. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 20. from concurrent.futures import ThreadPoolExecutor def fit_model(params): # Heavy computation return model # Create an executor with 4 threads with ThreadPoolExecutor(max_workers=4) as executor: # Submit an asynchronous job and return a Future future1 = executor.submit(fit_model, param1) # Submit other job future2 = executor.submit(fit_model, param2) # Run other computation ... # Blocking call, wait and return the result model1 = future1.result(timeout=None) model2 = future2.result(timeout=None) # The ressources have been cleaned up print(model1, model2) The Executor : a worker pool 6 / 26
  • 21. Choosing the type of worker: Thread or Process ? 7 / 26
  • 22. Running on multiple cores Python GIL The internal implementation of python interpreter relies on a "Global Interpreter Lock" (GIL), protecting the concurrent access to python objects: Only one thread can acquire it. Not designed for e cient multicore computing. Global lock everytime we access a python object. Released when performing long I/O operations or by some libraries. (e.g. numpy, openMP,..) 8 / 26
  • 23. Real system thread: pthread windows thread All the computation are done with a single interpreter. Advantages: Fast spawning Reduced memory overhead No communication overhead (shared python objects) Thread 9 / 26
  • 24. Real system thread: pthread windows thread All the computation are done with a single interpreter. Advantages: Fast spawning Reduced memory overhead No communication overhead (shared python objects) Thread Wait... shared python objects and a single interpreter?!? 9 / 26
  • 25. Real system thread: pthread windows thread All the computation are done with a single interpreter. Advantages: Fast spawning Reduced memory overhead No communication overhead (shared python objects) Thread Wait... shared python objects and a single interpreter?!? There is only one GIL! 9 / 26
  • 26. Thread Multiple threads running python code: This is not quicker than sequential even on a multicore machine. 10 / 26
  • 27. Thread Threads hold the GIL when running python code. They release it when blocking for I/O: Or when using some c library: 11 / 26
  • 28. Create a new python interpreter per worker. Each worker run in its own interpreter. Inconvenients: Slow spawning Higher memory overhead Higher communication overhead. Process 12 / 26
  • 29. Create a new python interpreter per worker. Each worker run in its own interpreter. Inconvenients: Slow spawning Higher memory overhead Higher communication overhead. Process But there is no GIL! The computation can be done in parallel even for python code. 12 / 26
  • 30. Create a new python interpreter per worker. Each worker run in its own interpreter. Inconvenients: Slow spawning Higher memory overhead Higher communication overhead. Process But there is no GIL! The computation can be done in parallel even for python code. Method to create a new interpreter: fork or spawn 12 / 26
  • 31. Advantages: Low spawning overhead. The interpreter is warm imported. Inconvenient: Bad interaction with multithreaded programs Does not respect the POSIX speci cations Launching a new interpreter: fork Duplicate the current interpreter. (Only available on UNIX) Some libraries crash: numpy on OSX, openMP, ...⇒ 13 / 26
  • 32. Advantages: Safe (respect POSIX) Fresh interpreter without extra libraries. Inconvenient: Slower to start. Need to reload the libraries, rede ne the functions... Launching a new interpreter: spawn Create a new interpreter from scratch. 14 / 26
  • 33. Comparison between Thread and Process Thread Process (fork) Process (spawn) E cient multicore run No communication overhead POSIX safe No spawning overhead 15 / 26
  • 34. Comparison between Thread and Process Thread Process (fork) Process (spawn) E cient multicore run No communication overhead POSIX safe No spawning overhead Hide the spawning overhead by reusing the pool of processes.⇒ 16 / 26
  • 36. Reusing a ProcessPoolExecutor . To Avoid the spawning overhead, reuse a previously started ProcessPoolExecutor . The spawning overhead is only paid once. Easy using a global pool of process. Main issue: is that robust? 18 / 26
  • 37. Managing the state of the executor Example deadlock >>> from concurrent.futures import ProcessPoolExecutor >>> with ProcessPoolExecutor(max_workers=4) as e: ... e.submit(lambda: 1).result() ... Traceback (most recent call last): File "/usr/lib/python3.6/multiprocessing/queues.py", line 241, in _feed obj = _ForkingPickler.dumps(obj) File "/usr/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) _pickle.PicklingError: Can't pickle <function <lambda> at 0x7fcff0184d08>: attribute lookup <lambda> on __main__ failed ^C It can be tricky to know which submit call crashed the Executor . 19 / 26
  • 38. Managing the state of the executor Example deadlock >>> from concurrent.futures import ProcessPoolExecutor >>> with ProcessPoolExecutor(max_workers=4) as e: ... e.submit(lambda: 1) ... Traceback (most recent call last): File "/usr/lib/python3.6/multiprocessing/queues.py", line 241, in _feed obj = _ForkingPickler.dumps(obj) File "/usr/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) _pickle.PicklingError: Can't pickle <function <lambda> at 0x7f5c787bd488>: attribute lookup <lambda> on __main__ failed ^C Even worse, shutdown itself is deadlocked. 20 / 26
  • 39. Reusable pool of workers: loky . 21 / 26
  • 40. loky : a robust pool of workers >>> from loky import ProcessPoolExecutor >>> class CrashAtPickle(object): ... """Bad object that triggers a segfault at unpickling time.""" ... def __reduce__(self): ... raise RuntimeError() ... >>> with ProcessPoolExecutor(max_workers=4) as e: ... e.submit(CrashAtPickle()).result() ... Traceback (most recent call last): ... RuntimeError Traceback (most recent call last): ... BrokenExecutor: The QueueFeederThread was terminated abruptly while feeding a new job. This can be due to a job pickling error. >>> Return and raise a user friendly exception. Fix some other deadlocks. 22 / 26
  • 41. >>> from loky import get_reusable_executor >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(id, 42).result() 139655595838272 >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(CrashAtUnpickle()).result() Traceback (most recent call last): ... BrokenExecutorError >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 1 >>> excutor.submit(id, 42).result() 139655595838272 Create a ProcessPoolExecutor using the factory function get_reusable_executor . A reusable ProcessPoolExecutor 23 / 26
  • 42. >>> from loky import get_reusable_executor >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(id, 42).result() 139655595838272 >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(CrashAtUnpickle()).result() Traceback (most recent call last): ... BrokenExecutorError >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 1 >>> excutor.submit(id, 42).result() 139655595838272 Create a ProcessPoolExecutor using the factory function get_reusable_executor . The executor can be used exactly as ProcessPoolExecutor . A reusable ProcessPoolExecutor 23 / 26
  • 43. >>> from loky import get_reusable_executor >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(id, 42).result() 139655595838272 >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(CrashAtUnpickle()).result() Traceback (most recent call last): ... BrokenExecutorError >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 1 >>> excutor.submit(id, 42).result() 139655595838272 Create a ProcessPoolExecutor using the factory function get_reusable_executor . The executor can be used exactly as ProcessPoolExecutor . When the factory is called elsewhere, reuse the same executor if it is working. A reusable ProcessPoolExecutor 23 / 26
  • 44. >>> from loky import get_reusable_executor >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(id, 42).result() 139655595838272 >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 0 >>> excutor.submit(CrashAtUnpickle()).result() Traceback (most recent call last): ... BrokenExecutorError >>> excutor = get_reusable_executor(max_workers=4) >>> print(excutor.executor_id) 1 >>> excutor.submit(id, 42).result() 139655595838272 Create a ProcessPoolExecutor using the factory function get_reusable_executor . The executor can be used exactly as ProcessPoolExecutor . When the factory is called elsewhere, reuse the same executor if it is working. When the executor is broken, automatically re-spawn a new one. A reusable ProcessPoolExecutor 23 / 26
  • 46. Conclusion Thread can be e cient to run multicore programs if your code releases the GIL. Else, you should use Process with spawn and try to reuse the pool of process as much as possible. loky can help you do that ;). Improves the management of a pool of workers in projects such as joblib . 25 / 26
  • 47. Thanks for your attention! Slides available at tommoral.github.io/pyparis17/ More on the GIL by Dave Beazley : dabeaz.com/python/GIL.pdf Loky project : github.com/tommoral/loky @tomamoral 26 / 26