并行化Numpy向量操作

use*_*412 43 python numpy multiprocessing numexpr

让我们用,例如, numpy.sin()

以下代码将返回数组的每个值的正弦值a:

import numpy
a = numpy.arange( 1000000 )
result = numpy.sin( a )
Run Code Online (Sandbox Code Playgroud)

但我的机器有32个核心,所以我想利用它们.(对于类似的东西,开销可能不值得,numpy.sin()但我实际想要使用的功能要复杂得多,而且我将使用大量数据.)

这是最好的(阅读:最聪明或最快)方法:

from multiprocessing import Pool
if __name__ == '__main__':
    pool = Pool()
    result = pool.map( numpy.sin, a )
Run Code Online (Sandbox Code Playgroud)

或者有更好的方法吗?

jor*_*eca 59

一个更好的办法:numexpr

从他们的主页略微改写:

它是一个用C语言编写的多线程VM,它可以分析表达式,更有效地重写它们,并将它们编译成代码,使代码能够接近内存和CPU限制操作的最佳并行性能.

例如,在我的4核机器中,评估正弦值比numpy略快不到4倍.

In [1]: import numpy as np
In [2]: import numexpr as ne
In [3]: a = np.arange(1000000)
In [4]: timeit ne.evaluate('sin(a)')
100 loops, best of 3: 15.6 ms per loop    
In [5]: timeit np.sin(a)
10 loops, best of 3: 54 ms per loop
Run Code Online (Sandbox Code Playgroud)

文档,包括支持的功能在这里.您必须检查或提供更多信息,以确定是否可以通过numexpr评估更复杂的功能.

  • 我编写了使用numexpr的代码,它比使用numpy的相同代码快了大约6倍.非常感谢您的建议!现在我想知道为什么numexpr不是更普遍.在我用Python搜索数字包的过程中,直到现在我还没有遇到它.在mathxpr中也有一些小麻烦不支持数组索引,但这几乎不是一个挫折. (5认同)
  • 我的速度提高了 30 倍!惊人的 :) (3认同)
  • 为什么不能更广泛地使用numexpr的一个原因是我认为使用它比纯NumPy更麻烦(如上例所示).尽管如此,轻松加速NumPy计算确实比运行速度更快. (2认同)

Sam*_*lar 20

如果您运行以下命令,这是一个有趣的注意事项:

import numpy
from multiprocessing import Pool
a = numpy.arange(1000000)    
pool = Pool(processes = 5)
result = pool.map(numpy.sin, a)

UnpicklingError: NEWOBJ class argument has NULL tp_new
Run Code Online (Sandbox Code Playgroud)

没想到,所以最新情况如下:

>>> help(numpy.sin)
   Help on ufunc object:

sin = class ufunc(__builtin__.object)
 |  Functions that operate element by element on whole arrays.
 |  
 |  To see the documentation for a specific ufunc, use np.info().  For
 |  example, np.info(np.sin).  Because ufuncs are written in C
 |  (for speed) and linked into Python with NumPy's ufunc facility,
 |  Python's help() function finds this page whenever help() is called
 |  on a ufunc.
Run Code Online (Sandbox Code Playgroud)

yep numpy.sin在c中实现,因此你无法直接在多处理中使用它.

所以我们必须用另一个函数包装它

PERF:

import time
import numpy
from multiprocessing import Pool

def numpy_sin(value):
    return numpy.sin(value)

a = numpy.arange(1000000)
pool = Pool(processes = 5)

start = time.time()
result = numpy.sin(a)
end = time.time()
print 'Singled threaded %f' % (end - start)
start = time.time()
result = pool.map(numpy_sin, a)
pool.close()
pool.join()
end = time.time()
print 'Multithreaded %f' % (end - start)


$ python perf.py 
Singled threaded 0.032201
Multithreaded 10.550432
Run Code Online (Sandbox Code Playgroud)

哇,也没想到,对于初学者我们正在使用python函数的几个问题,即使它只是一个包装器与纯c函数,还有复制值的开销,默认情况下多处理没有'共享数据,因此每个值需要来回复制.

请注意,如果正确分割我们的数据:

import time
import numpy
from multiprocessing import Pool

def numpy_sin(value):
    return numpy.sin(value)

a = [numpy.arange(100000) for _ in xrange(10)]
pool = Pool(processes = 5)

start = time.time()
result = numpy.sin(a)
end = time.time()
print 'Singled threaded %f' % (end - start)
start = time.time()
result = pool.map(numpy_sin, a)
pool.close()
pool.join()
end = time.time()
print 'Multithreaded %f' % (end - start)

$ python perf.py 
Singled threaded 0.150192
Multithreaded 0.055083
Run Code Online (Sandbox Code Playgroud)

那么我们可以从中得到什么,多处理是很好的但我们应该总是测试和比较它有时它更快,有时它更慢,取决于它的使用方式......

numpy.sin虽然您没有使用其他功能,但我建议您首先验证多处理确实会加速计算,也许前后复制值的开销可能会影响您.

无论哪种方式,我也相信使用pool.map是最好,最安全的多线程代码方法...

我希望这有帮助.


ent*_*opy 6

SciPy在这里有关于这个主题的非常好的写作:http://wiki.scipy.org/ParallelProgramming