Task Parallelism and Data Parallelism Thread Pools
Task Parallelism and Data Parallelism Thread Pools
Parallel computing environments often involve distributing code across multiple processors for efficient execution. Two common paradigms of parallelization are task parallelism and data parallelism.
Task parallelism focuses on distributing encapsulated tasks that can execute the same or different code on the same or different data across different processors. On the other hand, data parallelism involves performing the same operations on different subsets of the same data on multiple processors.
Both task parallelism and data parallelism can be implemented using thread pools. This article explores simple implementations of thread pools for task parallelism and data parallelism scenarios.
Task Parallelism Thread Pool
The following code demonstrates a simple thread pool for task parallelism. It utilizes a task queue to distribute tasks, which are represented as Callable[[], None]
objects (callables accepting no parameters and returning None) across multiple threads. Each thread continuously fetches a task from the task queue and executes it. If an exception occurs during task execution, a traceback is printed, and a new task is obtained from the task queue.
1 |
|
To use this task parallelism thread pool, provide it with a collection of tasks (represented as Callable[[], None]
objects) and the desired number of threads. The tasks will be executed in parallel by the thread pool until all tasks have finished.
Data Parallelism Thread Pool
The following code showcases a simple thread pool for data parallelism. It distributes data as argument tuples across multiple threads using an argument tuple queue. Each thread is assigned an operation created using an operation factory. After executing an operation on an argument tuple, the resulting return value is passed to a return value callback.
As with the task parallelism thread pool, all threads in the thread pool are always busy by getting a new argument tuple whenever its operation finishes execution on a previous argument tuple. Should an exception be raised when executing an operation on an argument tuple, a traceback is printed, and a new argument tuple is taken from the shared argument tuple queue.
1 |
|
To utilize the data parallelism thread pool, provide an operation factory, a collection of argument tuples, and a return value callback. The operation factory, when called, creates operations represented as Callable[[...], Any]
objects, accepting arguments from an argument tuple and returning a value. Each thread in the thread pool will execute these operations on the provided argument tuples. Any return values will be passed to the return value callback, which can be customized according to your needs. The data parallelism thread pool will process the argument tuples in parallel until all tuples have been processed.
Example
Say that we want to sleep for \(0, 1, 2, \dots, N - 1\) seconds before printing the number of seconds slept in parallel, where \(N\) is the number of threads in our thread pool.
We can adopt a task parallelism approach, where we create tasks which encapsulate how long they sleep, and add them to a task parallelism thread pool:
1 |
|
As an alternative, we can also use a data parallelism approach, in which operations accept the number of seconds they sleep from argument tuples, sleep for those time durations, and return those time durations before return value callbacks operate on the return values and print those time durations:
1 |
|
Running both thread pools takes the same time and produces the same output.
References
- https://en.m.wikipedia.org/wiki/Task_parallelism
- https://en.wikipedia.org/wiki/Data_parallelism