Warum ist numpy's einsum schneller als die eingebauten Funktionen von numpy's?

Beginnen wir mit drei Arrays vondtype=np.double. Timings werden auf einer Intel-CPU mit NumPy 1.7.1 durchgeführt, das mit kompiliert wurdeicc und mit Intel verbundenmkl. Eine AMD CPU mit NumPy 1.6.1 kompiliert mitgcc ohnemkl wurde auch verwendet, um die Timings zu überprüfen. Bitte beachten Sie, dass die Timings nahezu linear mit der Systemgröße skalieren und nicht auf den geringen Overhead zurückzuführen sind, der bei den Numpy-Funktionen entstehtif Aussagen, bei denen dieser Unterschied in Mikrosekunden und nicht in Millisekunden angezeigt wird:

arr_1D=np.arange(500,dtype=np.double)
large_arr_1D=np.arange(100000,dtype=np.double)
arr_2D=np.arange(500**2,dtype=np.double).reshape(500,500)
arr_3D=np.arange(500**3,dtype=np.double).reshape(500,500,500)

Zuerst schauen wir uns die annp.sum Funktion:

np.all(np.sum(arr_3D)==np.einsum('ijk->',arr_3D))
True

%timeit np.sum(arr_3D)
10 loops, best of 3: 142 ms per loop

%timeit np.einsum('ijk->', arr_3D)
10 loops, best of 3: 70.2 ms per loop

Befugnisse:

np.allclose(arr_3D*arr_3D*arr_3D,np.einsum('ijk,ijk,ijk->ijk',arr_3D,arr_3D,arr_3D))
True

%timeit arr_3D*arr_3D*arr_3D
1 loops, best of 3: 1.32 s per loop

%timeit np.einsum('ijk,ijk,ijk->ijk', arr_3D, arr_3D, arr_3D)
1 loops, best of 3: 694 ms per loop

Äußeres Produkt:

np.all(np.outer(arr_1D,arr_1D)==np.einsum('i,k->ik',arr_1D,arr_1D))
True

%timeit np.outer(arr_1D, arr_1D)
1000 loops, best of 3: 411 us per loop

%timeit np.einsum('i,k->ik', arr_1D, arr_1D)
1000 loops, best of 3: 245 us per loop

Alle oben genannten sind doppelt so schnell mitnp.einsum. Dies sollten Äpfel zu Äpfeln Vergleiche sein, da alles speziell von istdtype=np.double. Ich würde die Geschwindigkeit in einer Operation wie dieser erwarten:

np.allclose(np.sum(arr_2D*arr_3D),np.einsum('ij,oij->',arr_2D,arr_3D))
True

%timeit np.sum(arr_2D*arr_3D)
1 loops, best of 3: 813 ms per loop

%timeit np.einsum('ij,oij->', arr_2D, arr_3D)
10 loops, best of 3: 85.1 ms per loop

Einsum scheint dafür mindestens doppelt so schnell zu seinnp.inner, np.outer, np.kron, undnp.sum Egal obaxes Auswahl. Die Hauptausnahme istnp.dot wie es DGEMM aus einer BLAS-Bibliothek aufruft. Warum ist das so?np.einsum schneller als andere Numpy-Funktionen, die gleichwertig sind?

Der DGEMM-Fall zur Vollständigkeit:

np.allclose(np.dot(arr_2D,arr_2D),np.einsum('ij,jk',arr_2D,arr_2D))
True

%timeit np.einsum('ij,jk',arr_2D,arr_2D)
10 loops, best of 3: 56.1 ms per loop

%timeit np.dot(arr_2D,arr_2D)
100 loops, best of 3: 5.17 ms per loop

Die Leittheorie von @sebergs kommentiert dasnp.einsum nutzen könnenSSE2, aber numpy's ufuncs werden nicht vor numpy 1.8 (siehe dieÄnderungsprotokoll). Ich glaube das ist die richtige Antwort, habe abernicht konnte es bestätigen. Einige eingeschränkte Beweise können gefunden werden, indem der d-Typ des Eingabearrays geändert wird und der Geschwindigkeitsunterschied sowie die Tatsache beobachtet werden, dass nicht jeder die gleichen Trends bei den Timings beobachtet.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage