使用Cython并行支持:
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
在一台双核机器上:
$ 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
(像其他ufunc一样)释放了全局解释器锁,因此此代码可以并行运行。
如果您想要交互式使用:
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.so
和asd.c
):
>>> import pyximport
>>> pyximport.install(reload_support=True)
>>> import asd
>>> q1 = asd.foo()
>>> reload(asd)
>>> q2 = asd.foo()
所以,在某些情况下,您可以仅使用线程并行化。 OpenMP只是线程的高级封装,因此Cython仅在这里需要更简单的语法。如果没有Cython,您可以使用threading
模块 --- 它的工作方式与多进程类似(可能更加稳健),但您无需特别声明数组为共享内存。
然而,并非所有操作都会释放GIL,因此性能因人而异。
***
这是从其他Stackoverflow答案中爬取的另一个可能有用的链接 --- 另一个与多进程交互的接口:http://packages.python.org/joblib/parallel.html
prange
替换range
):http://docs.cython.org/src/userguide/parallelism.html - pv.