Multiprocessing Pool Best Practices in Python – SuperFastPython
Multiprocessing Pool Best Practices in Python
August 12, 2022 Python Multiprocessing Pool
It is important to follow best practices when using the multiprocessing.Pool in Python .
Best practices allow you to side-step the most common errors and bugs when using processes to execute ad hoc tasks in your programs.
In this tutorial you will discover the best practices when using process pools in Python .
Let's get started.
Multiprocessing Pool Best Practices
The multiprocessing pool is a flexible and powerful process pool for executing ad hoc tasks in a synchronous or asynchronous manner.
Once you know how the multiprocessing pool works, it is important to review some best practices to consider when bringing process pools into our Python programs.
To keep things simple, there are 6 best practices when using the multiprocessing pool, they are:
Use the Context Manager<br>Use map() for Parallel For-Loops<br>Use imap_unordered() For Responsive Code<br>Use map_async() to Issue Tasks Asynchronously<br>Use Independent Functions as Tasks<br>Use for CPU-Bound Tasks (probably)
Let's get started with the first practice, which is to use the context manager.
Use the Context Manager
Use the context manager when using the multiprocessing pool to ensure the pool is always closed correctly.
For example:
...<br># create a process pool via the context manager<br>with Pool(4) as pool:<br># ...
Remember to configure your multiprocessing pool when creating it in the context manager, specifically by setting the number of child process workers to use in the pool.
Using the context manager avoids the situation where you have explicitly instantiated the process pool and forget to shut it down manually by calling close() or terminate() .
It is also less code and better grouped than managing instantiation and shutdown manually, for example:
...<br># create a process pool manually<br>pool = Pool(4)<br># ...<br>pool.close()<br>Don't use the context manager when you need to dispatch tasks and get results over a broader context (e.g. multiple functions) and/or when you have more control over the shutdown of the pool.
You can learn more about how to use the multiprocessing pool context manager in the tutorial:
Multiprocessing Pool Context Manager
Use map() for Parallel For-Loops
If you have a for-loop that applies a function to each item in a list or iterable, then use the map() function to dispatch all tasks and process results once all tasks are completed.
For example, you may have a for-loop over a list that calls task() for each item:
...<br># apply a function to each item in an iterable<br>for item in mylist:<br>result = task(item)<br># do something...
Or, you may already be using the built-in map() function:
...<br># apply a function to each item in an iterable<br>for result in map(task, mylist):<br># do something...
Both of these cases can be made parallel using the map() function on the process pool.
...<br># apply a function to each item in a iterable in parallel<br>for result in pool.map(task, mylist):<br># do something...
Probably do not use the map() function if your target task function has side effects.
Do not use the map() function if your target task function has no arguments or more than one argument. If you have multiple arguments, you can use the starmap() function instead.
Do not use the map() function if you need control over exception handling for each task, or if you would like to get results to tasks in the order that tasks are completed.
Do not use the map() function if you have many tasks (e.g. hundreds or thousands) as all tasks will be dispatched at once. Instead, consider the more lazy imap() function.
You can learn more about the parallel version of map() with the multiprocessing pool in the tutorial:
Multiprocessing Pool.map() in Python
Use imap_unordered() For Responsive Code
If you would like to process results in the order that tasks are completed, rather than the order that tasks are submitted, then use imap_unordered() function.
Unlike the Pool.map() function, the Pool.imap_unordered() function will iterate the provided iterable one item at a time and issue tasks to the process pool.
Unlike the Pool.imap() function, the Pool.imap_unordered() function will yield return values in the order that tasks are completed, not the order that tasks were issued to the process pool.
This allows the caller to process results from issued tasks as they become available, making the program more responsive.
For example:
...<br># apply function to each item in the iterable in parallel<br>for result in pool.imap_unordered(task, items):<br># ...
Do not use the imap_unordered() function if you need to process the results in the order that the tasks were submitted to the process pool, instead, use map() function.
Do not use the imap_unordered() function if you need results from all tasks before continuing on in the program, instead, you may be better off using map_async() and the...