Ich habe ein seltsames Leistungsproblem beim Hinzufügen zu einem Str-Klassenmitglied in Python 2.7.3 festgestellt. Ich weiß, dass der Zugriff auf lokale Variablen schneller ist, jedoch gibt es in dem unten beschriebenen Problem mehr als 100-fache Geschwindigkeitsdifferenz zwischen den beiden Schleifen. Derjenige, der auf a.accum_ zugreift, beginnt schnell, verlangsamt sich aber, als wäre str iadd O (n ^ 2) mit der Länge von str.
Kennt jemand den Grund?
%Vor% Für das erste Beispiel ist es ziemlich klar, dass es sich um eine Einzelreferenzoptimierung handelt (es gibt tatsächlich zwei Referenzen: eine aus dem Objekt selbst und eine LOAD_FAST
; unicode_concatenate
versucht, den Wert auf 1 zu reduzieren, bevor die Kontrolle an PyUnicode_Append
übergeben wird. ) wurde von CPython mit dieser Funktion unicode_modifiable
ausgeführt:
Aber im zweiten Fall, da die Instanzdaten in einer Python dict
statt einer einfachen Variablen gespeichert sind, unterscheiden sich die Dinge ein wenig.
erfordert eigentlich das Vorabladen des Wertes von a.accum_
und das Speichern auf dem Stack. Also, jetzt hat die Zeichenfolge mindestens drei Referenzen: eine aus dem Instanz-Wörterbuch, eine aus DUP_TOP
und eine von PyObject_GetAttr
verwendet von LOAD_ATTR
. Daher kann Python diesen Fall nicht optimieren, da das Ändern einer dieser Dateien sich auch auf andere Referenzen auswirkt.
Sie würden erwarten, dass die Ausgabe hier 'spam_from_func'
ist, aber es wird anders sein, weil der ursprüngliche Wert von a.str
von Python gespeichert wurde, bevor func()
aufgerufen wurde.
Bytecode:
%Vor% Beachten Sie, dass diese Optimierung in um 2004 (CPython 2.4) vorgenommen wurde, um Benutzer daran zu hindern
Langsamkeit von a += b
oder a = a + b
, also ist es meistens für einfache Variablen gedacht und funktioniert nur, wenn die nächste Anweisung STORE_FAST
(lokale Variable), STORE_DEREF
(Closures) und STORE_NAME
ist. Es ist keine allgemeine Lösung, die Der beste Weg, dies in Python zu tun, besteht darin, eine Liste zu erstellen und ihre Elemente mit str.join
zu verbinden.
CPython-Implementierungsdetail : Wenn
s
undt
beide Zeichenfolgen sind, können einige Python-Implementierungen wie CPython normalerweise eine In-Place-Operation ausführen Optimierung für Zuordnungen der Forms = s + t
oders += t
. Wann Diese Optimierung macht die quadratische Laufzeit deutlich geringer wahrscheinlich. Diese Optimierung ist sowohl Version als auch Implementierung abhängig. Für leistungsabhängigen Code ist es vorzuziehen, denstr.join()
Methode, die eine konsistente lineare Verkettung sicherstellt Leistung über Versionen und Implementierungen.
Python-Strings sind unveränderlich und kann daher nicht eine __iadd__
-Methode haben. Was Sie im ersten Beispiel erleben, ist eine Mikrooptimierung des CPython-Interpreters. Im ersten Beispiel hat der Interpreter bemerkt, dass er eine lokale Variable hat, die eine Referenzzahl von 1 hat. Somit kann der Interpreter frech damit umgehen, die Zeichenfolge an Ort und Stelle zu ändern. Obwohl dies gegen den Vertrag von str
verstößt, wird zu keinem Zeitpunkt während der Ausführung des Programms ersichtlich, dass dieser Vertrag kurzzeitig verletzt wurde.
Im letzten Beispiel wird diese Mikrooptimierung nicht implementiert, weshalb sie so langsam ist. Es sieht so aus, als könnte die Optimierung angewendet werden, weshalb ich mir nicht sicher bin, warum sie nicht angewendet wird.
Wenn Sie jedoch eine Zeichenfolge erstellen, ordnen Sie die Teilzeichenfolgen in einer Liste zusammen und verwenden dann str.join
, um das Endprodukt zu erstellen.
Tags und Links python string performance