2012-07-11 14 views
36
korzystanie

Powiedzmy, na przykład, numpy.sin()parallelizing operację wektorowego w NumPy

Poniższy kod zwróci wartość sinusa dla każdej wartości tablicy a:

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

Ale moja maszyna ma 32 rdzeni , więc chciałbym z nich skorzystać. (Obciążenie może nie być opłacalne dla czegoś takiego jak numpy.sin(), ale funkcja, której naprawdę chcę użyć, jest nieco bardziej skomplikowana i będę pracować z ogromną ilością danych.)

Czy to jest najlepsze (czytaj: najmądrzejsza lub najszybsza) metoda:

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

czy jest lepszy sposób na zrobienie tego?

+0

Jeśli zamierzasz użyć 'pool.map()', powinieneś użyć 'math.sin', ponieważ jest on szybszy niż' numpy.sin'. Odniesienia: http://stackoverflow.com/questions/3650194/are-numpys-math-functions-faster-than-pythons. – EOL

+2

Dla 'numpy.sin', [oficjalne numpy/scipy wiki] (http://wiki.scipy.org/ParallelProgramming) mówi, że powinien działać równolegle, jeśli [kompilujesz numpy z włączonym openmp] (https: // software.intel.com/en-us/articles/numpyscipy-with-intel-mkl). – ziyuang

+0

Można również użyć [Bohrium] (http://bohrium.readthedocs.io/): Powinno być tak proste, jak zamiana pierwszej linii na 'import bohrium jako numpy' ... – j08lue

Odpowiedz

48

Nie jest lepszy sposób: numexpr

nieznacznie przeredagowany z ich strony głównej:

Jest to wielowątkowy VM napisany w C, który analizuje wyrażeń, zmienia ich bardziej efektywnie i zestawia je na leci do kodu, który zbliża się do optymalnej wydajności równoległej zarówno dla operacji związanych z pamięcią, jak iz procesorem cpu.

Na przykład, w mojej 4 rdzeniowej maszynie, ocena sinusa jest nieco mniej niż 4 razy szybsza niż numpy.

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 

Dokumentacja, w tym obsługiwane funkcje here. Będziesz musiał sprawdzić lub podać nam więcej informacji, aby sprawdzić, czy twoja bardziej skomplikowana funkcja może być oceniona przez numexpr.

+4

Napisałem swój kod wykorzystując numexpr i działa on około 6 razy szybciej niż ten sam kod używając numpy. Wielkie dzięki za sugestię! Teraz zastanawiam się, dlaczego numexpr nie jest bardziej rozpowszechniony. We wszystkich moich poszukiwaniach pakietów liczbowych w Pythonie, nie natknąłem się na to do tej pory. W numexpr nie było też pewności, że nie obsługuje on indeksowania tablic, ale nie było to porażką. – user1475412

+1

Może powinieneś również sprawdzić Theano i Cythona. Theano może używać GPU, ale tak naprawdę nie używałem go, więc nie mogę podać przykładu. – jorgeca

+2

Jednym z powodów, dla których numexpr nie jest bardziej rozpowszechniony, jest to, że jest on bardziej nieporęczny w użyciu niż czysty NumPy (jak w powyższym przykładzie). Rzeczywiście świetnie nadaje się do łatwego przyspieszania obliczeń NumPy niż do szybszego uruchamiania. – EOL

17

dobrze jest to rodzaj interesująca uwaga, jeśli uruchomić następujące polecenia:

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 

nie spodziewałem się, że tak Siema dzieje się, dobrze:

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

yep numpy.sin jest zaimplementowany w c, dlatego nie można go używać bezpośrednio w trybie wieloprocesorowym.

więc musimy owinąć go z innej funkcji

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 

wow, nie spodziewałem się, że obaj, dobrze tam kilka problemów na początek używamy funkcji Pythona nawet jeśli jest to tylko funkcja otoki a funkcja czystego c, a także narzut kopiowania wartości, to proces wieloprocesowy domyślnie nie współużytkuje danych, jako że każda wartość musi być kopiowana w przód/w tył.

należy pamiętać, że jeśli prawidłowo Segment nasze dane:

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 

Więc co możemy z tym wieloprocesorowe jest wielki, ale zawsze powinniśmy przetestować i porównać go czasami szybszym a czasami także jej wolniej, w zależności od sposobu jej używany ...

Zgoda, że ​​nie używasz numpy.sin, ale inną funkcją, którą poleciłbym ci najpierw sprawdzić, czy rzeczywiście proces wieloprocesowy przyspieszy obliczenia, może narzut kopiowania wartości w przód/w tył może wpłynąć na ciebie.

Tak czy inaczej ja też nie uwierzyć, że za pomocą pool.map jest najlepsza, najbezpieczniejsza metoda kodu wielowątkowość ...

Mam nadzieję, że to pomaga.

+0

Wielkie dzięki! Jest to bardzo pouczające. Na podstawie tego, co przeczytałem, założyłem, że funkcja 'map'' '' '' '' 'działa trochę w sposób inteligentny na danych, ale myślę, że segmentowanie jej w pierwszej kolejności robi ogromną różnicę. Czy jest jakiś inny sposób na uniknięcie narzutu procesów kopiowania danych? Czy oczekujesz jakiejkolwiek różnicy wydajności, jeśli użyję zamiast tego 'math.sin()'? – user1475412

+0

Tak naprawdę wypróbowałem 'math.sin' i dobrze jego dużo wolniej, nawet wielowątkowe, a następnie single threaded numpy.sin, chociaż było szybsze (zajęło' 6.435199's niż wielowątkowe 'numpy.sin', które zajęło' 10,5', prawdopodobnie ze względu na to, że 'numpy.sin' może obsługiwać tablice, faceci' numpy' są naprawdę dobrzy w matematyce;), tak, jest sposób na użycie 'shared memory' http://docs.python.org/library/multiprocessing .html, ale proszę, nie używaj tego dość niebezpiecznego i ma ograniczone wsparcie lub przynajmniej ostrożnie stąpaj. –

+0

Jeśli twoja jedyna praca czyta, to może być bezpieczna, podproces tylko śledzić ich odpowiedni indeks lub podzbiór indeksów ... –

Powiązane problemy