Python: wann sind slice-Operationen an Arrays vorteilhaft?
Ich habe in Python versucht, eine doppelte Schleife über ein 2D Array (Variante 1) durch "array slice" Operationen (Variante 2) zu optimieren, stelle aber fest, dass dies nichts bringt, sondern die Rechenzeit sogar etwas größer wird.
Ich dachte eigentlich, dass man wo immer möglich slice Notation verwenden sollte, da dies schneller ist. Scheinbar kann man das aber nicht so einfach sagen.
Wann sind slice Operationen schneller bzw. von Vorteil? Leserlicher wid der Code ja nicht, aber dass er sogar langsamer wird überrascht mich nun doch ein wenig...
Der Code:
import numpy as np
import numpy.ma as ma
import time
def test():
f = np.array([
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 3, 6 , 4, 2, 0],
[0, 2, 4, 7 , 6, 4, 0],
[0, 0, 0, 0, 0, 0, 0]
], dtype=float)
u = np.array([
[0, 0, 0, 0, 0, 0, 0],
[0, 0.5, 1, 0, -1, -0.5, 0],
[0, 0.7, 1.1, 0, -1, -0.4, 0],
[0, 0, 0, 0, 0, 0, 0],
], dtype=float)
# calculate : variant 1
x = np.zeros_like(f)
maxcount = 100000
start = time.time()
for count in range(maxcount):
for i in range(1,u.shape[0]-1):
for j in range(1,u.shape[1]-1):
if u[i,j] > 0:
x[i,j] = u[i,j]*(f[i,j]-f[i,j-1])
else:
x[i,j] = u[i,j]*(f[i,j+1]-f[i,j])
end = time.time()
print("used time for variant 1:", end-start)
# calculate : variant 2
y = np.zeros_like(f)
start = time.time()
for count in range(maxcount):
maskl = (u[1:-1, 1:-1] > 0)
maskr = ~maskl
diff = f[1:-1, 1:] - f[1:-1, 0:-1]
yy = (y[1:-1, 1:-1])
uu = (u[1:-1, 1:-1 ])
yy[maskl] = uu[maskl] * (diff[:, :-1])[maskl]
yy[maskr] = uu[maskr] * (diff[:, 1: ])[maskr]
end = time.time()
print("used time for variant 2:", end-start)
np.testing.assert_array_equal(x, y)
test()
Die Ausgabe:
D:\python\animation>python test.py
used time for variant 1: 1.0328729152679443
used time for variant 2: 1.3058593273162842
D:\python\animation>python test.py
used time for variant 1: 1.1189219951629639
used time for variant 2: 1.3527190685272217
D:\python\animation>python test.py
used time for variant 1: 1.066974401473999
used time for variant 2: 1.3022441864013672
Ich sehe gerade, dass wenn man die Arry-Größe um das hundertfache vergrößert, die Variante 2 tatsächlich schneller ist:
D:\python\animation>python test.py
used time for variant 1: 1.234086513519287
used time for variant 2: 0.03494548797607422
t1/t2: 35.31461670714734
Somit ist die Antwort scheinbar, dass der Overhead des slicings ab einer bestimmten Größe gegenüber der eigentlichen Rechnerei vernachlässigbar wird.
1 Antwort
Scheinbar kann man das aber nicht so einfach sagen. Wann sind slice Operationen schneller bzw. von Vorteil?
Bei Python-eigenen Datenstrukturen (wie Listen und Tupeln) fast immer.
Numpy ist halt...speziell. Das ist eine separate Library, die ohnehin großteils in C(++) implementiert wurde. Da ist die Slice-Notation vermutlich ein Wrapper rund um die "nativen" Funktionen von Numpy - wenn du die Zeit hast, schau dir den Sourcecode an.
Bei 1000 fach größer ist der Faktor sogar 70! wow!
Interessant, das klingt aber auch plausibel. Bei "Methode 1" springst du ja ständig zwischen nativem Numpy und (tendenziell langsamem) Python hin und her. Im zweiten Fall kann das (bei entsprechender Implementierung) komplett innerhalb von Numpy abgehandelt werden.
Wenn man sich das API von Numpy so anschaut gibt es ja viele Funktionen, die genau das erreichen sollen, d.h. Operationen "internalisieren".
ja, wieder mal was gelernt. Dass ich so viel rausschinden kann, hab ich mir nicht gedacht. Ein Faktor 2 hätte mir schon gereicht. Ich liebe Python :-)
D:\python\animation>python test.py
used time for variant 1: 12.936085939407349
used time for variant 2: 0.18498826026916504
t1/t2: 69.9292264308278
Danke für die Antwort. Ich habe gerade festgestellt, dass sich der Vorteil der zweiten Methode bei großen Arrays bezahlt macht. Vergrößere ich die Eingangs-Arrays um das hundertfache, ist Variante 35 mal schneller ! Das habe ich nicht bedacht und ist schon gewaltig...