OpenMP 和 Python

Posted

技术标签:

【中文标题】OpenMP 和 Python【英文标题】:OpenMP and Python 【发布时间】:2012-07-07 06:48:06 【问题描述】:

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

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

我的问题是:

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

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

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

C 扩展 无堆栈 Python

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

【问题讨论】:

【参考方案1】:

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

您可以在 Cython 中轻松编写使用多线程的 C 扩展,example。

【讨论】:

在 Python 中 ctypes 上的任何 cmets(常规)? @Inquest:ctypes 允许您在纯 Python 中从 C 库中调用几个函数 是的,我知道这一点。我正在向你们的 cmets 征求关于在 Python 中使用 ctypes 进行并行编程的可行性。 @Inquest ctypes 只有在基本上所有工作都在 C 函数中完成的情况下才真正有助于并行化,并且您的 python 只是调用它们的包装器。这并不是真正的“Python 中的并行编程”,但它有时很方便。 ctypes 让您可以访问 C 中的共享内存。因此它对于并行编程非常有用,因为如果您有需要在进程之间共享的大数据,它可以显着提高速度。 multiprocessing 和其他 python 模块具有通过ctypes利用共享内存的方法【参考方案2】:

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

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

其他开始寻找的地方:

Experiences Making CPU Bound Tasks Much Faster Speeding up Python (NumPy, Cython and Weave)

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

【讨论】:

所以你甚至不能编译openmp c代码并从python调用二进制文件?为什么这不起作用 @MySchizoBuddy - 是的,您可以编写 OpenMP C 代码,编译并从 Python 调用它。这在 GIL 的范围之外执行,因为它不是由 Python 引擎执行的。但是,在 Python 中没有 直接 方式使用 OpenMP。 是 ipython 中使用的“map reduce”,是一种可接受的替代方案,尽管它主要是为大数据设计的 map reduce 是一种函数式编程概念,您可以在其中获取数据并将每个数据映射到一个函数,然后对函数的结果进行归约(例如求和它们的答案)。 MapReduce 大数据范式将数据映射到对这些数据执行某些操作的多个工作进程。然后减少数据以提供答案。 MapReduce 最大的优势在于它的约束简化了代码并行化的过程。挑战在于分解您的问题以适应 MapReduce 约束。如果 iPython 有一个分布式 MapReduce,您可以从中获得并行性,但它需要工作。【参考方案3】:

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

【讨论】:

【参考方案4】:

也许您的回复是在 Cython 中:

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

【讨论】:

带有 openmp 的 cython 在多核 cpu 上非常棒,与单线程相比,我在 8 核机器上获得了 700% 的加速【参考方案5】:

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

【讨论】:

【参考方案6】:

赛通

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

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

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

麻木

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

Pythran

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

PyPy

​​>

JIT Python 编译器 PyPy 支持多处理模块(见下文),并有一个名为 PyPy-STM "a special in-development version of PyPy which can run multiple independent CPU-hungry threads in the same process in parallel" 的项目。

旁注:多处理

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

【讨论】:

这是最完整的答案。谢谢! @boardrider ,你能更新一下这个答案吗?这会很棒,因为它确实具有解释性和广泛性。谢谢【参考方案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))

【讨论】:

以上是关于OpenMP 和 Python的主要内容,如果未能解决你的问题,请参考以下文章

结合 OpenMP 和 OpenCL

MacOS、CMake 和 OpenMP

openacc 与 openmp 和 mpi 的区别?

OpenMP 和 MPI 混合程序

混合 C++11 原子和 OpenMP

OpenMP用法大全