OpenMP and Python

Cython

Cython has OpenMP support: With Cython, OpenMP can be added by using the prange (parallel range) operator and adding the -fopenmp compiler directive to setup.py.

When working in a prange stanza, execution is performed in parallel because we disable the global interpreter lock (GIL) by using the with nogil: to specify the block where the GIL is disabled.

To compile cython_np.pyx we have to modify the setup.py script as shown below. We tell it to inform the C compiler to use -fopenmp as an argument during compilation – to enable OpenMP and to link with the OpenMP libraries.

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
setup(
    cmdclass = {"build_ext": build_ext},
    ext_modules = [
        Extension(
            "calculate",
            ["cython_np.pyx"],
            extra_compile_args = ["-fopenmp"],
            extra_link_args = ["-fopenmp"]
        )
    ]
)

With Cython’s prange, we can choose different scheduling approaches. With static, the workload is distributed evenly across the available CPUs. However, as some of your calculation regions are expensive in time, while others are cheap – if we ask Cython to schedule the work chunks equally using static across the CPUs, then the results for some regions will complete faster than others and those threads will then sit idle.
Both the dynamic and guided schedule options attempt to mitigate this problem by allocating work in smaller chunks dynamically at runtime so that the CPUs are more evenly distributed when the workload’s calculation time is variable. Thus, for your code, the correct choice will vary depending on the nature of your workload.

Numba

Numba’s premium version, NumbaPro, has experimental support of a prange parallelization operator for working with OpenMP.

Pythran

Pythran (a Python-to-C++ compiler for a subset of Python) can take advantage of vectorization possibilities and of OpenMP-based parallelization possibilities, though it runs using Python 2.7 only. You specify parallel sections using pragma omp directives (very similarly to Cython’s OpenMP support described above), e.g.:

pragma omp

PyPy

The JIT Python compiler PyPy supports the multiprocessing module (see following) and has a project called PyPy-STM “a special in-development version of PyPy which can run multiple independent CPU-hungry threads in the same process in parallel“.

Side note: multiprocessing

OpenMP is a low-level interface to multiple cores. You may want to look at multiprocessing. The multiprocessing module works at a higher level, sharing Python data structures, while OpenMP works with C primitive objects (e.g., integers and floats) once you’ve compiled to C. It only makes sense to use OpenMP if you’re compiling your code; if you’re not compiling (e.g., if you’re using efficient numpy code and you want to run on many cores), then sticking with multiprocessing is probably the right approach.

Leave a Comment