80

我有为共享内存机器(在 C 和 FORTRAN 中)编写 OpenMP 的经验,以执行简单的任务,如矩阵加法、乘法等(只是想看看它如何与 LAPACK 竞争)。我对 OpenMP 的了解足以执行简单的任务,而无需查看文档。

最近,我为我的项目转向 Python,除了绝对基础之外,我没有任何 Python 经验。

我的问题是:

在 Python 中使用 OpenMP的最简单方法是什么?最简单的意思是在程序员方面花费最少的努力(即使它是以增加系统时间为代价的)?

我使用 OpenMP 的原因是因为可以将串行代码转换为工作并行代码,其中!$OMP散布一些 s。实现粗略并行化所需的时间非常短。有没有办法在 Python 中复制这个特性?

通过在 SO 上浏览,我可以找到:

  • C 扩展
  • 无堆栈 Python

还有更多吗?哪个最符合我的问题?

4

7 回答 7

47

赛通

Cython具有OpenMP支持:使用 Cython,可以通过使用prange(并行范围)运算符并将-fopenmp编译器指令添加到 setup.py 来添加 OpenMP。

在 prange 节中工作时,执行是并行执行的,因为我们通过使用指定禁用 GIL 的块来禁用全局解释器锁 (GIL) 。with nogil:

要编译cython_np.pyx,我们必须修改 setup.py 脚本,如下所示。我们告诉它通知 C 编译器-fopenmp在编译期间用作参数 - 以启用 OpenMP 并与 OpenMP 库链接。

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"]
        )
    ]
)

使用 Cython prange,,我们可以选择不同的调度方法。使用静态,工作负载均匀分布在可用的 CPU 上。但是,由于您的一些计算区域在时间上很昂贵,而另一些则很便宜 - 如果我们要求 Cython 在 CPU 上使用静态平均调度工作块,那么某些区域的结果将比其他区域更快地完成,然后这些线程将闲着。动态的引导调度选项试图通过在运行时动态地分配更小的块来缓解这个问题,以便在工作负载的计算时间可变时 CPU 分布更均匀。因此,对于您的代码,正确的选择将取决于您的工作负载的性质。

努巴

Numba 的高级版本 NumbaPro 具有对prange并行化运算符的实验性支持,可与 OpenMP 一起使用。

吡斯兰

Pythran(Python 子集的 Python 到 C++ 编译器)可以利用矢量化可能性和基于 OpenMP 的并行化可能性,尽管它仅使用 Python 2.7 运行。您可以使用指令指定并行部分pragma omp(非常类似于上述 Cython 的 OpenMP 支持),例如:

编译指令

派皮

JIT Python 编译器 PyPy 支持多处理模块(见下文),并有一个名为 PyPy-STM 的项目“PyPy的特殊开发版本,它可以在同一进程中并行运行多个独立的 CPU 密集型线程”。

旁注:多处理

OpenMP 是多核的低级接口。您可能想查看multiprocessing.multiprocessing模块在更高级别上工作,共享 Python 数据结构,而 OpenMP 在您编译为 C 后与 C 原始对象(例如整数和浮点数)一起工作。只有在您使用 OpenMP 时才有意义'正在编译你的代码;如果您不进行编译(例如,如果您使用高效的numpy代码并且希望在多个内核上运行),那么坚持使用multiprocessing可能是正确的方法。

于 2016-04-20T11:17:47.170 回答
35

由于 GIL,没有必要在 CPython 中将线程用于 CPU 密集型任务。您需要多处理(示例)或使用在计算期间释放 GIL 的 C 扩展,例如,一些 numpy 函数,示例

您可以在 Cython 中轻松编写使用多个线程的 C 扩展,例如.

于 2012-07-06T20:20:13.523 回答
17

据我所知,没有适用于 Python 的 OpenMP 包(我不知道如果有它会做什么)。如果您希望线程直接在您的控制之下,您将不得不使用其中一个线程库。然而,正如其他人所指出的,GIL(全局解释器锁)使得 Python 中的多线程性能有点……好吧,毫无意义*。GIL 意味着一次只有一个线程可以访问解释器。

我建议改为查看 NumPy/SciPy。NumPy 让您可以编写 Matlab 风格的代码,您可以在其中使用单个操作对数组和矩阵进行操作。它还具有一些并行处理能力,请参阅SciPy Wiki

其他开始寻找的地方:

* 好吧,这不是毫无意义的,但除非时间消耗在 Python 代码之外(例如通过调用的外部进程popen或类似的方法),否则线程不会为您购买任何东西,而不是方便。

于 2012-07-06T20:46:17.433 回答
12

如果你想发布 GIL 并使用 OpenMP ypu 可以看看 Cython。它为一些常见任务提供了简单的并行性。您可以在 Cython文档中阅读更多内容。

于 2012-10-25T23:35:54.637 回答
9

也许您的回复是在 Cython 中:

“Cython 通过 cython.parallel 模块支持本机并行性。要使用这种并行性,必须发布 GIL(请参阅发布 GIL)。它目前支持 OpenMP,但以后可能会支持更多后端。” Cython 文档

于 2013-01-05T02:29:43.013 回答
7

有一个名为pymp的包,作者将其描述为为 Python 带来类似 OpenMP 的功能的包。我尝试过使用它,但有不同的用例:文件处理。有效。我认为使用起来非常简单。以下是取自 GitHub 页面的示例:

import pymp
ex_array = pymp.shared.array((100,), dtype='uint8')
with pymp.Parallel(4) as p:
    for index in p.range(0, 100):
        ex_array[index] = 1
        # The parallel print function takes care of asynchronous output.
        p.print('Yay! {} done!'.format(index))
于 2018-01-31T15:24:31.427 回答
0

http://archive.euroscipy.org/talk/6857 “介绍了 Cython 的 OpenMP 能力,专注于 NumPy 数组上的并行循环。源代码示例演示了如何在 Python 中使用 OpenMP。使用 OpenMP 的并行算法的结果显示了可以实现哪些加速与其他并行化策略相比,实现了不同的数据大小。”

import numpy
import cython
from cython cimport parallel

@cython.boundscheck(False)
@cython.wraparound(False)
def func(object[double, ndim=2] buf1 not None,
        object[double, ndim=2] buf2 not None,
        object[double, ndim=2] output=None,
        int num_threads=2):
    cdef unsigned int x, y, inner, outer
    if buf1.shape != buf2.shape:
        raise TypeError('Arrays have different shapes: %s, %s' % (buf1.shape,
            buf2.shape))
    if output is None:
        output = numpy.empty_like(buf1)
    outer = buf1.shape[0]
    inner = buf1.shape[1]
    with nogil, cython.boundscheck(False), cython.wraparound(False):
        for x in parallel.prange(outer, schedule='static',
                num_threads=num_threads):
            for y in xrange(inner):
                output[x, y] = ((buf1[x, y] + buf2[x, y]) * 2 +
                    buf1[x, y] * buf2[x, y])
return output
于 2015-02-13T00:49:24.183 回答