在上一篇中我们介绍了在 cython 中使用 mpi4py 的方法,下面我们将介绍 mpi4py 与 OpenMP 混合编程。
OpenMP 简介
OpenMP (Open Multi-Processing) 是一个跨平台的多线程实现,它本身不是一种独立的并行语言,而是为多处理器上编写并行程序而设计的、指导共享内存多线程并行的编译制导指令和应用程序编程接口(API),可在 C/C++ 和 Fortran 中使用,一般是在这些编程语言的串行代码中以编译器可识别的注释形式出现。OpenMP 可以在大多数的处理器体系和操作系统中运行,包括 GNU/Linux, Mac OS X 和 Microsoft Windows 等。OpenMP 包括一套编译器指令、库和一些能够影响运行行为的环境变量。在使用 OpenMP 时,主线程(顺序的执行指令)生成一系列的子线程,并将任务划分给这些子线程进行执行。这些子线程并行的运行,由运行时环境将线程分配给不同的处理器。
OpenMP 采用可移植的、可扩展的模型,为程序员提供了一个简单而灵活的开发平台,及从标准桌面电脑到超级计算机的并行应用程序接口。
混合并行编程模型构建的应用程序可以同时使用 OpenMP 和 MPI。
OpenMP 的执行模式
OpenMP 的执行模型采用 fork-join 的形式,其中 fork 创建新线程或者唤醒已有线程;join 即多线程的会合。fork-join 执行模型在刚开始执行的时候,只有一个称为“主线程”的运行线程存在。主线程在运行过程中,当遇到需要进行并行计算的时候,派生出线程来执行并行任务。在并行执行的时候,主线程和派生线程共同工作。在并行代码执行结束后,派生线程退出或者阻塞,不再工作,控制流程回到单独的主线程中。
OpenMP 编程要素
OpenMP 编程模型以线程为基础,通过编译制导指令来显式地指导并行化,OpenMP 提供了三种编程要素来实现对并行化的完善控制,它们是:编译制导、API 函数集和环境变量。
在 C/C++ 和 Fortran 中使用 OpenMP 时需要对 OpenMP 的相关知识足够了解和熟悉,读者可以参考 OpenMP 的相关文档和资料,这里不做进一步的介绍。
在 cython 中使用 OpenMP
不能直接在 Python 中使用 OpenMP,不过幸运的是 cython 支持 OpenMP,而我们可以方便而且容易地用 cython 编写 Python 的扩展模块,或者将 C/C++ 的代码包装成 Python 的扩展模块,因此也就可以间接地在使用 OpenMP 的多线程加速了。
cython 通过其 parallel 模块支持本地并行化,目前该模块支持和使用 OpenMP 的多线程并行方案,今后还可能会支持其它并行机制。要使用 cython 的 parallel 模块以实现并行加速,需要释放 Python 的 GIL (Global Interpreter Lock)。
相关函数
下面是 cython.paralle 中的主要函数:
cython.parallel.prange([start,] stop[, step][, nogil=False][, schedule=None[, chunksize=None]][, num_threads=None])
用于并行的 for 循环,只能用在 Python GIL 被释放的情况下。OpenMP 会自动启动一个线程池并将计算任务按照指定的 schedule
分配给线程池中的线程去完成。在 prange 块下面赋值的变量是最后私有的(lastprivate),即该变量会获得其最后一次迭代的值。如果使用 inplace 算符(如 +=, *= 等)为 prange 块中的变量赋值,则该操作会变成规约运算,即在循环完成后,每个线程本地的该变量副本的值会使用该算符执行规约运算并将运算的结果赋值给此变量。prange 的循环变量总是最后私有的。其参数的意义如下:
- start:循环变量的起始值。
- end:循环变量的结束值。
- step:循环步长,不能为 0.
- nogil:如果为 True,整个 prange 循环会被包裹在一个 nogil 代码段中,否则需要手动将该循环放入 nogil 块下。
- schedule:任务分配给线程的调度方式,会传递给 OpemMP,可用的方式有:
- static:如果设置了
chunksize
,计算任务会被提前按照给定的chunksize
分配到所有线程上。如果没有设置chunksize
,则任务会被划分成近似相同大小的块并提前分配给各个线程。这种分配方式适合于任务能够划分成差不多大小的块并且这些块的执行时间差不多相同的情况。 - dynamic:任务会被以默认
chunksize
为 1 动态分配给任何空闲的线程。这种方式适合于不同的任务块执行时间不定且事先无法预知的情况。 - g
- static:如果设置了