具有numpy数组和共享内存的并行python循环

发布于 2021-01-29 15:09:24

我知道有关此主题的几个问题和答案,但尚未找到对此特定问题的满意答案:

什么是对python循环进行简单的共享内存并行化的最简单方法,在该循环中,通过numpy / scipy函数操作numpy数组?

我并不是在寻找最有效的方法,我只是想实现一些简单的实现,当循环不并行运行时,不需要大量重写。就像OpenMP以较低级别的语言实现一样。

我在这方面看到的最好的答案就是这个,但这是一种很笨拙的方法,它要求一个将循环表达为一个带有单个参数的函数,几行共享数组转换为crud,似乎要求从调用了并行函数__main__,并且在交互式提示(我花了很多时间)上,它似乎不能很好地工作。

借助Python的所有简单性,这真的是使循环并行化的最佳方法吗?真?这对于以OpenMP方式并行化来说是微不足道的。

我辛苦地阅读了多处理模块的不透明文档,却发现它是如此通用,以至于它似乎适用于除简单循环并行化之外的所有内容。我对设置管理器,代理,管道等不感兴趣。我只有一个简单的循环,完全并行,在任务之间没有任何通信。使用MPI并行化这种简单情况似乎有点过头了,更不用说在这种情况下内存效率低下了。

我没有时间去学习大量不同的Python共享内存并行程序包,但是想知道是否有人对此有更多的经验并且可以向我展示一种更简单的方法。请不要建议使用Cython等串行优化技术(我已经使用过),也不要建议使用诸如BLAS的并行numpy
/ scipy函数(我的情况更普遍,更并行)。

关注者
0
被浏览
88
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    使用Cython并行支持:

    # asd.pyx
    from cython.parallel cimport prange
    
    import numpy as np
    
    def foo():
        cdef int i, j, n
    
        x = np.zeros((200, 2000), float)
    
        n = x.shape[0]
        for i in prange(n, nogil=True):
            with gil:
                for j in range(100):
                    x[i,:] = np.cos(x[i,:])
    
        return x
    

    在2核计算机上:

    $ cython asd.pyx
    $ gcc -fPIC -fopenmp -shared -o asd.so asd.c -I/usr/include/python2.7
    $ export OMP_NUM_THREADS=1
    $ time python -c 'import asd; asd.foo()'
    real    0m1.548s
    user    0m1.442s
    sys 0m0.061s
    
    $ export OMP_NUM_THREADS=2
    $ time python -c 'import asd; asd.foo()'
    real    0m0.602s
    user    0m0.826s
    sys 0m0.075s
    

    由于np.cos(像其他ufuncs一样)释放了GIL ,因此这可以并行运行。

    如果要交互使用此功能:

    # asd.pyxbdl
    def make_ext(modname, pyxfilename):
        from distutils.extension import Extension
        return Extension(name=modname,
                         sources=[pyxfilename],
                         extra_link_args=['-fopenmp'],
                         extra_compile_args=['-fopenmp'])
    

    和(删除asd.soasd.c首先):

    >>> import pyximport
    >>> pyximport.install(reload_support=True)
    >>> import asd
    >>> q1 = asd.foo()
    # Go to an editor and change asd.pyx
    >>> reload(asd)
    >>> q2 = asd.foo()
    

    所以是的,在某些情况下,您可以仅使用线程来并行化。OpenMP只是线程的一个高级包装器,因此,此处只需要Cython即可获得更简单的语法。没有Cython,您可以使用threading模块
    —与多处理类似(并且可能更健壮),但是您无需执行任何特殊操作即可将数组声明为共享内存。

    但是,并非所有操作都会释放GIL,因此YMMV会提高性能。

    ***
    

    从其他Stackoverflow答案中刮取的另一个可能有用的链接—
    另一个指向多处理的接口:http :
    //packages.python.org/joblib/parallel.html



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看