2017-05-10 41 views
7

Mam projekt obliczeniowy z dużym wykorzystaniem funkcji dziennika (dla liczb całkowitych), miliardy połączeń. Uważam, że wydajność dziennika numpy jest zaskakująco powolna.Co dzieje się w funkcji rejestru numpy? Czy są sposoby na poprawę wydajności?

Następujący kod ma 15 do 17 sek do zakończenia:

import numpy as np 
import time 

t1 = time.time() 
for i in range(1,10000000): 
    np.log(i) 
t2 = time.time() 
print(t2 - t1) 

Jednakże funkcja Math.log zajmuje znacznie mniej czasu od 3 do 4 sekund.

import math 
import time 

t1 = time.time() 
for i in range(1,10000000): 
    math.log(i) 
t2 = time.time() 
print(t2 - t1) 

Testowałem również Matlab i C#, co zajmuje odpowiednio około 2 s i tylko 0,3 s.

Matlab

tic 
for i = 1:10000000 
    log(i); 
end 
toc 

C#

var t = DateTime.Now; 
for (int i = 1; i < 10000000; ++i) 
    Math.Log(i); 
Console.WriteLine((DateTime.Now - t).TotalSeconds); 

Czy istnieje sposób w Pythonie, które mogą poprawić wydajność funkcji logarytmicznej?

+0

Jestem dość zainteresowany odpowiedzią. Specjalnie dlatego, że właśnie wszedłem w kod źródłowy, a funkcja jest trudna do prześledzenia. Po lewej stronie znajduje się kod źródłowy, napisany: '# prawdziwy podpis nieznany; przywrócono z __doc__'. Czy ktoś może również wyjaśnić, w jaki sposób działa kod źródłowy? –

+1

Odpowiedź brzmi: http://stackoverflow.com/questions/3650194/are-numpys-math-functions-faster-than-pythons?rq=1 – Bathsheba

+9

'np.log' jest zoptymalizowany do działania na tablicach wartości, a nie pojedyncze wartości. Na przykład 'np.log (np.arange (1 10000000))' (log tablicy liczb całkowitych w tym zakresie) zajmuje dla mnie około 120ms. –

Odpowiedz

2

Funkcje NumPys są zaprojektowane dla tablic nie dla pojedynczych wartości lub skalarów. Mają one dość wysokie koszty ogólne, ponieważ wykonują kilka testów i konwersji, które zapewnią dużą szybkość dla dużych macierzy, ale są one kosztowne dla skalarów.

Konwersja jest bardzo oczywiste, sprawdzając typ powrotu:

>>> import numpy as np 
>>> import math 

>>> type(np.log(2.)) 
numpy.float64 
>>> type(math.log(2.)) 
float 

Z drugiej strony math -module jest zoptymalizowany dla skalarów. Więc nie potrzebują wielu kontroli (myślę, że są tylko dwa: Konwertuj na float i sprawdź, czy jest to <= 0). Dlatego też math.log jest szybszy dla skalarów w porównaniu do numpy.log.

Ale jeśli operujesz na tablicach i chcesz wziąć logarytm wszystkich elementów w tablicy NumPy może być znacznie szybszy. Na moim komputerze, jeśli limit czasu na wykonanie np.log na tablicy porównaniu math.log każdej pozycji na liście, a następnie czas wygląda inaczej:

arr = np.arange(1, 10000000) 
%timeit np.log(arr) 
201 ms ± 959 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) 

lst = arr.tolist() 
%timeit [math.log(item) for item in lst] 
8.77 s ± 63.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

Więc np.log będzie o wiele rzędów wielkości szybciej na macierzach (to więcej niż 40 razy szybciej w tym przypadku)! I nie musisz sam pisać żadnej pętli. Ponieważ ufunc np.log będzie poprawnie działał na wielowymiarowych tablicach numpy, a także pozwala na wykonanie operacji w miejscu.

Zasada: jeśli macie tablicę z tysiącami przedmiotów, NumPy będzie szybsze, jeśli macie skalary lub tylko kilkadziesiąt pozycji: math + wyraźna pętla będzie szybsza.


Również nie używaj kodu time dla kodu czasu. podczas taktowania Są dedykowane moduły, które dają bardziej dokładne wyniki, lepsze statystyki i wyłączyć zbieranie śmieci:

ja zazwyczaj używają %timeit który jest wygodnym otokiem wokół funkcji timeit, ale wymaga IPython. Już wygodnie wyświetlają średnią wyników i odchylenie oraz wykonują niektóre (w większości) użyteczne statystyki, takie jak wyświetlanie wyniku "najlepszego z 7" lub "najlepszego z 3".


Niedawno analizowano zachowanie w czasie wykonywania funkcji NumPy dla another question niektóre punkty zastosowanie również tutaj.

1

Co ciekawe, problem powolnej biblioteki języka Python nie jest replikowany na moim komputerze (Windows 10, działający w Pythonie 2.7.11 i numpy 1.11.0).

>>> t1 = time.time() 
>>> for i in range(1,10000000): 
>>>  _ = np.log(i) 
>>> t2 = time.time() 
>>> print(t2 - t1) 
9.86099982262 
>>> t1 = time.time() 
>>> for i in range(1,10000000): 
>>>  _ = math.log(i) 
>>> t2 = time.time() 
>>> print(t2 - t1) 
2.48300004005 

Podobny do twojego wykonania w Matlabie. @Nils podnosi jednak sens, numpy ma na celu wydajne działanie na tablicach.

>>> t1 = time.time() 
>>> for i in range(1,1000): 
>>>  _ = np.log(np.arange(1,10000)) 
>>> t2 = time.time() 
>>> print(t2 - t1) 
0.146000146866 
>>> t1 = time.time() 
>>> for i in range(1,1000): 
>>>  _ = [math.log(i) for i in range(1,10000)] 
>>> t2 = time.time() 
>>> print(t2 - t1) 
2.3220000267 

Jeśli możesz wektoryzować swoje dane wejściowe, numpy przewyższy standardową bibliotekę matematyczną, a nawet zbliży się do C#.