python member str Leistung zu langsam

8

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%     
Dr.Altan 07.06.2015, 20:32
quelle

2 Antworten

5

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:

%Vor%

Aber im zweiten Fall, da die Instanzdaten in einer Python dict statt einer einfachen Variablen gespeichert sind, unterscheiden sich die Dinge ein wenig.

%Vor%

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.

%Vor%

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.

%Vor%

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 und t beide Zeichenfolgen sind, können einige Python-Implementierungen wie CPython normalerweise eine In-Place-Operation ausführen   Optimierung für Zuordnungen der Form s = s + t oder s += 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, den    str.join() Methode, die eine konsistente lineare Verkettung sicherstellt   Leistung über Versionen und Implementierungen.

    
Ashwini Chaudhary 07.06.2015 21:35
quelle
3

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.

    
Dunes 07.06.2015 21:13
quelle

Tags und Links