-
Warum optimieren Sie das? Haben Sie funktionierenden, getesteten Code geschrieben und dann Ihren Algorithmus untersucht? profiliert und festgestellt, dass die Optimierung dieses Codes eine Wirkung hat? Tun Sie dies in einer tiefen inneren Schleife, in der Sie Ihre Zeit verbringen? Wenn nicht, lassen Sie es bleiben.
-
Sie werden nur dann wissen, was für Sie am schnellsten funktioniert, wenn Sie die Zeit dafür nehmen. Um die Zeitmessung sinnvoll zu gestalten, müssen Sie sie auf Ihren tatsächlichen Anwendungsfall abstimmen. So können Sie beispielsweise deutliche Leistungsunterschiede zwischen einem Funktionsaufruf in einem Listenverständnis und einem Inline-Ausdruck feststellen; es ist nicht klar, ob Sie wirklich Ersteres wollten oder ob Sie es darauf reduziert haben, um Ihre Fälle ähnlich zu machen.
-
Sie sagen, dass es keine Rolle spielt, ob Sie am Ende ein Numpy-Array oder ein list
aber wenn man diese Art von Mikro-Optimierung vornimmt, ist es hace Das spielt keine Rolle, da sie sich bei der späteren Verwendung anders verhalten. Es könnte schwierig sein, den Finger darauf zu legen, also wird sich hoffentlich herausstellen, dass das ganze Problem vorzeitig hinfällig ist.
-
In der Regel ist es besser, einfach das richtige Werkzeug für die jeweilige Aufgabe zu verwenden, um Klarheit, Lesbarkeit usw. zu gewährleisten. Es ist selten, dass mir die Entscheidung zwischen diesen Dingen schwer fällt.
- Wenn ich Numpy-Arrays bräuchte, würde ich sie verwenden. Ich würde sie für die Speicherung großer homogener Arrays oder multidimensionaler Daten verwenden. Ich verwende sie häufig, aber nur selten, wenn ich eine Liste verwenden möchte.
- Wenn ich diese verwenden würde, würde ich mein Bestes tun, um meine Funktionen zu schreiben bereits vektorisiert, so dass ich nicht mit
numpy.vectorize
. Zum Beispiel, times_five
kann auf ein Numpy-Array ohne Dekoration angewendet werden.
- Wenn ich keinen Grund hätte, numpy zu verwenden, d.h. wenn ich keine numerischen mathematischen Probleme lösen oder spezielle numpy-Funktionen verwenden oder mehrdimensionale Arrays speichern würde oder was auch immer...
- Hätte ich eine bereits existierende Funktion, würde ich verwenden
map
. Dafür ist es da.
- Wenn ich eine Operation hätte, die in einen kleinen Ausdruck passt und ich keine Funktion bräuchte, würde ich eine Listenverarbeitung verwenden.
- Wenn ich nur die Operation für alle Fälle durchführen wollte, aber das Ergebnis nicht speichern müsste, würde ich eine einfache for-Schleife verwenden.
- In vielen Fällen würde ich sogar
map
und die faulen Äquivalente der Listenauffassungen: itertools.imap
und Generatorausdrücke. Diese können den Speicherverbrauch um einen Faktor von n
in einigen Fällen und kann die Durchführung unnötiger Operationen manchmal vermeiden.
Wenn sich herausstellt, dass hier die Leistungsprobleme liegen, ist es schwierig, diese Dinge richtig zu machen. Es ist sehr Es kommt häufig vor, dass Menschen für ihre tatsächlichen Probleme den falschen Spielzeugkoffer wählen. Schlimmer noch, es ist extrem üblich, dass die Leute daraus dumme allgemeine Regeln ableiten.
Betrachten Sie die folgenden Fälle (timeme.py wird unten veröffentlicht)
python -m timeit "from timeme import x, times_five; from numpy import vectorize" "vectorize(times_five)(x)"
1000 loops, best of 3: 924 usec per loop
python -m timeit "from timeme import x, times_five" "[times_five(item) for item in x]"
1000 loops, best of 3: 510 usec per loop
python -m timeit "from timeme import x, times_five" "map(times_five, x)"
1000 loops, best of 3: 484 usec per loop
Ein naiver Beobachter würde zu dem Schluss kommen, dass map die leistungsstärkste dieser Optionen ist, aber die Antwort lautet immer noch "es kommt darauf an". Bedenken Sie die Vorteile der von Ihnen verwendeten Tools: Mit List Comprehensions können Sie die Definition einfacher Funktionen vermeiden; mit Numpy können Sie Dinge in C vektorisieren, wenn Sie die richtigen Dinge tun.
python -m timeit "from timeme import x, times_five" "[item + item + item + item + item for item in x]"
1000 loops, best of 3: 285 usec per loop
python -m timeit "import numpy; x = numpy.arange(1000)" "x + x + x + x + x"
10000 loops, best of 3: 39.5 usec per loop
Aber das ist nicht alles - es gibt noch mehr. Bedenken Sie die Macht einer Algorithmusänderung. Sie kann sogar noch dramatischer sein.
python -m timeit "from timeme import x, times_five" "[5 * item for item in x]"
10000 loops, best of 3: 147 usec per loop
python -m timeit "import numpy; x = numpy.arange(1000)" "5 * x"
100000 loops, best of 3: 16.6 usec per loop
Manchmal kann eine Änderung des Algorithmus sogar noch effektiver sein. Dies wird immer effektiver, je größer die Zahlen werden.
python -m timeit "from timeme import square, x" "map(square, x)"
10 loops, best of 3: 41.8 msec per loop
python -m timeit "from timeme import good_square, x" "map(good_square, x)"
1000 loops, best of 3: 370 usec per loop
Und selbst jetzt mag das alles wenig mit Ihrem eigentlichen Problem zu tun haben. Es sieht so aus, als ob numpy so großartig ist, wenn man es richtig einsetzen kann, aber es hat seine Grenzen: Keines dieser numpy-Beispiele verwendet echte Python-Objekte in den Arrays. Das verkompliziert das, was getan werden muss, sogar sehr. Und was ist, wenn wir doch C-Datentypen verwenden? Diese sind weniger robust als Python-Objekte. Sie sind nicht nullbar. Die Ganzzahlen laufen über. Man muss etwas zusätzliche Arbeit leisten, um sie abzurufen. Sie sind statisch typisiert. Manchmal erweisen sich diese Dinge als Probleme, sogar als unerwartete Probleme.
Da haben Sie es also: eine endgültige Antwort. "Es kommt darauf an."
# timeme.py
x = xrange(1000)
def times_five(a):
return a + a + a + a + a
def square(a):
if a == 0:
return 0
value = a
for i in xrange(a - 1):
value += a
return value
def good_square(a):
return a ** 2