str Leistung in Python

87

Beim Profilieren eines Teils des Python-Codes ( python 2.6bis zu 3.2) stellte ich fest, dass die strMethode zum Konvertieren eines Objekts (in meinem Fall einer Ganzzahl) in eine Zeichenfolge fast eine Größenordnung langsamer ist als die Verwendung der Zeichenfolgenformatierung.

Hier ist der Benchmark

>>> from timeit import Timer
>>> Timer('str(100000)').timeit()
0.3145311339386332
>>> Timer('"%s"%100000').timeit()
0.03803517023435887

Weiß jemand warum das so ist? Vermisse ich etwas

Luca Sbardella
quelle
2
Und was ist mit'{}'.format(100000)
wim
Das ist das langsamste, aber auch das flexibelste.
Luca Sbardella

Antworten:

105

'%s' % 100000 wird vom Compiler ausgewertet und entspricht zur Laufzeit einer Konstanten.

>>> import dis
>>> dis.dis(lambda: str(100000))
  8           0 LOAD_GLOBAL              0 (str)
              3 LOAD_CONST               1 (100000)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda: '%s' % 100000)
  9           0 LOAD_CONST               3 ('100000')
              3 RETURN_VALUE        

%mit einem Laufzeitausdruck ist nicht (signifikant) schneller als str:

>>> Timer('str(x)', 'x=100').timeit()
0.25641703605651855
>>> Timer('"%s" % x', 'x=100').timeit()
0.2169809341430664

Beachten Sie, dass dies strimmer noch etwas langsamer ist, wie @DietrichEpp sagte. Dies liegt daran, strdass Such- und Funktionsaufrufoperationen erforderlich sind, während %ein einzelner sofortiger Bytecode kompiliert wird:

>>> dis.dis(lambda x: str(x))
  9           0 LOAD_GLOBAL              0 (str)
              3 LOAD_FAST                0 (x)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda x: '%s' % x)
 10           0 LOAD_CONST               1 ('%s')
              3 LOAD_FAST                0 (x)
              6 BINARY_MODULO       
              7 RETURN_VALUE        

Dies gilt natürlich für das System, auf dem ich getestet habe (CPython 2.7). andere Implementierungen können abweichen.

georg
quelle
In der Tat scheint dies der Grund zu sein, ich habe es gerade selbst versucht und die Formatierung von Zeichenfolgen ist nur etwa 5% schneller als str. Danke für die Antwort. Kein Grund, den Code überall zu ändern :-)
Luca Sbardella
2
Um es näher zu erläutern: strist ein Name, der auf einen anderen Wert als den Zeichenfolgentyp zurückgesetzt werden kann, aber die Zeichenfolgenformatierung - dh die str.__mod__Methode - kann nicht ersetzt werden, sodass der Compiler die Optimierung durchführen kann. Der Compiler macht nicht viel in Bezug auf die Optimierung, aber er macht mehr als Sie vielleicht denken :)
Karl Knechtel
4
... und die Lektion, die Sie hier lernen sollten, lautet: Verwenden Sie in solchen Tests niemals Literale!
Onkel Zeiv
Dieser spezielle Blogeintrag könnte Sie interessieren: skymind.com/~ocrow/python_string . Es enthält eine Tabelle mit Benchmarks für verschiedene Methoden zur Verkettung von Zeichenfolgen, die den oben angegebenen ähnlich sind.
Aaron Newton
14

Ein Grund, der mir in den Sinn kommt, ist die Tatsache, dass str(100000)eine globale Suche "%s"%100000erforderlich ist , dies jedoch nicht. Das strGlobale muss im globalen Bereich nachgeschlagen werden. Dies erklärt nicht den gesamten Unterschied:

>>> Timer('str(100000)').timeit()
0.2941889762878418
>>> Timer('x(100000)', 'x=str').timeit()
0.24904918670654297

Wie von thg435 festgestellt ,

>>> Timer('"%s"%100000',).timeit()
0.034214019775390625
>>> Timer('"%s"%x','x=100000').timeit()
0.2940788269042969
Dietrich Epp
quelle