58

たとえば、numpy.sin()

次のコードは、配列の各値の正弦の値を返しますa

import numpy
a = numpy.arange( 1000000 )
result = numpy.sin( a )

しかし、私のマシンには 32 コアがあるので、それらを活用したいと考えています。(オーバーヘッドは価値がないかもしれませんnumpy.sin()が、実際に使用したい関数はかなり複雑で、膨大な量のデータを扱うことになります。)

これは最善の方法ですか(読み取り:最もスマートまたは最速):

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

またはこれを行うためのより良い方法はありますか?

4

3 に答える 3

69

より良い方法があります: numexpr

彼らのメインページからわずかに言い直されました:

これは、C で記述されたマルチスレッド VM であり、式を分析し、より効率的に書き直し、オンザフライでコードにコンパイルし、メモリと CPU に制限された操作の両方で最適な並列パフォーマンスを実現します。

たとえば、私の 4 コア マシンでは、sine の評価は 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

サポートされている関数を含むドキュメントはこちら。より複雑な関数を numexpr で評価できるかどうかを確認するには、確認するか、詳細情報を提供する必要があります。

于 2012-07-12T20:28:37.763 に答える
26

次のコマンドを実行すると、これは興味深いメモになります。

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

期待していなかったので、何が起こっているのか:

>>> 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.

はい、numpy.sin は c で実装されているため、マルチプロセッシングで直接使用することはできません。

そのため、別の関数でラップする必要があります

パフォーマンス:

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

うわー、どちらも期待していませんでした。まあ、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

マルチプロセッシングは素晴らしいですが、使用方法に応じて、高速な場合と低速な場合があり、常にテストして比較する必要があります...

使用していないことを認めますがnumpy.sin、実際にマルチプロセッシングが計算を高速化することを最初に確認することをお勧めします。値を前後にコピーするオーバーヘッドが影響する可能性があります。

いずれにせよ、マルチスレッドコードの使用が最良で最も安全な方法であると私は信じています...pool.map

これが役立つことを願っています。

于 2012-07-11T23:17:22.527 に答える
11

SciPy には、実際にこの件に関してかなり良い記事があります: http://wiki.scipy.org/ParallelProgramming

編集: リンク切れ、現在は http://scipy-cookbook.readthedocs.io/items/ParallelProgramming.htmlにあります。

于 2012-07-11T22:13:51.043 に答える